Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for AWS CodeArtifact #1660

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 63 additions & 29 deletions src/code/V3ServerAPICalls.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ internal class V3ServerAPICalls : ServerApiCall
private bool _isJFrogRepo { get; set; }
private bool _isGHPkgsRepo { get; set; }
private bool _isMyGetRepo { get; set; }
private bool _isAWSCodeArtifactRepo { get; set; }
public FindResponseType v3FindResponseType = FindResponseType.ResponseString;
private static readonly Hashtable[] emptyHashResponses = new Hashtable[]{};
private static readonly string nugetRepoUri = "https://api.nuget.org/v3/index.json";
Expand Down Expand Up @@ -55,7 +56,7 @@ public V3ServerAPICalls(PSRepositoryInfo repository, PSCmdlet cmdletPassedIn, Ne
handler.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
bool token = false;

if(networkCredential != null)
if(networkCredential != null)
{
token = String.Equals("token", networkCredential.UserName) ? true : false;
};
Expand All @@ -71,7 +72,7 @@ public V3ServerAPICalls(PSRepositoryInfo repository, PSCmdlet cmdletPassedIn, Ne
} else {

handler.Credentials = networkCredential;

_sessionClient = new HttpClient(handler);
};

Expand All @@ -80,9 +81,10 @@ public V3ServerAPICalls(PSRepositoryInfo repository, PSCmdlet cmdletPassedIn, Ne
_sessionClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", userAgentString);

_isNuGetRepo = String.Equals(Repository.Uri.AbsoluteUri, nugetRepoUri, StringComparison.InvariantCultureIgnoreCase);
_isJFrogRepo = Repository.Uri.AbsoluteUri.ToLower().Contains("jfrog.io");
_isGHPkgsRepo = Repository.Uri.AbsoluteUri.ToLower().Contains("pkg.github.com");
_isMyGetRepo = Repository.Uri.AbsoluteUri.ToLower().Contains("myget.org");
_isJFrogRepo = RepositoryUriContains("jfrog.io");
_isGHPkgsRepo = RepositoryUriContains("pkg.github.com");
_isMyGetRepo = RepositoryUriContains("myget.org");
_isAWSCodeArtifactRepo = RepositoryUriContains(".codeartifact.") && RepositoryUriContains(".amazonaws.com");
}

#endregion
Expand All @@ -91,18 +93,37 @@ public V3ServerAPICalls(PSRepositoryInfo repository, PSCmdlet cmdletPassedIn, Ne

/// <summary>
/// Find method which allows for searching for all packages from a repository and returns latest version for each.
/// Not supported for V3 repository.
/// Supported for AWS CodeArtifact V3 repository only.
/// </summary>
public override FindResults FindAll(bool includePrerelease, ResourceType type, out ErrorRecord errRecord)
{
_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::FindAll()");
errRecord = new ErrorRecord(
new InvalidOperationException($"Find all is not supported for the V3 server protocol repository '{Repository.Name}'"),
"FindAllFailure",
ErrorCategory.InvalidOperation,
this);

return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v3FindResponseType);
if (!_isAWSCodeArtifactRepo)
{
errRecord = new ErrorRecord(
new InvalidOperationException($"Find all is not supported for the V3 server protocol repository '{Repository.Name}'"),
"FindAllFailure",
ErrorCategory.InvalidOperation,
this);

return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v3FindResponseType);
}

var queryTerm = "";
var matchingPkgEntries = GetVersionedPackageEntriesFromSearchQueryResource(queryTerm, includePrerelease, out errRecord);
if (errRecord != null)
{
return new FindResults(stringResponse: Utils.EmptyStrArray, hashtableResponse: emptyHashResponses, responseType: v3FindResponseType);
}

List<string> matchingResponses = new List<string>();
foreach (var pkgEntry in matchingPkgEntries)
{
matchingResponses.Add(pkgEntry.ToString());
}

return new FindResults(stringResponse: matchingResponses.ToArray(), hashtableResponse: emptyHashResponses, responseType: v3FindResponseType);
}

