Skip to content

Commit 1dd4bfb

Browse files
authored
ACR: Add resource type to returned PSResourceInfo object (PowerShell#1594)
1 parent c353ebe commit 1dd4bfb

File tree

5 files changed

+172
-83
lines changed

5 files changed

+172
-83
lines changed

src/code/ACRResponseUtil.cs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,24 @@ public override IEnumerable<PSResourceResult> ConvertToPSResourceResult(FindResu
4242
string responseConversionError = String.Empty;
4343
PSResourceInfo pkg = null;
4444

45-
string packageName = string.Empty;
46-
string packageMetadata = null;
45+
// Hashtable should have keys for Name, Metadata, ResourceType
46+
if (!response.ContainsKey("Name") && string.IsNullOrWhiteSpace(response["Name"].ToString()))
47+
{
48+
yield return new PSResourceResult(returnedObject: pkg, exception: new ConvertToPSResourceException("Error retrieving package name from response."), isTerminatingError: true);
49+
}
4750

48-
foreach (DictionaryEntry entry in response)
51+
if (!response.ContainsKey("Metadata"))
4952
{
50-
packageName = (string)entry.Key;
51-
packageMetadata = (string)entry.Value;
53+
yield return new PSResourceResult(returnedObject: pkg, exception: new ConvertToPSResourceException("Error retrieving package metadata from response."), isTerminatingError: true);
5254
}
5355

56+
ResourceType? resourceType = response.ContainsKey("ResourceType") ? response["ResourceType"] as ResourceType? : ResourceType.None;
57+
5458
try
5559
{
56-
using (JsonDocument pkgVersionEntry = JsonDocument.Parse(packageMetadata))
60+
using (JsonDocument pkgVersionEntry = JsonDocument.Parse(response["Metadata"].ToString()))
5761
{
58-
PSResourceInfo.TryConvertFromACRJson(packageName, pkgVersionEntry, out pkg, Repository, out responseConversionError);
62+
PSResourceInfo.TryConvertFromACRJson(response["Name"].ToString(), pkgVersionEntry, resourceType, out pkg, Repository, out responseConversionError);
5963
}
6064
}
6165
catch (Exception e)

src/code/ACRServerAPICalls.cs

Lines changed: 90 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ public override FindResults FindVersion(string packageName, string version, Reso
243243

244244
return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: acrFindResponseType);
245245
}
246-
246+
247247
_cmdletPassedIn.WriteDebug($"'{packageName}' version parsed as '{requiredVersion}'");
248248
bool includePrereleaseVersions = requiredVersion.IsPrerelease;
249249

@@ -563,124 +563,140 @@ internal Hashtable GetACRMetadata(string registry, string packageName, string ex
563563
* }
564564
*/
565565

566-
Tuple<string,string> metadataTuple = GetMetadataProperty(foundTags, packageName, out Exception exception);
566+
var serverPkgInfo = GetMetadataProperty(foundTags, packageName, out Exception exception);
567567
if (exception != null)
568568
{
569-
errRecord = new ErrorRecord(exception, "FindNameFailure", ErrorCategory.InvalidResult, this);
569+
errRecord = new ErrorRecord(exception, "ParseMetadataFailure", ErrorCategory.InvalidResult, this);
570570

571571
return requiredVersionResponse;
572572
}
573573

574-
string metadataPkgName = metadataTuple.Item1;
575-
string metadata = metadataTuple.Item2;
576-
string pkgVersionString = String.Empty;
577-
using (JsonDocument metadataJSONDoc = JsonDocument.Parse(metadata))
574+
try
578575
{
579-
JsonElement rootDom = metadataJSONDoc.RootElement;
580-
if (rootDom.TryGetProperty("ModuleVersion", out JsonElement pkgVersionElement))
576+
using (JsonDocument metadataJSONDoc = JsonDocument.Parse(serverPkgInfo.Metadata))
581577
{
582-
// module metadata will have "ModuleVersion" property
583-
pkgVersionString = pkgVersionElement.ToString();
584-
if (rootDom.TryGetProperty("PrivateData", out JsonElement pkgPrivateDataElement) && pkgPrivateDataElement.TryGetProperty("PSData", out JsonElement pkgPSDataElement)
585-
&& pkgPSDataElement.TryGetProperty("Prerelease", out JsonElement pkgPrereleaseLabelElement) && !String.IsNullOrEmpty(pkgPrereleaseLabelElement.ToString().Trim()))
578+
string pkgVersionString = String.Empty;
579+
JsonElement rootDom = metadataJSONDoc.RootElement;
580+
if (rootDom.TryGetProperty("ModuleVersion", out JsonElement pkgVersionElement))
586581
{
587-
pkgVersionString += $"-{pkgPrereleaseLabelElement.ToString()}";
582+
// module metadata will have "ModuleVersion" property
583+
pkgVersionString = pkgVersionElement.ToString();
584+
if (rootDom.TryGetProperty("PrivateData", out JsonElement pkgPrivateDataElement) && pkgPrivateDataElement.TryGetProperty("PSData", out JsonElement pkgPSDataElement)
585+
&& pkgPSDataElement.TryGetProperty("Prerelease", out JsonElement pkgPrereleaseLabelElement) && !String.IsNullOrEmpty(pkgPrereleaseLabelElement.ToString().Trim()))
586+
{
587+
pkgVersionString += $"-{pkgPrereleaseLabelElement.ToString()}";
588+
}
588589
}
589-
}
590-
else if(rootDom.TryGetProperty("Version", out pkgVersionElement))
591-
{
592-
// script metadata will have "Version" property
593-
pkgVersionString = pkgVersionElement.ToString();
594-
}
595-
else
596-
{
597-
errRecord = new ErrorRecord(
598-
new InvalidOrEmptyResponse($"Response does not contain 'ModuleVersion' or 'Version' property in metadata for package '{packageName}' in '{Repository.Name}'."),
599-
"FindNameFailure",
600-
ErrorCategory.InvalidResult,
601-
this);
590+
else if (rootDom.TryGetProperty("Version", out pkgVersionElement))
591+
{
592+
// script metadata will have "Version" property
593+
pkgVersionString = pkgVersionElement.ToString();
594+
}
595+
else
596+
{
597+
errRecord = new ErrorRecord(
598+
new InvalidOrEmptyResponse($"Response does not contain 'ModuleVersion' or 'Version' property in metadata for package '{packageName}' in '{Repository.Name}'."),
599+
"ParseMetadataFailure",
600+
ErrorCategory.InvalidResult,
601+
this);
602602

603-
return requiredVersionResponse;
604-
}
603+
return requiredVersionResponse;
604+
}
605605

606-
if (!NuGetVersion.TryParse(pkgVersionString, out NuGetVersion pkgVersion))
607-
{
608-
errRecord = new ErrorRecord(
609-
new ArgumentException($"Version {pkgVersionString} to be parsed from metadata is not a valid NuGet version."),
610-
"FindNameFailure",
611-
ErrorCategory.InvalidArgument,
612-
this);
606+
if (!NuGetVersion.TryParse(pkgVersionString, out NuGetVersion pkgVersion))
607+
{
608+
errRecord = new ErrorRecord(
609+
new ArgumentException($"Version {pkgVersionString} to be parsed from metadata is not a valid NuGet version."),
610+
"ParseMetadataFailure",
611+
ErrorCategory.InvalidArgument,
612+
this);
613613

614-
return requiredVersionResponse;
615-
}
614+
return requiredVersionResponse;
615+
}
616616

617-
if (!NuGetVersion.TryParse(exactTagVersion, out NuGetVersion requiredVersion))
618-
{
619-
errRecord = new ErrorRecord(
620-
new ArgumentException($"Version {exactTagVersion} to be parsed from method input is not a valid NuGet version."),
621-
"FindNameFailure",
622-
ErrorCategory.InvalidArgument,
623-
this);
617+
if (!NuGetVersion.TryParse(exactTagVersion, out NuGetVersion requiredVersion))
618+
{
619+
errRecord = new ErrorRecord(
620+
new ArgumentException($"Version {exactTagVersion} to be parsed from method input is not a valid NuGet version."),
621+
"ParseMetadataFailure",
622+
ErrorCategory.InvalidArgument,
623+
this);
624624

625-
return requiredVersionResponse;
626-
}
625+
return requiredVersionResponse;
626+
}
627627

628-
_cmdletPassedIn.WriteDebug($"'{packageName}' version parsed as '{pkgVersion}'");
629-
if (pkgVersion.ToNormalizedString() == requiredVersion.ToNormalizedString())
630-
{
631-
requiredVersionResponse.Add(metadataPkgName, metadata);
628+
_cmdletPassedIn.WriteDebug($"'{packageName}' version parsed as '{pkgVersion}'");
629+
if (pkgVersion.ToNormalizedString() == requiredVersion.ToNormalizedString())
630+
{
631+
requiredVersionResponse = serverPkgInfo.ToHashtable();
632+
}
632633
}
633634
}
635+
catch (Exception e)
636+
{
637+
errRecord = new ErrorRecord(
638+
new ArgumentException($"Error parsing server metadata: {e.Message}"),
639+
"ParseMetadataFailure",
640+
ErrorCategory.InvalidData,
641+
this);
642+
643+
return requiredVersionResponse;
644+
}
634645

635646
return requiredVersionResponse;
636647
}
637648

