From 26826b2e98180460f3be75e1db0324e9df13dc4e Mon Sep 17 00:00:00 2001 From: anamnavi Date: Sat, 18 Oct 2025 22:45:32 -0400 Subject: [PATCH 1/3] ensure an xml string that would be returned from graphql query can be parsed into a PSResourceInfo --- src/code/FindPSResource.cs | 90 +++++++++++++++++++++ src/code/PSResourceInfo.cs | 155 +++++++++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+) diff --git a/src/code/FindPSResource.cs b/src/code/FindPSResource.cs index 2709073e7..a003a3567 100644 --- a/src/code/FindPSResource.cs +++ b/src/code/FindPSResource.cs @@ -167,6 +167,96 @@ protected override void ProcessRecord() private void ProcessResourceNameParameterSet() { + if (Name[0].Equals("graphql")) + { + // string xmlString = "\r\n \r\n 26.0.0\r\n PowerShell module with commands for discovering, installing, updating and publishing the PowerShell artifacts like Modules, DSC Resources, Role Capabilities and Scripts.\r\n true\r\n \r\n"; + // string xmlString = @" + // + // (c) Microsoft Corporation. All rights reserved. + // PowerShell module with commands for discovering, installing, updating and publishing the PowerShell artifacts like Modules, DSC Resources, Role Capabilities and Scripts. + // + // https://go.microsoft.com/fwlink/?LinkId=829061 + // 2020-08-10T21:22:07.4Z + // https://go.microsoft.com/fwlink/?LinkId=828955 + // PackageManagement PSEdition_Desktop PSEdition_Core Linux Mac Windows PSModule + // + // <version>26.0.0</version> + // <flattenedAuthors>Microsoft Corporation</flattenedAuthors> + // <flattenedDependencies></flattenedDependencies> + // <isPrerelease>false</isPrerelease> + // <releaseNotes>### 3.0.0-beta8 + // </releaseNotes> + // <normalizedVersion>26.0.0</normalizedVersion> + // <companyName>Microsoft Corporation</companyName> + // <cmdlets>Find-PSResource Get-PSResourceRepository Get-PSResource Install-PSResource Register-PSResourceRepository Save-PSResource Set-PSResourceRepository Publish-PSResource Uninstall-PSResource Unregister-PSResourceRepository Update-PSResource</cmdlets> + // <functions></functions> + // <dscResources>PSModule PSRepository</dscResources> + // <roleCapabilities></roleCapabilities> + // </packageByName> + // </Root>"; + + string xmlString = @"<Root> + <packageByName> + <copyright>Microsoft Corporation. All rights reserved.</copyright> + <description>Microsoft Azure PowerShell - Storage service data plane and management cmdlets for Azure Resource Manager in Windows PowerShell and PowerShell Core. Creates and manages storage accounts in Azure Resource Manager. + For more information on Storage, please visit the following: https://docs.microsoft.com/azure/storage/</description> + <iconUrl /> + <licenseUrl>https://aka.ms/azps-license</licenseUrl> + <published>2022-04-01T02:13:27.757Z</published> + <projectUrl>https://github.com/Azure/azure-powershell</projectUrl> + <tags>Azure ResourceManager ARM Storage StorageAccount PSModule PSEdition_Core PSEdition_Desktop</tags> + <title /> + <version>4.4.0</version> + <flattenedAuthors>Microsoft Corporation</flattenedAuthors> + <flattenedDependencies>Az.Accounts:[2.7.5, ):</flattenedDependencies> + <isPrerelease>false</isPrerelease> + <releaseNotes>* Updated examples in reference documentation for 'Close-AzStorageFileHandle'</releaseNotes> + <normalizedVersion>4.4.0</normalizedVersion> + <companyName>Microsoft Corporation</companyName> + <cmdlets>Get-AzStorageAccount Get-AzStorageAccountKey New-AzStorageAccount New-AzStorageAccountKey Remove-AzStorageAccount Set-AzCurrentStorageAccount Set-AzStorageAccount Get-AzStorageAccountNameAvailability Get-AzStorageUsage Update-AzStorageAccountNetworkRuleSet Get-AzStorageAccountNetworkRuleSet Add-AzStorageAccountNetworkRule Remove-AzStorageAccountNetworkRule Get-AzStorageTable New-AzStorageTableSASToken New-AzStorageTableStoredAccessPolicy New-AzStorageTable Remove-AzStorageTableStoredAccessPolicy Remove-AzStorageTable Get-AzStorageTableStoredAccessPolicy Set-AzStorageTableStoredAccessPolicy Get-AzStorageQueue New-AzStorageQueue Remove-AzStorageQueue Get-AzStorageQueueStoredAccessPolicy New-AzStorageQueueSASToken New-AzStorageQueueStoredAccessPolicy Remove-AzStorageQueueStoredAccessPolicy Set-AzStorageQueueStoredAccessPolicy Get-AzStorageFile Get-AzStorageFileContent Get-AzStorageFileCopyState Get-AzStorageShare Get-AzStorageShareStoredAccessPolicy New-AzStorageDirectory New-AzStorageFileSASToken New-AzStorageShare New-AzStorageShareSASToken New-AzStorageShareStoredAccessPolicy Remove-AzStorageDirectory Remove-AzStorageFile Remove-AzStorageShare Remove-AzStorageShareStoredAccessPolicy Set-AzStorageFileContent Set-AzStorageShareQuota Set-AzStorageShareStoredAccessPolicy Start-AzStorageFileCopy Stop-AzStorageFileCopy New-AzStorageAccountSASToken Set-AzStorageCORSRule Get-AzStorageCORSRule Get-AzStorageServiceLoggingProperty Get-AzStorageServiceMetricsProperty Remove-AzStorageCORSRule Set-AzStorageServiceLoggingProperty Set-AzStorageServiceMetricsProperty New-AzStorageContext Set-AzStorageContainerAcl Remove-AzStorageBlob Set-AzStorageBlobContent Get-AzStorageBlob Get-AzStorageBlobContent Get-AzStorageBlobCopyState Get-AzStorageContainer Get-AzStorageContainerStoredAccessPolicy New-AzStorageBlobSASToken New-AzStorageContainer New-AzStorageContainerSASToken New-AzStorageContainerStoredAccessPolicy Remove-AzStorageContainer Remove-AzStorageContainerStoredAccessPolicy Set-AzStorageContainerStoredAccessPolicy Start-AzStorageBlobCopy Start-AzStorageBlobIncrementalCopy Stop-AzStorageBlobCopy Update-AzStorageServiceProperty Get-AzStorageServiceProperty Enable-AzStorageDeleteRetentionPolicy Disable-AzStorageDeleteRetentionPolicy Enable-AzStorageStaticWebsite Disable-AzStorageStaticWebsite Get-AzRmStorageContainer Update-AzRmStorageContainer New-AzRmStorageContainer Remove-AzRmStorageContainer Add-AzRmStorageContainerLegalHold Remove-AzRmStorageContainerLegalHold Set-AzRmStorageContainerImmutabilityPolicy Get-AzRmStorageContainerImmutabilityPolicy Remove-AzRmStorageContainerImmutabilityPolicy Lock-AzRmStorageContainerImmutabilityPolicy Set-AzStorageAccountManagementPolicy Get-AzStorageAccountManagementPolicy Remove-AzStorageAccountManagementPolicy New-AzStorageAccountManagementPolicyFilter New-AzStorageAccountManagementPolicyRule Add-AzStorageAccountManagementPolicyAction Update-AzStorageBlobServiceProperty Get-AzStorageBlobServiceProperty Enable-AzStorageBlobDeleteRetentionPolicy Disable-AzStorageBlobDeleteRetentionPolicy Revoke-AzStorageAccountUserDelegationKeys Get-AzStorageFileHandle Close-AzStorageFileHandle New-AzRmStorageShare Remove-AzRmStorageShare Get-AzRmStorageShare Update-AzRmStorageShare Update-AzStorageFileServiceProperty Get-AzStorageFileServiceProperty Restore-AzRmStorageShare Get-AzDataLakeGen2ChildItem Get-AzDataLakeGen2Item New-AzDataLakeGen2Item Move-AzDataLakeGen2Item Remove-AzDataLakeGen2Item Update-AzDataLakeGen2Item Set-AzDataLakeGen2ItemAclObject Get-AzDataLakeGen2ItemContent Invoke-AzStorageAccountFailover Get-AzStorageBlobQueryResult New-AzStorageBlobQueryConfig New-AzStorageObjectReplicationPolicyRule Set-AzStorageObjectReplicationPolicy Get-AzStorageObjectReplicationPolicy Remove-AzStorageObjectReplicationPolicy Enable-AzStorageBlobRestorePolicy Disable-AzStorageBlobRestorePolicy New-AzStorageBlobRangeToRestore Restore-AzStorageBlobRange Set-AzDataLakeGen2AclRecursive Update-AzDataLakeGen2AclRecursive Remove-AzDataLakeGen2AclRecursive New-AzStorageEncryptionScope Update-AzStorageEncryptionScope Get-AzStorageEncryptionScope Copy-AzStorageBlob Set-AzStorageBlobInventoryPolicy New-AzStorageBlobInventoryPolicyRule Get-AzStorageBlobInventoryPolicy Remove-AzStorageBlobInventoryPolicy Enable-AzStorageContainerDeleteRetentionPolicy Disable-AzStorageContainerDeleteRetentionPolicy Restore-AzStorageContainer Enable-AzStorageBlobLastAccessTimeTracking Disable-AzStorageBlobLastAccessTimeTracking Set-AzStorageBlobTag Get-AzStorageBlobTag Get-AzStorageBlobByTag Invoke-AzStorageAccountHierarchicalNamespaceUpgrade Stop-AzStorageAccountHierarchicalNamespaceUpgrade Set-AzStorageBlobImmutabilityPolicy Remove-AzStorageBlobImmutabilityPolicy Set-AzStorageBlobLegalHold Invoke-AzRmStorageContainerImmutableStorageWithVersioningMigration</cmdlets> + <functions></functions> + <dscResources></dscResources> + <roleCapabilities></roleCapabilities> + </packageByName> + </Root>"; + +// string xmlString = @"<Root> +// <packageByName> +// <copyright>(c) 2020 Contoso Corporation. All rights reserved.</copyright> +// <description>this is a test module without including any categories</description> +// <iconUrl /> +// <licenseUrl /> +// <published>2020-09-21T15:46:13.317Z</published> +// <projectUrl /> +// <tags>Test CommandsAndResource Tag2 PSModule</tags> +// <title /> +// <version>5.0.0</version> +// <flattenedAuthors>americks</flattenedAuthors> +// <flattenedDependencies>RequiredModule1:(, ):|RequiredModule2:[2.0.0, ):|RequiredModule3:[2.5.0, 2.5.0]:|RequiredModule4:[1.1.0, 2.0.0]:|RequiredModule5:(, 1.5.0]:</flattenedDependencies> +// <isPrerelease>false</isPrerelease> +// <releaseNotes /> +// <normalizedVersion>5.0.0</normalizedVersion> +// <companyName>Me</companyName> +// <cmdlets></cmdlets> +// <functions></functions> +// <dscResources></dscResources> +// <roleCapabilities></roleCapabilities> +// </packageByName> +// </Root>"; + PSResourceInfo.TryConvertXmlFromGraphQL(xmlString, out PSResourceInfo obj, out string errMsg); + if (!String.IsNullOrEmpty(errMsg)) + { + WriteError(new ErrorRecord( + new PSInvalidOperationException(errMsg), + "ErrorFilteringNamesForUnsupportedWildcards", + ErrorCategory.InvalidArgument, + this)); + } + + WriteObject(obj); + return; + } + // only cases where Name is allowed to not be specified is if Type or Tag parameters are if (!MyInvocation.BoundParameters.ContainsKey(nameof(Name))) { diff --git a/src/code/PSResourceInfo.cs b/src/code/PSResourceInfo.cs index d2351da35..cc4b30802 100644 --- a/src/code/PSResourceInfo.cs +++ b/src/code/PSResourceInfo.cs @@ -469,6 +469,158 @@ private static Version GetVersionInfo( return GetProperty<Version>(nameof(PSResourceInfo.Version), psObjectInfo); } + /// <summary + public static bool TryConvertXmlFromGraphQL( + string xmlString, // TODO: later this will be sent as a XmlDocument or XmlNode + out PSResourceInfo psGetInfo, + out string errorMsg) + { + psGetInfo = null; + errorMsg = String.Empty; + + if (String.IsNullOrEmpty(xmlString)) + { + errorMsg = "TryConvertXmlFromGraphQL: Invalid xmlString. String cannot be null."; + return false; + } + + try + { + Hashtable metadata = new Hashtable(StringComparer.InvariantCultureIgnoreCase); + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xmlString); + + XmlElement rootElement = doc.DocumentElement; + if (rootElement.HasChildNodes) + { + XmlNode operationNameElement = rootElement.ChildNodes[0]; + if (operationNameElement.HasChildNodes) + { + // this is where the metadata properties reside + var metadataElements = operationNameElement.ChildNodes; + foreach (XmlElement entryChild in metadataElements) + { + var metadataPropertyKey = entryChild.LocalName; + var metadataPropertyValue = entryChild.InnerText; + if (metadataPropertyKey.Equals("title")) + { + metadata["id"] = metadataPropertyValue; + } + if (metadataPropertyKey.Equals("version")) + { + metadata[metadataPropertyKey] = ParseHttpVersion(metadataPropertyValue, out string prereleaseLabel); + metadata["prerelease"] = prereleaseLabel; + } + else if (metadataPropertyKey.EndsWith("Url")) + { + metadata[metadataPropertyKey] = ParseHttpUrl(metadataPropertyValue) as Uri; + } + else if (metadataPropertyKey.Equals("tags")) + { + metadata[metadataPropertyKey] = metadataPropertyValue.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + } + else if (metadataPropertyKey.Equals("published")) + { + metadata[metadataPropertyKey] = ParseHttpDateTime(metadataPropertyValue); + } + else if (metadataPropertyKey.Equals("flattenedDependencies")) + { + metadata["dependencies"] = ParseHttpDependencies(metadataPropertyValue); + } + else if (metadataPropertyKey.Equals("isPrerelease")) + { + bool.TryParse(metadataPropertyValue, out bool isPrerelease); + + metadata[metadataPropertyKey] = isPrerelease; + } + else if (metadataPropertyKey.Equals("normalizedVersion")) + { + if (!NuGetVersion.TryParse(metadataPropertyValue, out NuGetVersion parsedNormalizedVersion)) + { + errorMsg = string.Format( + CultureInfo.InvariantCulture, + @"TryReadPSGetInfo: Cannot parse NormalizedVersion"); + + parsedNormalizedVersion = new NuGetVersion("1.0.0.0"); + } + + metadata[metadataPropertyKey] = parsedNormalizedVersion; + } + else if (metadataPropertyKey.Equals("flattenedAuthors")) + { + metadata["authors"] = metadataPropertyValue; + } + else + { + metadata[metadataPropertyKey] = metadataPropertyValue; + } + } + + var typeInfo = ParseHttpMetadataType(metadata["tags"] as string[], out ArrayList commandNames, out ArrayList cmdletNames, out ArrayList dscResourceNames); + var resourceHashtable = new Hashtable { + { nameof(PSResourceInfo.Includes.Command), new PSObject(commandNames) }, + { nameof(PSResourceInfo.Includes.Cmdlet), new PSObject(cmdletNames) }, + { nameof(PSResourceInfo.Includes.DscResource), new PSObject(dscResourceNames) } + }; + + var additionalMetadataHashtable = new Dictionary<string, string>(); + + // Only add NormalizedVersion to additionalMetadata if server response included it + if (metadata.ContainsKey("normalizedVersion")) + { + additionalMetadataHashtable.Add("NormalizedVersion", metadata["normalizedVersion"].ToString()); + } + + var includes = new ResourceIncludes(resourceHashtable); + + psGetInfo = new PSResourceInfo( + additionalMetadata: additionalMetadataHashtable, + author: metadata["authors"] as String, + companyName: metadata["companyName"] as String, + copyright: metadata["copyright"] as String, + dependencies: metadata["dependencies"] as Dependency[], + description: metadata["description"] as String, + iconUri: metadata["iconUrl"] as Uri, + includes: includes, + installedDate: null, + installedLocation: null, + isPrerelease: (bool)metadata["isPrerelease"], + licenseUri: metadata["licenseUrl"] as Uri, + name: String.Empty, //metadata["Id"] as String + powershellGetFormatVersion: null, + prerelease: metadata["prerelease"] as String, + projectUri: metadata["projectUrl"] as Uri, + publishedDate: metadata["published"] as DateTime?, + releaseNotes: metadata["releaseNotes"] as String, + repository: null, //repository.Name + repositorySourceLocation: String.Empty, //repository.Uri.ToString() + tags: metadata["tags"] as string[], + type: typeInfo, + updatedDate: null, + version: metadata["version"] as Version); + + return true; + } + } + + Console.WriteLine(rootElement.LocalName); + } + catch (Exception ex) + { + errorMsg = string.Format( + CultureInfo.InvariantCulture, + @"TryConvertFromXml: Cannot parse PSResourceInfo from xml string with error: {0}", + ex.Message); + + return false; + } + + return true; + } + + + /// <summary> /// Converts XML entry to PSResourceInfo instance /// used for V2 Server API call find response conversion to PSResourceInfo object @@ -1601,6 +1753,9 @@ internal static Uri ParseHttpUrl(string uriString) internal static Dependency[] ParseHttpDependencies(string dependencyString) { + /* + RequiredModule1:(, ):|RequiredModule2:[2.0.0, ):|RequiredModule3:[2.5.0, 2.5.0]:|RequiredModule4:[1.1.0, 2.0.0]:|RequiredModule5:(, 1.5.0]: + */ /* Az.Profile:[0.1.0, ):|Az.Aks:[0.1.0, ):|Az.AnalysisServices:[0.1.0, ): Post 1st Split: From 41ba8b972cd40e9b87fa3c235d46616004f919e9 Mon Sep 17 00:00:00 2001 From: anamnavi <annavied@microsoft.com> Date: Wed, 22 Oct 2025 16:51:47 -0400 Subject: [PATCH 2/3] add support for FindTags, FindAll, FindVersionGlobbing --- src/code/FindPSResource.cs | 105 ++-------- src/code/PSResourceInfo.cs | 308 +++++++++++++++++++++++------ src/code/V2ResponseUtil.cs | 113 +++++++++-- src/code/V2ServerAPICalls.cs | 370 ++++++++++++++++++++++------------- 4 files changed, 592 insertions(+), 304 deletions(-) diff --git a/src/code/FindPSResource.cs b/src/code/FindPSResource.cs index a003a3567..a95a23537 100644 --- a/src/code/FindPSResource.cs +++ b/src/code/FindPSResource.cs @@ -167,95 +167,22 @@ protected override void ProcessRecord() private void ProcessResourceNameParameterSet() { - if (Name[0].Equals("graphql")) - { - // string xmlString = "<Root>\r\n <packageByName>\r\n <version>26.0.0</version>\r\n <description>PowerShell module with commands for discovering, installing, updating and publishing the PowerShell artifacts like Modules, DSC Resources, Role Capabilities and Scripts.</description>\r\n <isLatest>true</isLatest>\r\n </packageByName>\r\n</Root>"; - // string xmlString = @"<Root> - // <packageByName> - // <copyright>(c) Microsoft Corporation. All rights reserved.</copyright> - // <description>PowerShell module with commands for discovering, installing, updating and publishing the PowerShell artifacts like Modules, DSC Resources, Role Capabilities and Scripts.</description> - // <iconUrl /> - // <licenseUrl>https://go.microsoft.com/fwlink/?LinkId=829061</licenseUrl> - // <published>2020-08-10T21:22:07.4Z</published> - // <projectUrl>https://go.microsoft.com/fwlink/?LinkId=828955</projectUrl> - // <tags>PackageManagement PSEdition_Desktop PSEdition_Core Linux Mac Windows PSModule</tags> - // <title /> - // <version>26.0.0</version> - // <flattenedAuthors>Microsoft Corporation</flattenedAuthors> - // <flattenedDependencies></flattenedDependencies> - // <isPrerelease>false</isPrerelease> - // <releaseNotes>### 3.0.0-beta8 - // </releaseNotes> - // <normalizedVersion>26.0.0</normalizedVersion> - // <companyName>Microsoft Corporation</companyName> - // <cmdlets>Find-PSResource Get-PSResourceRepository Get-PSResource Install-PSResource Register-PSResourceRepository Save-PSResource Set-PSResourceRepository Publish-PSResource Uninstall-PSResource Unregister-PSResourceRepository Update-PSResource</cmdlets> - // <functions></functions> - // <dscResources>PSModule PSRepository</dscResources> - // <roleCapabilities></roleCapabilities> - // </packageByName> - // </Root>"; - - string xmlString = @"<Root> - <packageByName> - <copyright>Microsoft Corporation. All rights reserved.</copyright> - <description>Microsoft Azure PowerShell - Storage service data plane and management cmdlets for Azure Resource Manager in Windows PowerShell and PowerShell Core. Creates and manages storage accounts in Azure Resource Manager. - For more information on Storage, please visit the following: https://docs.microsoft.com/azure/storage/</description> - <iconUrl /> - <licenseUrl>https://aka.ms/azps-license</licenseUrl> - <published>2022-04-01T02:13:27.757Z</published> - <projectUrl>https://github.com/Azure/azure-powershell</projectUrl> - <tags>Azure ResourceManager ARM Storage StorageAccount PSModule PSEdition_Core PSEdition_Desktop</tags> - <title /> - <version>4.4.0</version> - <flattenedAuthors>Microsoft Corporation</flattenedAuthors> - <flattenedDependencies>Az.Accounts:[2.7.5, ):</flattenedDependencies> - <isPrerelease>false</isPrerelease> - <releaseNotes>* Updated examples in reference documentation for 'Close-AzStorageFileHandle'</releaseNotes> - <normalizedVersion>4.4.0</normalizedVersion> - <companyName>Microsoft Corporation</companyName> - <cmdlets>Get-AzStorageAccount Get-AzStorageAccountKey New-AzStorageAccount New-AzStorageAccountKey Remove-AzStorageAccount Set-AzCurrentStorageAccount Set-AzStorageAccount Get-AzStorageAccountNameAvailability Get-AzStorageUsage Update-AzStorageAccountNetworkRuleSet Get-AzStorageAccountNetworkRuleSet Add-AzStorageAccountNetworkRule Remove-AzStorageAccountNetworkRule Get-AzStorageTable New-AzStorageTableSASToken New-AzStorageTableStoredAccessPolicy New-AzStorageTable Remove-AzStorageTableStoredAccessPolicy Remove-AzStorageTable Get-AzStorageTableStoredAccessPolicy Set-AzStorageTableStoredAccessPolicy Get-AzStorageQueue New-AzStorageQueue Remove-AzStorageQueue Get-AzStorageQueueStoredAccessPolicy New-AzStorageQueueSASToken New-AzStorageQueueStoredAccessPolicy Remove-AzStorageQueueStoredAccessPolicy Set-AzStorageQueueStoredAccessPolicy Get-AzStorageFile Get-AzStorageFileContent Get-AzStorageFileCopyState Get-AzStorageShare Get-AzStorageShareStoredAccessPolicy New-AzStorageDirectory New-AzStorageFileSASToken New-AzStorageShare New-AzStorageShareSASToken New-AzStorageShareStoredAccessPolicy Remove-AzStorageDirectory Remove-AzStorageFile Remove-AzStorageShare Remove-AzStorageShareStoredAccessPolicy Set-AzStorageFileContent Set-AzStorageShareQuota Set-AzStorageShareStoredAccessPolicy Start-AzStorageFileCopy Stop-AzStorageFileCopy New-AzStorageAccountSASToken Set-AzStorageCORSRule Get-AzStorageCORSRule Get-AzStorageServiceLoggingProperty Get-AzStorageServiceMetricsProperty Remove-AzStorageCORSRule Set-AzStorageServiceLoggingProperty Set-AzStorageServiceMetricsProperty New-AzStorageContext Set-AzStorageContainerAcl Remove-AzStorageBlob Set-AzStorageBlobContent Get-AzStorageBlob Get-AzStorageBlobContent Get-AzStorageBlobCopyState Get-AzStorageContainer Get-AzStorageContainerStoredAccessPolicy New-AzStorageBlobSASToken New-AzStorageContainer New-AzStorageContainerSASToken New-AzStorageContainerStoredAccessPolicy Remove-AzStorageContainer Remove-AzStorageContainerStoredAccessPolicy Set-AzStorageContainerStoredAccessPolicy Start-AzStorageBlobCopy Start-AzStorageBlobIncrementalCopy Stop-AzStorageBlobCopy Update-AzStorageServiceProperty Get-AzStorageServiceProperty Enable-AzStorageDeleteRetentionPolicy Disable-AzStorageDeleteRetentionPolicy Enable-AzStorageStaticWebsite Disable-AzStorageStaticWebsite Get-AzRmStorageContainer Update-AzRmStorageContainer New-AzRmStorageContainer Remove-AzRmStorageContainer Add-AzRmStorageContainerLegalHold Remove-AzRmStorageContainerLegalHold Set-AzRmStorageContainerImmutabilityPolicy Get-AzRmStorageContainerImmutabilityPolicy Remove-AzRmStorageContainerImmutabilityPolicy Lock-AzRmStorageContainerImmutabilityPolicy Set-AzStorageAccountManagementPolicy Get-AzStorageAccountManagementPolicy Remove-AzStorageAccountManagementPolicy New-AzStorageAccountManagementPolicyFilter New-AzStorageAccountManagementPolicyRule Add-AzStorageAccountManagementPolicyAction Update-AzStorageBlobServiceProperty Get-AzStorageBlobServiceProperty Enable-AzStorageBlobDeleteRetentionPolicy Disable-AzStorageBlobDeleteRetentionPolicy Revoke-AzStorageAccountUserDelegationKeys Get-AzStorageFileHandle Close-AzStorageFileHandle New-AzRmStorageShare Remove-AzRmStorageShare Get-AzRmStorageShare Update-AzRmStorageShare Update-AzStorageFileServiceProperty Get-AzStorageFileServiceProperty Restore-AzRmStorageShare Get-AzDataLakeGen2ChildItem Get-AzDataLakeGen2Item New-AzDataLakeGen2Item Move-AzDataLakeGen2Item Remove-AzDataLakeGen2Item Update-AzDataLakeGen2Item Set-AzDataLakeGen2ItemAclObject Get-AzDataLakeGen2ItemContent Invoke-AzStorageAccountFailover Get-AzStorageBlobQueryResult New-AzStorageBlobQueryConfig New-AzStorageObjectReplicationPolicyRule Set-AzStorageObjectReplicationPolicy Get-AzStorageObjectReplicationPolicy Remove-AzStorageObjectReplicationPolicy Enable-AzStorageBlobRestorePolicy Disable-AzStorageBlobRestorePolicy New-AzStorageBlobRangeToRestore Restore-AzStorageBlobRange Set-AzDataLakeGen2AclRecursive Update-AzDataLakeGen2AclRecursive Remove-AzDataLakeGen2AclRecursive New-AzStorageEncryptionScope Update-AzStorageEncryptionScope Get-AzStorageEncryptionScope Copy-AzStorageBlob Set-AzStorageBlobInventoryPolicy New-AzStorageBlobInventoryPolicyRule Get-AzStorageBlobInventoryPolicy Remove-AzStorageBlobInventoryPolicy Enable-AzStorageContainerDeleteRetentionPolicy Disable-AzStorageContainerDeleteRetentionPolicy Restore-AzStorageContainer Enable-AzStorageBlobLastAccessTimeTracking Disable-AzStorageBlobLastAccessTimeTracking Set-AzStorageBlobTag Get-AzStorageBlobTag Get-AzStorageBlobByTag Invoke-AzStorageAccountHierarchicalNamespaceUpgrade Stop-AzStorageAccountHierarchicalNamespaceUpgrade Set-AzStorageBlobImmutabilityPolicy Remove-AzStorageBlobImmutabilityPolicy Set-AzStorageBlobLegalHold Invoke-AzRmStorageContainerImmutableStorageWithVersioningMigration</cmdlets> - <functions></functions> - <dscResources></dscResources> - <roleCapabilities></roleCapabilities> - </packageByName> - </Root>"; - -// string xmlString = @"<Root> -// <packageByName> -// <copyright>(c) 2020 Contoso Corporation. All rights reserved.</copyright> -// <description>this is a test module without including any categories</description> -// <iconUrl /> -// <licenseUrl /> -// <published>2020-09-21T15:46:13.317Z</published> -// <projectUrl /> -// <tags>Test CommandsAndResource Tag2 PSModule</tags> -// <title /> -// <version>5.0.0</version> -// <flattenedAuthors>americks</flattenedAuthors> -// <flattenedDependencies>RequiredModule1:(, ):|RequiredModule2:[2.0.0, ):|RequiredModule3:[2.5.0, 2.5.0]:|RequiredModule4:[1.1.0, 2.0.0]:|RequiredModule5:(, 1.5.0]:</flattenedDependencies> -// <isPrerelease>false</isPrerelease> -// <releaseNotes /> -// <normalizedVersion>5.0.0</normalizedVersion> -// <companyName>Me</companyName> -// <cmdlets></cmdlets> -// <functions></functions> -// <dscResources></dscResources> -// <roleCapabilities></roleCapabilities> -// </packageByName> -// </Root>"; - PSResourceInfo.TryConvertXmlFromGraphQL(xmlString, out PSResourceInfo obj, out string errMsg); - if (!String.IsNullOrEmpty(errMsg)) - { - WriteError(new ErrorRecord( - new PSInvalidOperationException(errMsg), - "ErrorFilteringNamesForUnsupportedWildcards", - ErrorCategory.InvalidArgument, - this)); - } - - WriteObject(obj); - return; - } + // if (Name[0].Equals("graphql")) + // { + // string xmlString = @"<Root>\r\n <packageByName>\r\n <packageId>test_module</packageId>\r\n <package>\r\n <copyright>(c) 2020 Contoso Corporation. All rights reserved.</copyright>\r\n <description>this is a test module without including any categories</description>\r\n <iconUrl />\r\n <licenseUrl />\r\n <published>2020-09-21T15:47:45.273Z</published>\r\n <projectUrl />\r\n <tags>Test CommandsAndResource Tag2 PSModule</tags>\r\n <title />\r\n <version>5.2.5-alpha001</version>\r\n <flattenedAuthors>americks</flattenedAuthors>\r\n <flattenedDependencies>RequiredModule1:(, ):|RequiredModule2:[2.0.0, ):|RequiredModule3:[2.5.0, 2.5.0]:|RequiredModule4:[1.1.0, 2.0.0]:|RequiredModule5:(, 1.5.0]:</flattenedDependencies>\r\n <isPrerelease>true</isPrerelease>\r\n <releaseNotes />\r\n <normalizedVersion>5.2.5-alpha001</normalizedVersion>\r\n <companyName>Me</companyName>\r\n <cmdlets></cmdlets>\r\n <functions></functions>\r\n <dscResources></dscResources>\r\n <roleCapabilities></roleCapabilities>\r\n </package>\r\n </packageByName>\r\n</Root>"; + // string responseToConvert = xmlString.Trim('\"').Replace("\\n", "").Replace("\\r", ""); + // if (!PSResourceInfo.TryConvertXmlFromGraphQL(responseToConvert, out PSResourceInfo psGetInfo, null, out string errMsg)) + // { + // WriteError(new ErrorRecord( + // new PSInvalidOperationException(errMsg), + // "ErrorFilteringNamesForUnsupportedWildcards", + // ErrorCategory.InvalidArgument, + // this)); + // } + + // WriteObject(psGetInfo); + // return; + // } // only cases where Name is allowed to not be specified is if Type or Tag parameters are if (!MyInvocation.BoundParameters.ContainsKey(nameof(Name))) diff --git a/src/code/PSResourceInfo.cs b/src/code/PSResourceInfo.cs index cc4b30802..78e7c78fc 100644 --- a/src/code/PSResourceInfo.cs +++ b/src/code/PSResourceInfo.cs @@ -469,18 +469,18 @@ private static Version GetVersionInfo( return GetProperty<Version>(nameof(PSResourceInfo.Version), psObjectInfo); } - /// <summary public static bool TryConvertXmlFromGraphQL( - string xmlString, // TODO: later this will be sent as a XmlDocument or XmlNode + XmlNode entry, out PSResourceInfo psGetInfo, + PSRepositoryInfo repository, out string errorMsg) { psGetInfo = null; errorMsg = String.Empty; - if (String.IsNullOrEmpty(xmlString)) + if (entry == null) { - errorMsg = "TryConvertXmlFromGraphQL: Invalid xmlString. String cannot be null."; + errorMsg = "TryConvertXmlFromGraphQL: Invalid XmlNode object. Object cannot be null."; return false; } @@ -488,25 +488,22 @@ public static bool TryConvertXmlFromGraphQL( { Hashtable metadata = new Hashtable(StringComparer.InvariantCultureIgnoreCase); - XmlDocument doc = new XmlDocument(); - doc.LoadXml(xmlString); - - XmlElement rootElement = doc.DocumentElement; - if (rootElement.HasChildNodes) + // this is where the metadata properties reside + var entryChildNodes = entry.ChildNodes; + foreach (XmlElement entryChild in entryChildNodes) { - XmlNode operationNameElement = rootElement.ChildNodes[0]; - if (operationNameElement.HasChildNodes) + var infoPropertyKey = entryChild.LocalName; + if (infoPropertyKey.Equals("packageId")) { - // this is where the metadata properties reside - var metadataElements = operationNameElement.ChildNodes; - foreach (XmlElement entryChild in metadataElements) + metadata["id"] = entryChild.InnerText; + } + else if (infoPropertyKey.Equals("package") && entryChild.HasChildNodes) + { + var pkgInfoElements = entryChild.ChildNodes; + foreach (XmlElement pkgObjChild in pkgInfoElements) { - var metadataPropertyKey = entryChild.LocalName; - var metadataPropertyValue = entryChild.InnerText; - if (metadataPropertyKey.Equals("title")) - { - metadata["id"] = metadataPropertyValue; - } + var metadataPropertyKey = pkgObjChild.LocalName; + var metadataPropertyValue = pkgObjChild.InnerText; if (metadataPropertyKey.Equals("version")) { metadata[metadataPropertyKey] = ParseHttpVersion(metadataPropertyValue, out string prereleaseLabel); @@ -557,60 +554,247 @@ public static bool TryConvertXmlFromGraphQL( } } - var typeInfo = ParseHttpMetadataType(metadata["tags"] as string[], out ArrayList commandNames, out ArrayList cmdletNames, out ArrayList dscResourceNames); - var resourceHashtable = new Hashtable { - { nameof(PSResourceInfo.Includes.Command), new PSObject(commandNames) }, - { nameof(PSResourceInfo.Includes.Cmdlet), new PSObject(cmdletNames) }, - { nameof(PSResourceInfo.Includes.DscResource), new PSObject(dscResourceNames) } - }; + } + } + + var typeInfo = ParseHttpMetadataType(metadata["tags"] as string[], out ArrayList commandNames, out ArrayList cmdletNames, out ArrayList dscResourceNames); + var resourceHashtable = new Hashtable { + { nameof(PSResourceInfo.Includes.Command), new PSObject(commandNames) }, + { nameof(PSResourceInfo.Includes.Cmdlet), new PSObject(cmdletNames) }, + { nameof(PSResourceInfo.Includes.DscResource), new PSObject(dscResourceNames) } + }; + + var additionalMetadataHashtable = new Dictionary<string, string>(); + + // Only add NormalizedVersion to additionalMetadata if server response included it + if (metadata.ContainsKey("normalizedVersion")) + { + additionalMetadataHashtable.Add("NormalizedVersion", metadata["normalizedVersion"].ToString()); + } + + var includes = new ResourceIncludes(resourceHashtable); + + psGetInfo = new PSResourceInfo( + additionalMetadata: additionalMetadataHashtable, + author: metadata["authors"] as String, + companyName: metadata["companyName"] as String, + copyright: metadata["copyright"] as String, + dependencies: metadata["dependencies"] as Dependency[], + description: metadata["description"] as String, + iconUri: metadata["iconUrl"] as Uri, + includes: includes, + installedDate: null, + installedLocation: null, + isPrerelease: (bool)metadata["isPrerelease"], + licenseUri: metadata["licenseUrl"] as Uri, + name: metadata["id"] as String, + powershellGetFormatVersion: null, + prerelease: metadata["prerelease"] as String, + projectUri: metadata["projectUrl"] as Uri, + publishedDate: metadata["published"] as DateTime?, + releaseNotes: metadata["releaseNotes"] as String, + repository: repository.Name, + repositorySourceLocation: repository.Uri.ToString(), + tags: metadata["tags"] as string[], + type: typeInfo, + updatedDate: null, + version: metadata["version"] as Version); + + return true; + } + catch (Exception ex) + { + errorMsg = string.Format( + CultureInfo.InvariantCulture, + @"TryConvertXmlFromGraphQL: Cannot parse PSResourceInfo from xml string with error: {0}", + ex.Message); + + return false; + } + } + + + + + + + + + + /// <summary + public static bool TryConvertXmlFromGraphQL2( + string xmlString, // TODO: later this will be sent as a XmlDocument or XmlNode + out PSResourceInfo psGetInfo, + PSRepositoryInfo repository, + out string errorMsg) + { + psGetInfo = null; + errorMsg = String.Empty; + + if (String.IsNullOrEmpty(xmlString)) + { + errorMsg = "TryConvertXmlFromGraphQL: Invalid xmlString. String cannot be null."; + return false; + } + + try + { + Hashtable metadata = new Hashtable(StringComparer.InvariantCultureIgnoreCase); + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xmlString); + XmlElement rootElement = doc.DocumentElement; + if (rootElement.HasChildNodes) + { + var entries = rootElement.ChildNodes; + int entriesReturnedCount = entries.Count; + if (entriesReturnedCount == 0) + { + // empty response, 4** situation, not 5** + errorMsg = string.Format( + CultureInfo.InvariantCulture, + @"TryConvertXmlFromGraphQL: Empty response"); - var additionalMetadataHashtable = new Dictionary<string, string>(); + return false; + } - // Only add NormalizedVersion to additionalMetadata if server response included it - if (metadata.ContainsKey("normalizedVersion")) + foreach (XmlNode operationNameElement in entries) + { + // XmlNode operationNameElement = rootElement.ChildNodes[0]; + if (operationNameElement.HasChildNodes) { - additionalMetadataHashtable.Add("NormalizedVersion", metadata["normalizedVersion"].ToString()); + // this is where the metadata properties reside + var metadataElements = operationNameElement.ChildNodes; + int metadataElementsCount = metadataElements.Count; + foreach (XmlElement infoNode in metadataElements) + { + var infoPropertyKey = infoNode.LocalName; + if (infoPropertyKey.Equals("packageId")) + { + metadata["id"] = infoNode.InnerText; + } + else if (infoPropertyKey.Equals("package") && infoNode.HasChildNodes) + { + var pkgInfoElements = infoNode.ChildNodes; + foreach (XmlElement pkgObjChild in pkgInfoElements) + { + var metadataPropertyKey = pkgObjChild.LocalName; + var metadataPropertyValue = pkgObjChild.InnerText; + if (metadataPropertyKey.Equals("version")) + { + metadata[metadataPropertyKey] = ParseHttpVersion(metadataPropertyValue, out string prereleaseLabel); + metadata["prerelease"] = prereleaseLabel; + } + else if (metadataPropertyKey.EndsWith("Url")) + { + metadata[metadataPropertyKey] = ParseHttpUrl(metadataPropertyValue) as Uri; + } + else if (metadataPropertyKey.Equals("tags")) + { + metadata[metadataPropertyKey] = metadataPropertyValue.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + } + else if (metadataPropertyKey.Equals("published")) + { + metadata[metadataPropertyKey] = ParseHttpDateTime(metadataPropertyValue); + } + else if (metadataPropertyKey.Equals("flattenedDependencies")) + { + metadata["dependencies"] = ParseHttpDependencies(metadataPropertyValue); + } + else if (metadataPropertyKey.Equals("isPrerelease")) + { + bool.TryParse(metadataPropertyValue, out bool isPrerelease); + + metadata[metadataPropertyKey] = isPrerelease; + } + else if (metadataPropertyKey.Equals("normalizedVersion")) + { + if (!NuGetVersion.TryParse(metadataPropertyValue, out NuGetVersion parsedNormalizedVersion)) + { + errorMsg = string.Format( + CultureInfo.InvariantCulture, + @"TryReadPSGetInfo: Cannot parse NormalizedVersion"); + + parsedNormalizedVersion = new NuGetVersion("1.0.0.0"); + } + + metadata[metadataPropertyKey] = parsedNormalizedVersion; + } + else if (metadataPropertyKey.Equals("flattenedAuthors")) + { + metadata["authors"] = metadataPropertyValue; + } + else + { + metadata[metadataPropertyKey] = metadataPropertyValue; + } + } + + } + } + + var typeInfo = ParseHttpMetadataType(metadata["tags"] as string[], out ArrayList commandNames, out ArrayList cmdletNames, out ArrayList dscResourceNames); + var resourceHashtable = new Hashtable { + { nameof(PSResourceInfo.Includes.Command), new PSObject(commandNames) }, + { nameof(PSResourceInfo.Includes.Cmdlet), new PSObject(cmdletNames) }, + { nameof(PSResourceInfo.Includes.DscResource), new PSObject(dscResourceNames) } + }; + + var additionalMetadataHashtable = new Dictionary<string, string>(); + + // Only add NormalizedVersion to additionalMetadata if server response included it + if (metadata.ContainsKey("normalizedVersion")) + { + additionalMetadataHashtable.Add("NormalizedVersion", metadata["normalizedVersion"].ToString()); + } + + var includes = new ResourceIncludes(resourceHashtable); + + psGetInfo = new PSResourceInfo( + additionalMetadata: additionalMetadataHashtable, + author: metadata["authors"] as String, + companyName: metadata["companyName"] as String, + copyright: metadata["copyright"] as String, + dependencies: metadata["dependencies"] as Dependency[], + description: metadata["description"] as String, + iconUri: metadata["iconUrl"] as Uri, + includes: includes, + installedDate: null, + installedLocation: null, + isPrerelease: (bool)metadata["isPrerelease"], + licenseUri: metadata["licenseUrl"] as Uri, + name: metadata["id"] as String, + powershellGetFormatVersion: null, + prerelease: metadata["prerelease"] as String, + projectUri: metadata["projectUrl"] as Uri, + publishedDate: metadata["published"] as DateTime?, + releaseNotes: metadata["releaseNotes"] as String, + repository: repository.Name, + repositorySourceLocation: repository.Uri.ToString(), + tags: metadata["tags"] as string[], + type: typeInfo, + updatedDate: null, + version: metadata["version"] as Version); + + return true; } + else + { + // empty response when element with operationName has 0 entries + errorMsg = string.Format( + CultureInfo.InvariantCulture, + @"TryConvertXmlFromGraphQL: Empty response"); - var includes = new ResourceIncludes(resourceHashtable); - - psGetInfo = new PSResourceInfo( - additionalMetadata: additionalMetadataHashtable, - author: metadata["authors"] as String, - companyName: metadata["companyName"] as String, - copyright: metadata["copyright"] as String, - dependencies: metadata["dependencies"] as Dependency[], - description: metadata["description"] as String, - iconUri: metadata["iconUrl"] as Uri, - includes: includes, - installedDate: null, - installedLocation: null, - isPrerelease: (bool)metadata["isPrerelease"], - licenseUri: metadata["licenseUrl"] as Uri, - name: String.Empty, //metadata["Id"] as String - powershellGetFormatVersion: null, - prerelease: metadata["prerelease"] as String, - projectUri: metadata["projectUrl"] as Uri, - publishedDate: metadata["published"] as DateTime?, - releaseNotes: metadata["releaseNotes"] as String, - repository: null, //repository.Name - repositorySourceLocation: String.Empty, //repository.Uri.ToString() - tags: metadata["tags"] as string[], - type: typeInfo, - updatedDate: null, - version: metadata["version"] as Version); - - return true; + return false; + } } } - - Console.WriteLine(rootElement.LocalName); } catch (Exception ex) { errorMsg = string.Format( CultureInfo.InvariantCulture, - @"TryConvertFromXml: Cannot parse PSResourceInfo from xml string with error: {0}", + @"TryConvertXmlFromGraphQL: Cannot parse PSResourceInfo from xml string with error: {0}", ex.Message); return false; diff --git a/src/code/V2ResponseUtil.cs b/src/code/V2ResponseUtil.cs index e62d15e77..01cd1a852 100644 --- a/src/code/V2ResponseUtil.cs +++ b/src/code/V2ResponseUtil.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Management.Automation; using System.Xml; namespace Microsoft.PowerShell.PSResourceGet.Cmdlets @@ -40,29 +41,68 @@ public override IEnumerable<PSResourceResult> ConvertToPSResourceResult(FindResu foreach (string response in responses) { - var elemList = ConvertResponseToXML(response); - if (elemList.Length == 0) + // if (Repository.Uri.AbsoluteUri.Contains("www.powershellgallery.com")) + // { + // string responseToConvert = response.Trim('\"').Replace("\\n", "").Replace("\\r", ""); + // if (!PSResourceInfo.TryConvertXmlFromGraphQL(responseToConvert, out PSResourceInfo psGetInfo, Repository, out string errorMsg)) + // { + // Exception parseException = new XmlParsingException(errorMsg); + + // yield return new PSResourceResult(returnedObject: null, exception: parseException, isTerminatingError: false); + // } + + // yield return new PSResourceResult(returnedObject: psGetInfo, exception: null, isTerminatingError: false); + // } + if (Repository.Uri.AbsoluteUri.Contains("www.powershellgallery.com")) { - // this indicates we got a non-empty, XML response (as noticed for V2 server) but it's not a response that's meaningful (contains 'properties') - Exception notFoundException = new ResourceNotFoundException("Package does not exist on the server"); + string responseToConvert = response.Trim('\"').Replace("\\n", "").Replace("\\r", ""); + var elemList = ConvertGraphQLResponseToXML(responseToConvert); + if (elemList.Length == 0) + { + // this indicates we got a non-empty, XML response (as noticed for V2 server) but it's not a response that's meaningful (contains 'properties') + Exception notFoundException = new ResourceNotFoundException("Package does not exist on the server"); - yield return new PSResourceResult(returnedObject: null, exception: notFoundException, isTerminatingError: false); - } + yield return new PSResourceResult(returnedObject: null, exception: notFoundException, isTerminatingError: false); + } - foreach (var element in elemList) + foreach(var element in elemList) + { + if (!PSResourceInfo.TryConvertXmlFromGraphQL(element, out PSResourceInfo psGetInfo, Repository, out string errorMsg)) + { + Exception parseException = new XmlParsingException(errorMsg); + + yield return new PSResourceResult(returnedObject: null, exception: parseException, isTerminatingError: false); + } + + yield return new PSResourceResult(returnedObject: psGetInfo, exception: null, isTerminatingError: false); + } + } + else { - if (!PSResourceInfo.TryConvertFromXml(element, out PSResourceInfo psGetInfo, Repository, out string errorMsg)) + var elemList = ConvertResponseToXML(response); + if (elemList.Length == 0) { - Exception parseException = new XmlParsingException(errorMsg); + // this indicates we got a non-empty, XML response (as noticed for V2 server) but it's not a response that's meaningful (contains 'properties') + Exception notFoundException = new ResourceNotFoundException("Package does not exist on the server"); - yield return new PSResourceResult(returnedObject: null, exception: parseException, isTerminatingError: false); + yield return new PSResourceResult(returnedObject: null, exception: notFoundException, isTerminatingError: false); } - // For V2 resources, specifically PSGallery, return unlisted version resources only when not requested with wildcard name - // Unlisted versions will have a published year as 1900 or earlier. - if (!isResourceRequestedWithWildcard || !psGetInfo.PublishedDate.HasValue || psGetInfo.PublishedDate.Value.Year > 1900) + foreach (var element in elemList) { - yield return new PSResourceResult(returnedObject: psGetInfo, exception: null, isTerminatingError: false); + if (!PSResourceInfo.TryConvertFromXml(element, out PSResourceInfo psGetInfo, Repository, out string errorMsg)) + { + Exception parseException = new XmlParsingException(errorMsg); + + yield return new PSResourceResult(returnedObject: null, exception: parseException, isTerminatingError: false); + } + + // For V2 resources, specifically PSGallery, return unlisted version resources only when not requested with wildcard name + // Unlisted versions will have a published year as 1900 or earlier. + if (!isResourceRequestedWithWildcard || !psGetInfo.PublishedDate.HasValue || psGetInfo.PublishedDate.Value.Year > 1900) + { + yield return new PSResourceResult(returnedObject: psGetInfo, exception: null, isTerminatingError: false); + } } } } @@ -72,7 +112,8 @@ public override IEnumerable<PSResourceResult> ConvertToPSResourceResult(FindResu #region V2 Specific Methods - public XmlNode[] ConvertResponseToXML(string httpResponse) { + public XmlNode[] ConvertResponseToXML(string httpResponse) + { NuGetVersion emptyVersion = new NuGetVersion("0.0.0.0"); NuGetVersion firstVersion = emptyVersion; NuGetVersion lastVersion = emptyVersion; @@ -136,6 +177,48 @@ public XmlNode[] ConvertResponseToXML(string httpResponse) { return nodes; } + + public XmlNode[] ConvertGraphQLResponseToXML(string graphQLResponse) + { + List<XmlNode> nodeList = new List<XmlNode>(); + // GraphQL response is a stringified XML, so trim quotes and remove extra space and newline escaped characters + string responseToConvert = graphQLResponse.Trim('\"').Replace("\\n", "").Replace("\\r", ""); + + // root + // operationName1 + // pkgId + // Package + // operationName2 + // pkgId + // Package + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(responseToConvert); + XmlElement rootElement = doc.DocumentElement; + if (!rootElement.HasChildNodes) + { + return nodeList.ToArray(); // TODO some error message? + } + + // operationName based element, there will be as many nodes as there were responses found (i.e if 2 packages matched, 2 operationName based elements) + var entryElements = rootElement.ChildNodes; + int entriesFound = entryElements.Count; + + foreach (XmlNode entry in entryElements) + { + if (entry.HasChildNodes) + { + // this node's child nodes will contain the metadata + nodeList.Add(entry); + } + else + { + continue; // what if FindName "existant", "nonExistant" returns <packageName> <metadata> </packageName> <packageName /> -- more testing needed + } + } + + return nodeList.ToArray(); + } #endregion } } diff --git a/src/code/V2ServerAPICalls.cs b/src/code/V2ServerAPICalls.cs index 94d0b3a0b..60cd71aaa 100644 --- a/src/code/V2ServerAPICalls.cs +++ b/src/code/V2ServerAPICalls.cs @@ -44,6 +44,8 @@ internal class V2ServerAPICalls : ServerApiCall private bool _isADORepo; private bool _isJFrogRepo; private bool _isPSGalleryRepo; + private bool _useGraphQL; + private string _graphQLUrl; #endregion @@ -82,6 +84,8 @@ public V2ServerAPICalls (PSRepositoryInfo repository, PSCmdlet cmdletPassedIn, N _isADORepo = repoURL.Contains("pkgs.dev.azure.com") || repoURL.Contains("pkgs.visualstudio.com"); _isJFrogRepo = repoURL.Contains("jfrog") || repoURL.Contains("artifactory"); _isPSGalleryRepo = repoURL.Contains("powershellgallery.com/api/v2"); + _useGraphQL = _isPSGalleryRepo; + _graphQLUrl = "https://localhost:7239"; } #endregion @@ -109,31 +113,39 @@ public override FindResults FindAll(bool includePrerelease, ResourceType type, o return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); } - int initialScriptCount = GetCountFromResponse(initialScriptResponse, out errRecord); - if (errRecord != null) + if (!_useGraphQL) { - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); - } + int initialScriptCount = GetCountFromResponse(initialScriptResponse, out errRecord); + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } - if (initialScriptCount != 0) - { - responses.Add(initialScriptResponse); - int count = initialScriptCount / 6000; - // if more than 100 count, loop and add response to list - while (count > 0) + if (initialScriptCount != 0) { - _cmdletPassedIn.WriteDebug($"Count is '{count}'"); - scriptSkip += 6000; - var tmpResponse = FindAllFromTypeEndPoint(includePrerelease, isSearchingModule: false, scriptSkip, out errRecord); - if (errRecord != null) + responses.Add(initialScriptResponse); + int count = initialScriptCount / 6000; + // if more than 100 count, loop and add response to list + while (count > 0) { - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + _cmdletPassedIn.WriteDebug($"Count is '{count}'"); + scriptSkip += 6000; + var tmpResponse = FindAllFromTypeEndPoint(includePrerelease, isSearchingModule: false, scriptSkip, out errRecord); + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + + responses.Add(tmpResponse); + count--; } - - responses.Add(tmpResponse); - count--; } } + else if(!initialScriptResponse.Equals("\"<Root />\"")) + { + // only add response if it's non-empty, i.e not just root node signifying empty response + responses.Add(initialScriptResponse); + } } if (type != ResourceType.Script) { @@ -144,32 +156,39 @@ public override FindResults FindAll(bool includePrerelease, ResourceType type, o return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); } - int initialModuleCount = GetCountFromResponse(initialModuleResponse, out errRecord); - if (errRecord != null) + if (!_useGraphQL) { - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); - } - - if (initialModuleCount != 0) - { - responses.Add(initialModuleResponse); - int count = initialModuleCount / 6000; + int initialModuleCount = GetCountFromResponse(initialModuleResponse, out errRecord); + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } - // if more than 100 count, loop and add response to list - while (count > 0) + if (initialModuleCount != 0) { - _cmdletPassedIn.WriteDebug($"Count is '{count}'"); - moduleSkip += 6000; - var tmpResponse = FindAllFromTypeEndPoint(includePrerelease, isSearchingModule: true, moduleSkip, out errRecord); - if (errRecord != null) + responses.Add(initialModuleResponse); + int count = initialModuleCount / 6000; + + // if more than 100 count, loop and add response to list + while (count > 0) { - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + _cmdletPassedIn.WriteDebug($"Count is '{count}'"); + moduleSkip += 6000; + var tmpResponse = FindAllFromTypeEndPoint(includePrerelease, isSearchingModule: true, moduleSkip, out errRecord); + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + + responses.Add(tmpResponse); + count--; } - - responses.Add(tmpResponse); - count--; } } + else if(!initialModuleResponse.Equals("\"<Root />\"")) + { + responses.Add(initialModuleResponse); + } } return new FindResults(stringResponse: responses.ToArray(), hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); @@ -191,37 +210,46 @@ public override FindResults FindTags(string[] tags, bool includePrerelease, Reso { int scriptSkip = 0; string initialScriptResponse = FindTagFromEndpoint(tags, includePrerelease, isSearchingModule: false, scriptSkip, out errRecord); - if (errRecord != null) - { - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); - } - int initialScriptCount = GetCountFromResponse(initialScriptResponse, out errRecord); if (errRecord != null) { return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); } - if (initialScriptCount != 0) + if (!_useGraphQL) { - responses.Add(initialScriptResponse); - int count = initialScriptCount / 100; - // if more than 100 count, loop and add response to list - while (count > 0) + int initialScriptCount = GetCountFromResponse(initialScriptResponse, out errRecord); + if (errRecord != null) { - _cmdletPassedIn.WriteDebug($"Count is '{count}'"); - // skip 100 - scriptSkip += 100; - var tmpResponse = FindTagFromEndpoint(tags, includePrerelease, isSearchingModule: false, scriptSkip, out errRecord); - if (errRecord != null) + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + + if (initialScriptCount != 0) + { + responses.Add(initialScriptResponse); + int count = initialScriptCount / 100; + // if more than 100 count, loop and add response to list + while (count > 0) { - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + _cmdletPassedIn.WriteDebug($"Count is '{count}'"); + // skip 100 + scriptSkip += 100; + var tmpResponse = FindTagFromEndpoint(tags, includePrerelease, isSearchingModule: false, scriptSkip, out errRecord); + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + + responses.Add(tmpResponse); + count--; } - - responses.Add(tmpResponse); - count--; } } + else if(!initialScriptResponse.Equals("\"<Root />\"")) + { + // only add response if it's non-empty, i.e not just root node signifying empty response + responses.Add(initialScriptResponse); + } } if (_type != ResourceType.Script) { @@ -232,34 +260,41 @@ public override FindResults FindTags(string[] tags, bool includePrerelease, Reso return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); } - int initialModuleCount = GetCountFromResponse(initialModuleResponse, out errRecord); - if (errRecord != null) + if (!_useGraphQL) { - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); - } + int initialModuleCount = GetCountFromResponse(initialModuleResponse, out errRecord); + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } - if (initialModuleCount != 0) - { - responses.Add(initialModuleResponse); - int count = initialModuleCount / 100; - // if more than 100 count, loop and add response to list - while (count > 0) + if (initialModuleCount != 0) { - _cmdletPassedIn.WriteDebug($"Count is '{count}'"); - moduleSkip += 100; - var tmpResponse = FindTagFromEndpoint(tags, includePrerelease, isSearchingModule: true, moduleSkip, out errRecord); - if (errRecord != null) + responses.Add(initialModuleResponse); + int count = initialModuleCount / 100; + // if more than 100 count, loop and add response to list + while (count > 0) { - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + _cmdletPassedIn.WriteDebug($"Count is '{count}'"); + moduleSkip += 100; + var tmpResponse = FindTagFromEndpoint(tags, includePrerelease, isSearchingModule: true, moduleSkip, out errRecord); + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + + responses.Add(tmpResponse); + count--; } - - responses.Add(tmpResponse); - count--; } } + else if(!initialModuleResponse.Equals("\"<Root />\"")) + { + responses.Add(initialModuleResponse); + } } - if (responses.Count == 0) + if (!_useGraphQL && responses.Count == 0) { errRecord = new ErrorRecord( new ResourceNotFoundException($"Package with Tags '{String.Join(", ", tags)}' could not be found in repository '{Repository.Name}'."), @@ -360,38 +395,54 @@ public override FindResults FindName(string packageName, bool includePrerelease, filterBuilder.AddCriterion(GetTypeFilterForRequest(type)); } - var requestUrlV2 = $"{Repository.Uri}/FindPackagesById()?{queryBuilder.BuildQueryString()}"; - string response = HttpRequestCall(requestUrlV2, out errRecord); - if (errRecord != null) + var requestUrlV2 = ""; + string response = ""; + errRecord = null; + + if (_useGraphQL) { - // usually this is for errors in calling the V2 server, but for ADO V2 this error will include package not found errors which we want to deliver in a standard message - if (_isADORepo && errRecord.Exception is ResourceNotFoundException) + requestUrlV2 = $"{_graphQLUrl}/api/v2/FindPackagesById?{queryBuilder.BuildQueryString()}"; + response = HttpRequestCall(requestUrlV2, out errRecord); + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + } + else + { + requestUrlV2 = $"{Repository.Uri}/FindPackagesById()?{queryBuilder.BuildQueryString()}"; + response = HttpRequestCall(requestUrlV2, out errRecord); + if (errRecord != null) + { + // usually this is for errors in calling the V2 server, but for ADO V2 this error will include package not found errors which we want to deliver in a standard message + if (_isADORepo && errRecord.Exception is ResourceNotFoundException) + { + errRecord = new ErrorRecord( + new ResourceNotFoundException($"Package with name '{packageName}' could not be found in repository '{Repository.Name}'. For ADO feed, if the package is in an upstream feed make sure you are authenticated to the upstream feed.", errRecord.Exception), + "PackageNotFound", + ErrorCategory.ObjectNotFound, + this); + response = string.Empty; + } + + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + + int count = GetCountFromResponse(response, out errRecord); + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + + if (count == 0) { errRecord = new ErrorRecord( - new ResourceNotFoundException($"Package with name '{packageName}' could not be found in repository '{Repository.Name}'. For ADO feed, if the package is in an upstream feed make sure you are authenticated to the upstream feed.", errRecord.Exception), + new ResourceNotFoundException($"Package with name '{packageName}' could not be found in repository '{Repository.Name}'."), "PackageNotFound", ErrorCategory.ObjectNotFound, this); response = string.Empty; } - - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); - } - - int count = GetCountFromResponse(response, out errRecord); - if (errRecord != null) - { - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); - } - - if (count == 0) - { - errRecord = new ErrorRecord( - new ResourceNotFoundException($"Package with name '{packageName}' could not be found in repository '{Repository.Name}'."), - "PackageNotFound", - ErrorCategory.ObjectNotFound, - this); - response = string.Empty; } return new FindResults(stringResponse: new string[]{ response }, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); @@ -589,35 +640,42 @@ public override FindResults FindVersionGlobbing(string packageName, VersionRange return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); } - int initialCount = GetCountFromResponse(initialResponse, out errRecord); - if (errRecord != null) + if (_useGraphQL) { - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + responses.Add(initialResponse); } - - if (initialCount == 0) + else { - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); - } + int initialCount = GetCountFromResponse(initialResponse, out errRecord); + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } - responses.Add(initialResponse); + if (initialCount == 0) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } - if (!getOnlyLatest) - { - int count = (int)Math.Ceiling((double)(initialCount / 100)); + responses.Add(initialResponse); - while (count > 0) + if (!getOnlyLatest) { - _cmdletPassedIn.WriteDebug($"Count is '{count}'"); - // skip 100 - skip += 100; - var tmpResponse = FindVersionGlobbing(packageName, versionRange, includePrerelease, type, skip, getOnlyLatest, out errRecord); - if (errRecord != null) + int count = (int)Math.Ceiling((double)(initialCount / 100)); + + while (count > 0) { - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + _cmdletPassedIn.WriteDebug($"Count is '{count}'"); + // skip 100 + skip += 100; + var tmpResponse = FindVersionGlobbing(packageName, versionRange, includePrerelease, type, skip, getOnlyLatest, out errRecord); + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + responses.Add(tmpResponse); + count--; } - responses.Add(tmpResponse); - count--; } } @@ -655,7 +713,16 @@ public override FindResults FindVersion(string packageName, string version, Reso filterBuilder.AddCriterion(GetTypeFilterForRequest(type)); } - var requestUrlV2 = $"{Repository.Uri}/FindPackagesById()?{queryBuilder.BuildQueryString()}"; + string requestUrlV2 = ""; + if (_useGraphQL) + { + requestUrlV2 = $"{_graphQLUrl}/api/v2/FindPackagesById?{queryBuilder.BuildQueryString()}"; + } + else + { + requestUrlV2 = $"{Repository.Uri}/FindPackagesById()?{queryBuilder.BuildQueryString()}"; + } + string response = HttpRequestCall(requestUrlV2, out errRecord); if (errRecord != null) { @@ -673,22 +740,25 @@ public override FindResults FindVersion(string packageName, string version, Reso return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); } - int count = GetCountFromResponse(response, out errRecord); - _cmdletPassedIn.WriteDebug($"Count from response is '{count}'"); - - if (errRecord != null) + if (!_useGraphQL) { - return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); - } + int count = GetCountFromResponse(response, out errRecord); + _cmdletPassedIn.WriteDebug($"Count from response is '{count}'"); - if (count == 0) - { - errRecord = new ErrorRecord( - new ResourceNotFoundException($"Package with name '{packageName}', version '{version}' could not be found in repository '{Repository.Name}'."), - "PackageNotFound", - ErrorCategory.ObjectNotFound, - this); - response = string.Empty; + if (errRecord != null) + { + return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); + } + + if (count == 0) + { + errRecord = new ErrorRecord( + new ResourceNotFoundException($"Package with name '{packageName}', version '{version}' could not be found in repository '{Repository.Name}'."), + "PackageNotFound", + ErrorCategory.ObjectNotFound, + this); + response = string.Empty; + } } return new FindResults(stringResponse: new string[] { response }, hashtableResponse: emptyHashResponses, responseType: v2FindResponseType); @@ -935,7 +1005,16 @@ private string FindAllFromTypeEndPoint(bool includePrerelease, bool isSearchingM } } - var requestUrlV2 = $"{Repository.Uri}{typeEndpoint}/Search()?{queryBuilder.BuildQueryString()}"; + string requestUrlV2 = ""; + if (_useGraphQL) + { + requestUrlV2 = $"{_graphQLUrl}/api/v2/FindPackagesById?{queryBuilder.BuildQueryString()}"; + } + else + { + requestUrlV2 = $"{Repository.Uri}{typeEndpoint}/Search()?{queryBuilder.BuildQueryString()}"; + } + return HttpRequestCall(requestUrlV2, out errRecord); } @@ -991,7 +1070,15 @@ private string FindTagFromEndpoint(string[] tags, bool includePrerelease, bool i filterBuilder.AddCriterion($"substringof('{tag}', Tags) eq true"); } - var requestUrlV2 = $"{Repository.Uri}{typeEndpoint}/Search()?{queryBuilder.BuildQueryString()}"; + string requestUrlV2 = ""; + if (_useGraphQL) + { + requestUrlV2 = $"{_graphQLUrl}/api/v2/FindPackagesById?{queryBuilder.BuildQueryString()}"; + } + else + { + requestUrlV2 = $"{Repository.Uri}{typeEndpoint}/Search()?{queryBuilder.BuildQueryString()}"; + } return HttpRequestCall(requestUrlV2: requestUrlV2, out errRecord); } @@ -1366,8 +1453,15 @@ private string FindVersionGlobbing(string packageName, VersionRange versionRange filterBuilder.AddCriterion($"substringof('PS{type.ToString()}', Tags) eq true"); } - - var requestUrlV2 = $"{Repository.Uri}/FindPackagesById()?{queryBuilder.BuildQueryString()}"; + string requestUrlV2 = ""; + if (_useGraphQL) + { + requestUrlV2 = $"{_graphQLUrl}/api/v2/FindPackagesById?{queryBuilder.BuildQueryString()}"; + } + else + { + requestUrlV2 = $"{Repository.Uri}/FindPackagesById()?{queryBuilder.BuildQueryString()}"; + } return HttpRequestCall(requestUrlV2, out errRecord); } From 82b34a0fed07e2075de4f6560db84718f9bc1960 Mon Sep 17 00:00:00 2001 From: anamnavi <annavied@microsoft.com> Date: Wed, 22 Oct 2025 18:14:47 -0400 Subject: [PATCH 3/3] clean up comments and debug statmeents --- src/code/FindPSResource.cs | 17 ---- src/code/PSResourceInfo.cs | 192 ------------------------------------- src/code/V2ResponseUtil.cs | 13 --- 3 files changed, 222 deletions(-) diff --git a/src/code/FindPSResource.cs b/src/code/FindPSResource.cs index a95a23537..2709073e7 100644 --- a/src/code/FindPSResource.cs +++ b/src/code/FindPSResource.cs @@ -167,23 +167,6 @@ protected override void ProcessRecord() private void ProcessResourceNameParameterSet() { - // if (Name[0].Equals("graphql")) - // { - // string xmlString = @"<Root>\r\n <packageByName>\r\n <packageId>test_module</packageId>\r\n <package>\r\n <copyright>(c) 2020 Contoso Corporation. All rights reserved.</copyright>\r\n <description>this is a test module without including any categories</description>\r\n <iconUrl />\r\n <licenseUrl />\r\n <published>2020-09-21T15:47:45.273Z</published>\r\n <projectUrl />\r\n <tags>Test CommandsAndResource Tag2 PSModule</tags>\r\n <title />\r\n <version>5.2.5-alpha001</version>\r\n <flattenedAuthors>americks</flattenedAuthors>\r\n <flattenedDependencies>RequiredModule1:(, ):|RequiredModule2:[2.0.0, ):|RequiredModule3:[2.5.0, 2.5.0]:|RequiredModule4:[1.1.0, 2.0.0]:|RequiredModule5:(, 1.5.0]:</flattenedDependencies>\r\n <isPrerelease>true</isPrerelease>\r\n <releaseNotes />\r\n <normalizedVersion>5.2.5-alpha001</normalizedVersion>\r\n <companyName>Me</companyName>\r\n <cmdlets></cmdlets>\r\n <functions></functions>\r\n <dscResources></dscResources>\r\n <roleCapabilities></roleCapabilities>\r\n </package>\r\n </packageByName>\r\n</Root>"; - // string responseToConvert = xmlString.Trim('\"').Replace("\\n", "").Replace("\\r", ""); - // if (!PSResourceInfo.TryConvertXmlFromGraphQL(responseToConvert, out PSResourceInfo psGetInfo, null, out string errMsg)) - // { - // WriteError(new ErrorRecord( - // new PSInvalidOperationException(errMsg), - // "ErrorFilteringNamesForUnsupportedWildcards", - // ErrorCategory.InvalidArgument, - // this)); - // } - - // WriteObject(psGetInfo); - // return; - // } - // only cases where Name is allowed to not be specified is if Type or Tag parameters are if (!MyInvocation.BoundParameters.ContainsKey(nameof(Name))) { diff --git a/src/code/PSResourceInfo.cs b/src/code/PSResourceInfo.cs index 78e7c78fc..38e6b8c26 100644 --- a/src/code/PSResourceInfo.cs +++ b/src/code/PSResourceInfo.cs @@ -613,198 +613,6 @@ public static bool TryConvertXmlFromGraphQL( } } - - - - - - - - - /// <summary - public static bool TryConvertXmlFromGraphQL2( - string xmlString, // TODO: later this will be sent as a XmlDocument or XmlNode - out PSResourceInfo psGetInfo, - PSRepositoryInfo repository, - out string errorMsg) - { - psGetInfo = null; - errorMsg = String.Empty; - - if (String.IsNullOrEmpty(xmlString)) - { - errorMsg = "TryConvertXmlFromGraphQL: Invalid xmlString. String cannot be null."; - return false; - } - - try - { - Hashtable metadata = new Hashtable(StringComparer.InvariantCultureIgnoreCase); - - XmlDocument doc = new XmlDocument(); - doc.LoadXml(xmlString); - XmlElement rootElement = doc.DocumentElement; - if (rootElement.HasChildNodes) - { - var entries = rootElement.ChildNodes; - int entriesReturnedCount = entries.Count; - if (entriesReturnedCount == 0) - { - // empty response, 4** situation, not 5** - errorMsg = string.Format( - CultureInfo.InvariantCulture, - @"TryConvertXmlFromGraphQL: Empty response"); - - return false; - } - - foreach (XmlNode operationNameElement in entries) - { - // XmlNode operationNameElement = rootElement.ChildNodes[0]; - if (operationNameElement.HasChildNodes) - { - // this is where the metadata properties reside - var metadataElements = operationNameElement.ChildNodes; - int metadataElementsCount = metadataElements.Count; - foreach (XmlElement infoNode in metadataElements) - { - var infoPropertyKey = infoNode.LocalName; - if (infoPropertyKey.Equals("packageId")) - { - metadata["id"] = infoNode.InnerText; - } - else if (infoPropertyKey.Equals("package") && infoNode.HasChildNodes) - { - var pkgInfoElements = infoNode.ChildNodes; - foreach (XmlElement pkgObjChild in pkgInfoElements) - { - var metadataPropertyKey = pkgObjChild.LocalName; - var metadataPropertyValue = pkgObjChild.InnerText; - if (metadataPropertyKey.Equals("version")) - { - metadata[metadataPropertyKey] = ParseHttpVersion(metadataPropertyValue, out string prereleaseLabel); - metadata["prerelease"] = prereleaseLabel; - } - else if (metadataPropertyKey.EndsWith("Url")) - { - metadata[metadataPropertyKey] = ParseHttpUrl(metadataPropertyValue) as Uri; - } - else if (metadataPropertyKey.Equals("tags")) - { - metadata[metadataPropertyKey] = metadataPropertyValue.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); - } - else if (metadataPropertyKey.Equals("published")) - { - metadata[metadataPropertyKey] = ParseHttpDateTime(metadataPropertyValue); - } - else if (metadataPropertyKey.Equals("flattenedDependencies")) - { - metadata["dependencies"] = ParseHttpDependencies(metadataPropertyValue); - } - else if (metadataPropertyKey.Equals("isPrerelease")) - { - bool.TryParse(metadataPropertyValue, out bool isPrerelease); - - metadata[metadataPropertyKey] = isPrerelease; - } - else if (metadataPropertyKey.Equals("normalizedVersion")) - { - if (!NuGetVersion.TryParse(metadataPropertyValue, out NuGetVersion parsedNormalizedVersion)) - { - errorMsg = string.Format( - CultureInfo.InvariantCulture, - @"TryReadPSGetInfo: Cannot parse NormalizedVersion"); - - parsedNormalizedVersion = new NuGetVersion("1.0.0.0"); - } - - metadata[metadataPropertyKey] = parsedNormalizedVersion; - } - else if (metadataPropertyKey.Equals("flattenedAuthors")) - { - metadata["authors"] = metadataPropertyValue; - } - else - { - metadata[metadataPropertyKey] = metadataPropertyValue; - } - } - - } - } - - var typeInfo = ParseHttpMetadataType(metadata["tags"] as string[], out ArrayList commandNames, out ArrayList cmdletNames, out ArrayList dscResourceNames); - var resourceHashtable = new Hashtable { - { nameof(PSResourceInfo.Includes.Command), new PSObject(commandNames) }, - { nameof(PSResourceInfo.Includes.Cmdlet), new PSObject(cmdletNames) }, - { nameof(PSResourceInfo.Includes.DscResource), new PSObject(dscResourceNames) } - }; - - var additionalMetadataHashtable = new Dictionary<string, string>(); - - // Only add NormalizedVersion to additionalMetadata if server response included it - if (metadata.ContainsKey("normalizedVersion")) - { - additionalMetadataHashtable.Add("NormalizedVersion", metadata["normalizedVersion"].ToString()); - } - - var includes = new ResourceIncludes(resourceHashtable); - - psGetInfo = new PSResourceInfo( - additionalMetadata: additionalMetadataHashtable, - author: metadata["authors"] as String, - companyName: metadata["companyName"] as String, - copyright: metadata["copyright"] as String, - dependencies: metadata["dependencies"] as Dependency[], - description: metadata["description"] as String, - iconUri: metadata["iconUrl"] as Uri, - includes: includes, - installedDate: null, - installedLocation: null, - isPrerelease: (bool)metadata["isPrerelease"], - licenseUri: metadata["licenseUrl"] as Uri, - name: metadata["id"] as String, - powershellGetFormatVersion: null, - prerelease: metadata["prerelease"] as String, - projectUri: metadata["projectUrl"] as Uri, - publishedDate: metadata["published"] as DateTime?, - releaseNotes: metadata["releaseNotes"] as String, - repository: repository.Name, - repositorySourceLocation: repository.Uri.ToString(), - tags: metadata["tags"] as string[], - type: typeInfo, - updatedDate: null, - version: metadata["version"] as Version); - - return true; - } - else - { - // empty response when element with operationName has 0 entries - errorMsg = string.Format( - CultureInfo.InvariantCulture, - @"TryConvertXmlFromGraphQL: Empty response"); - - return false; - } - } - } - } - catch (Exception ex) - { - errorMsg = string.Format( - CultureInfo.InvariantCulture, - @"TryConvertXmlFromGraphQL: Cannot parse PSResourceInfo from xml string with error: {0}", - ex.Message); - - return false; - } - - return true; - } - - - /// <summary> /// Converts XML entry to PSResourceInfo instance /// used for V2 Server API call find response conversion to PSResourceInfo object diff --git a/src/code/V2ResponseUtil.cs b/src/code/V2ResponseUtil.cs index 01cd1a852..0fcc755bc 100644 --- a/src/code/V2ResponseUtil.cs +++ b/src/code/V2ResponseUtil.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Management.Automation; using System.Xml; namespace Microsoft.PowerShell.PSResourceGet.Cmdlets @@ -41,18 +40,6 @@ public override IEnumerable<PSResourceResult> ConvertToPSResourceResult(FindResu foreach (string response in responses) { - // if (Repository.Uri.AbsoluteUri.Contains("www.powershellgallery.com")) - // { - // string responseToConvert = response.Trim('\"').Replace("\\n", "").Replace("\\r", ""); - // if (!PSResourceInfo.TryConvertXmlFromGraphQL(responseToConvert, out PSResourceInfo psGetInfo, Repository, out string errorMsg)) - // { - // Exception parseException = new XmlParsingException(errorMsg); - - // yield return new PSResourceResult(returnedObject: null, exception: parseException, isTerminatingError: false); - // } - - // yield return new PSResourceResult(returnedObject: psGetInfo, exception: null, isTerminatingError: false); - // } if (Repository.Uri.AbsoluteUri.Contains("www.powershellgallery.com")) { string responseToConvert = response.Trim('\"').Replace("\\n", "").Replace("\\r", "");