/// <summary>
Expand Down Expand Up @@ -175,7 +196,7 @@ public override FindResults FindNameWithTag(string packageName, string[] tags, b
public override FindResults FindNameGlobbing(string packageName, bool includePrerelease, ResourceType type, out ErrorRecord errRecord)
{
_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::FindNameGlobbing()");
if (_isNuGetRepo || _isJFrogRepo || _isGHPkgsRepo || _isMyGetRepo)
if (_isNuGetRepo || _isJFrogRepo || _isGHPkgsRepo || _isMyGetRepo || _isAWSCodeArtifactRepo)
{
return FindNameGlobbingFromNuGetRepo(packageName, tags: Utils.EmptyStrArray, includePrerelease, out errRecord);
}
Expand All @@ -198,7 +219,7 @@ public override FindResults FindNameGlobbing(string packageName, bool includePre
public override FindResults FindNameGlobbingWithTag(string packageName, string[] tags, bool includePrerelease, ResourceType type, out ErrorRecord errRecord)
{
_cmdletPassedIn.WriteDebug("In V3ServerAPICalls::FindNameGlobbingWithTag()");
if (_isNuGetRepo || _isJFrogRepo || _isGHPkgsRepo || _isMyGetRepo)
if (_isNuGetRepo || _isJFrogRepo || _isGHPkgsRepo || _isMyGetRepo || _isAWSCodeArtifactRepo)
{
return FindNameGlobbingFromNuGetRepo(packageName, tags, includePrerelease, out errRecord);
}
Expand Down Expand Up @@ -343,7 +364,7 @@ private FindResults FindNameGlobbingFromNuGetRepo(string packageName, string[] t
var names = packageName.Split(new char[] { '*' }, StringSplitOptions.RemoveEmptyEntries);
string querySearchTerm;

if (names.Length == 0)
if (names.Length == 0 && !_isAWSCodeArtifactRepo)
{
errRecord = new ErrorRecord(
new ArgumentException("-Name '*' for V3 server protocol repositories is not supported"),
Expand Down Expand Up @@ -834,7 +855,7 @@ private string[] GetVersionedPackageEntriesFromRegistrationsResource(string pack
/// <summary>
/// Gets the versioned package entries from SearchQueryService resource
/// i.e when the package Name being searched for contains wildcards or a Tag query search is performed
/// This is called by FindNameGlobbingFromNuGetRepo() and FindTagsFromNuGetRepo()
/// This is called by FindAll(), FindNameGlobbingFromNuGetRepo() and FindTagsFromNuGetRepo()
/// </summary>
private List<JsonElement> GetVersionedPackageEntriesFromSearchQueryResource(string queryTerm, bool includePrerelease, out ErrorRecord errRecord)
{
Expand All @@ -852,22 +873,27 @@ private List<JsonElement> GetVersionedPackageEntriesFromSearchQueryResource(stri
return pkgEntries;
}

// Get initial response
int skip = 0;
string query = $"{searchQueryServiceUrl}?q={queryTerm}&prerelease={includePrerelease}&semVerLevel=2.0.0&skip={skip}&take=100";
int skipAndTakeAmount = 100;
if (_isAWSCodeArtifactRepo) {
skipAndTakeAmount = 25;
}

// Get responses for all packages that contain the required tags
pkgEntries.AddRange(GetJsonElementArr(query, dataName, out int initialCount, out errRecord).ToList());
// Get initial response
string query = $"{searchQueryServiceUrl}?q={queryTerm}&prerelease={includePrerelease}&semVerLevel=2.0.0&skip={skip}&take={skipAndTakeAmount}";

// check count (ie "totalHits") 425 ==> count/100 ~~> 4 calls ~~> + 1 = 5 calls
int count = initialCount / 100 + 1;
// if more than 100 count, loop and add response to list
while (count > 0)
{
skip += 100;
query = $"{searchQueryServiceUrl}?q={queryTerm}&prerelease={includePrerelease}&semVerLevel=2.0.0&skip={skip}&take=100";
pkgEntries.AddRange(GetJsonElementArr(query, dataName, out int unneededCount, out errRecord).ToList());
count--;
var itemList = GetJsonElementArr(query, dataName, out int initialCount, out errRecord).ToList();
pkgEntries.AddRange(itemList);

// Get responses for all packages that contain the required tags
while (itemList.Count > 0) {
skip += skipAndTakeAmount;
query = $"{searchQueryServiceUrl}?q={queryTerm}&prerelease={includePrerelease}&semVerLevel=2.0.0&skip={skip}&take={skipAndTakeAmount}";
itemList = GetJsonElementArr(query, dataName, out int unneededCount, out errRecord).ToList();
if (itemList.Count == 0) {
break;
}
pkgEntries.AddRange(itemList);
}

return pkgEntries;
Expand Down Expand Up @@ -1716,6 +1742,14 @@ private static async Task<HttpContent> SendV3RequestForContentAsync(HttpRequestM
}
}


/// <summary>
/// Helper method called by FindAll() to validate whether the repositories AbsoluteUri contains a specified value.
/// <summary>
private bool RepositoryUriContains(string str) {
return Repository.Uri.AbsoluteUri.ToLower().Contains(str);
}

#endregion
}
}
14 changes: 13 additions & 1 deletion test/FindPSResourceTests/FindPSResourceV3Server.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Write-Verbose -Verbose "Current module search paths: $psmodulePaths"
Describe 'Test HTTP Find-PSResource for V3 Server Protocol' -tags 'CI' {

BeforeAll{
$AWSCodeArtifactGalleryName = Get-AWSCodeArtifactGalleryName
alerickson marked this conversation as resolved.
Show resolved Hide resolved
$AWSCodeArtifactGalleryLocation = Get-AWSCodeArtifactGalleryLocation
$NuGetGalleryName = Get-NuGetGalleryName
$testModuleName = "test_module"
Get-NewPSResourceRepositoryFile
Expand Down Expand Up @@ -269,13 +271,23 @@ Describe 'Test HTTP Find-PSResource for V3 Server Protocol' -tags 'CI' {
$err[0].FullyQualifiedErrorId | Should -BeExactly "FindAllFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource"
}

It "should support AWS CodeArtifact with find all resources given Name '*'" {
Register-PSResourceRepository -Name $AWSCodeArtifactGalleryName -Uri $AWSCodeArtifactGalleryLocation
alerickson marked this conversation as resolved.
Show resolved Hide resolved
$res = Find-PSResource -Name "*" -Repository $AWSCodeArtifactGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
alerickson marked this conversation as resolved.
Show resolved Hide resolved
Unregister-PSResourceRepository -Name $AWSCodeArtifactGalleryName
$res | Should -BeNullOrEmpty
$err.Count | Should -BeGreaterThan 0
# The `HttpRequestCallFailure` error indicates the cmdlet moved past the fast-fail for `-Name '*'` not supported
$err[0].FullyQualifiedErrorId | Should -BeExactly "HttpRequestCallFailure,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource"
}

It "should not find an unlisted module" {
$res = Find-PSResource -Name "PMTestDependency1" -Repository $NuGetGalleryName -ErrorVariable err -ErrorAction SilentlyContinue
$res | Should -BeNullOrEmpty
$err.Count | Should -BeGreaterThan 0
$err[0].FullyQualifiedErrorId | Should -BeExactly "PackageNotFound,Microsoft.PowerShell.PSResourceGet.Cmdlets.FindPSResource"
}

# "carb*" is intentionally chosen as a sequence that will trigger pagination (ie more than 100 results),
# but is not too time consuming.
# There are currently between 236 packages that should be returned
Expand Down
13 changes: 13 additions & 0 deletions test/PSGetTestUtils.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ $script:PSGalleryLocation = 'https://www.powershellgallery.com/api/v2'
$script:NuGetGalleryName = 'NuGetGallery'
$script:NuGetGalleryLocation = 'https://api.nuget.org/v3/index.json'

# This is not a valid Code Artifact endpoint. However, this will allow a unit test to move
# past the NuGet v3 fast fail in the FindAll() method.
$script:AWSCodeArtifactName = 'AWSCodeArtifact'
$script:AWSCodeArtifactLocation = 'https://cadomainname-awsaccountid.d.codeartifact.region.amazonaws.com/nuget/carepositoryname/v3/index.json'

if($script:IsInbox)
{
$script:ProgramFilesPSPath = Microsoft.PowerShell.Management\Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell"
Expand Down Expand Up @@ -135,6 +140,14 @@ function Get-PSGetLocalAppDataPath {
return $script:PSGetAppLocalPath
}

function Get-AWSCodeArtifactGalleryName {
return $script:AWSCodeArtifactName
}

function Get-AWSCodeArtifactGalleryLocation {
return $script:AWSCodeArtifactLocation
}

function Get-NuGetGalleryName
{
return $script:NuGetGalleryName
Expand Down
Loading