638-
internal Tuple<string,string> GetMetadataProperty(JObject foundTags, string packageName, out Exception exception)
649+
internal ContainerRegistryInfo GetMetadataProperty(JObject foundTags, string packageName, out Exception exception)
639650
{
640651
exception = null;
641-
var emptyTuple = new Tuple<string, string>(string.Empty, string.Empty);
652+
ContainerRegistryInfo serverPkgInfo = null;
642653
var layers = foundTags["layers"];
643654
if (layers == null || layers[0] == null)
644655
{
645656
exception = new InvalidOrEmptyResponse($"Response does not contain 'layers' element in manifest for package '{packageName}' in '{Repository.Name}'.");
646657

647-
return emptyTuple;
658+
return serverPkgInfo;
648659
}
649660

650661
var annotations = layers[0]["annotations"];
651662
if (annotations == null)
652663
{
653664
exception = new InvalidOrEmptyResponse($"Response does not contain 'annotations' element in manifest for package '{packageName}' in '{Repository.Name}'.");
654665

655-
return emptyTuple;
666+
return serverPkgInfo;
656667
}
657668

658-
if (annotations["metadata"] == null)
669+
// Check for package name
670+
var pkgTitleJToken = annotations["org.opencontainers.image.title"];
671+
if (pkgTitleJToken == null)
659672
{
660-
exception = new InvalidOrEmptyResponse($"Response does not contain 'metadata' element in manifest for package '{packageName}' in '{Repository.Name}'.");
673+
exception = new InvalidOrEmptyResponse($"Response does not contain 'org.opencontainers.image.title' element for package '{packageName}' in '{Repository.Name}'.");
661674

662-
return emptyTuple;
675+
return serverPkgInfo;
663676
}
664-
665-
var metadata = annotations["metadata"].ToString();
666-
667-
var metadataPkgTitleJToken = annotations["org.opencontainers.image.title"];
668-
if (metadataPkgTitleJToken == null)
677+
string metadataPkgName = pkgTitleJToken.ToString();
678+
if (string.IsNullOrWhiteSpace(metadataPkgName))
669679
{
670-
exception = new InvalidOrEmptyResponse($"Response does not contain 'org.opencontainers.image.title' element for package '{packageName}' in '{Repository.Name}'.");
680+
exception = new InvalidOrEmptyResponse($"Response element 'org.opencontainers.image.title' is empty for package '{packageName}' in '{Repository.Name}'.");
671681

672-
return emptyTuple;
682+
return serverPkgInfo;
673683
}
674684

675-
string metadataPkgName = metadataPkgTitleJToken.ToString();
676-
if (string.IsNullOrWhiteSpace(metadataPkgName))
685+
// Check for package metadata
686+
var pkgMetadataJToken = annotations["metadata"];
687+
if (pkgMetadataJToken == null)
677688
{
678-
exception = new InvalidOrEmptyResponse($"Response element 'org.opencontainers.image.title' is empty for package '{packageName}' in '{Repository.Name}'.");
689+
exception = new InvalidOrEmptyResponse($"Response does not contain 'metadata' element in manifest for package '{packageName}' in '{Repository.Name}'.");
679690

680-
return emptyTuple;
691+
return serverPkgInfo;
681692
}
693+
var metadata = pkgMetadataJToken.ToString();
694+
695+
// Check for package artifact type
696+
var resourceTypeJToken = annotations["resourceType"];
697+
var resourceType = resourceTypeJToken != null ? resourceTypeJToken.ToString() : string.Empty;
682698

683-
return new Tuple<string, string>(metadataPkgName, metadata);
699+
return new ContainerRegistryInfo(metadataPkgName, metadata, resourceType);
684700
}
685701

686702
internal JObject FindAcrManifest(string registry, string packageName, string version, string acrAccessToken, out ErrorRecord errRecord)
@@ -1026,7 +1042,7 @@ internal bool PushNupkgACR(string psd1OrPs1File, string outputNupkgDir, string p
10261042
}
10271043

10281044
// Create and upload manifest
1029-
TryCreateAndUploadManifest(fullNupkgFile, nupkgDigest, configDigest, pkgName, resourceType, metadataJson, configFilePath,
1045+
TryCreateAndUploadManifest(fullNupkgFile, nupkgDigest, configDigest, pkgName, resourceType, metadataJson, configFilePath,
10301046
pkgNameLower, pkgVersion, acrAccessToken);
10311047

10321048
return true;
@@ -1172,7 +1188,7 @@ private string CreateManifestContent(
11721188
jsonWriter.WriteValue(fileName);
11731189
jsonWriter.WritePropertyName("metadata");
11741190
jsonWriter.WriteValue(metadata);
1175-
jsonWriter.WritePropertyName("artifactType");
1191+
jsonWriter.WritePropertyName("resourceType");
11761192
jsonWriter.WriteValue(resourceType.ToString());
11771193
jsonWriter.WriteEndObject(); // end of annotations object
11781194

@@ -1295,7 +1311,7 @@ private Hashtable[] FindPackagesWithVersionHelper(string packageName, VersionTyp
12951311

12961312
var pkgsInDescendingOrder = sortedQualifyingPkgs.Reverse();
12971313

1298-
foreach(var pkgVersionTag in pkgsInDescendingOrder)
1314+
foreach (var pkgVersionTag in pkgsInDescendingOrder)
12991315
{
13001316
string exactTagVersion = pkgVersionTag.Value.ToString();
13011317
Hashtable metadata = GetACRMetadata(Registry, packageNameLowercase, exactTagVersion, acrAccessToken, out errRecord);
@@ -1310,7 +1326,7 @@ private Hashtable[] FindPackagesWithVersionHelper(string packageName, VersionTyp
13101326
// getOnlyLatest will be true for FindName(), as only the latest criteria satisfying version should be returned
13111327
break;
13121328
}
1313-
}
1329+
}
13141330

13151331
return latestVersionResponse.ToArray();
13161332
}

src/code/ContainerRegistryInfo.cs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Collections;
6+
7+
namespace Microsoft.PowerShell.PSResourceGet.UtilClasses
8+
{
9+
10+
public sealed class ContainerRegistryInfo
11+
{
12+
#region Properties
13+
14+
public string Name { get; }
15+
public string Metadata { get; }
16+
public ResourceType ResourceType { get; }
17+
18+
#endregion
19+
20+
21+
#region Constructors
22+
23+
internal ContainerRegistryInfo(string name, string metadata, string resourceType)
24+
25+
{
26+
Name = name ?? string.Empty;
27+
Metadata = metadata ?? string.Empty;
28+
ResourceType = string.IsNullOrWhiteSpace(resourceType) ? ResourceType.None :
29+
(ResourceType)Enum.Parse(typeof(ResourceType), resourceType, ignoreCase: true);
30+
}
31+
32+
#endregion
33+
34+
#region Methods
35+
36+
internal Hashtable ToHashtable()
37+
{
38+
Hashtable hashtable = new Hashtable
39+
{
40+
{ "Name", Name },
41+
{ "Metadata", Metadata },
42+
{ "ResourceType", ResourceType }
43+
};
44+
45+
return hashtable;
46+
}
47+
48+
#endregion
49+
}
50+
}

src/code/PSResourceInfo.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,7 @@ public static bool TryConvertFromJson(
810810
public static bool TryConvertFromACRJson(
811811
string packageName,
812812
JsonDocument packageMetadata,
813+
ResourceType? resourceType,
813814
out PSResourceInfo psGetInfo,
814815
PSRepositoryInfo repository,
815816
out string errorMsg)
@@ -973,7 +974,7 @@ public static bool TryConvertFromACRJson(
973974
{
974975
{ "NormalizedVersion", metadata["NormalizedVersion"].ToString() }
975976
};
976-
977+
977978
psGetInfo = new PSResourceInfo(
978979
additionalMetadata: additionalMetadataHashtable,
979980
author: metadata["Authors"] as String,
@@ -996,7 +997,7 @@ public static bool TryConvertFromACRJson(
996997
repository: repository.Name,
997998
repositorySourceLocation: repository.Uri.ToString(),
998999
tags: metadata["Tags"] as string[],
999-
type: ResourceType.None,
1000+
type: resourceType ?? ResourceType.None,
10001001
updatedDate: null,
10011002
version: metadata["Version"] as Version);
10021003

test/FindPSResourceTests/FindPSResourceACRServer.Tests.ps1

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,4 +192,22 @@ Describe 'Test HTTP Find-PSResource for ACR Server Protocol' -tags 'CI' {
192192
$res.Version | Should -Be "3.5.0"
193193
$res.Prerelease | Should -Be "alpha"
194194
}
195+
196+
It "Should find and return correct resource type - module" {
197+
$moduleName = "test_dependency_mod"
198+
$res = Find-PSResource -Name $moduleName -Repository $ACRRepoName
199+
$res | Should -Not -BeNullOrEmpty
200+
$res.Name | Should -BeExactly $moduleName
201+
$res.Version | Should -Be "1.0.0"
202+
$res.Type.ToString() | Should -Be "Module"
203+
}
204+
205+
It "Should find and return correct resource type - script" {
206+
$scriptName = "test-script"
207+
$res = Find-PSResource -Name $scriptName -Repository $ACRRepoName
208+
$res | Should -Not -BeNullOrEmpty
209+
$res.Name | Should -BeExactly $scriptName
210+
$res.Version | Should -Be "1.0.0"
211+
$res.Type.ToString() | Should -Be "Script"
212+
}
195213
}

0 commit comments

Comments
 (0)