Skip to content

Commit c4c1ec3

Browse files
Fix the behavior of Get-GitHubRepository (microsoft#179)
There were a few problems with this function: * -GetAllPublicRepositories didn't actually work. That's now been fixed, and support for the optional Since paramater has been added as well. * Fixed the ParameterSet handling for all of the parameters to make sure that users can only specify parameters relevant with the appropriate parameter set given that there are five different use cases for this method: * Getting repos for the current authenticated user * Getting repos for any GitHub user * Getting repos for an organization * Getting all public repos) * Getting a specific GitHub repo Resolves microsoft#178
1 parent d279f1c commit c4c1ec3

File tree

3 files changed

+245
-33
lines changed

3 files changed

+245
-33
lines changed

GitHubCore.ps1

+6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
mediaTypeVersion = 'v3'
77
squirrelAcceptHeader = 'application/vnd.github.squirrel-girl-preview'
88
symmetraAcceptHeader = 'application/vnd.github.symmetra-preview+json'
9+
mercyAcceptHeader = 'application/vnd.github.mercy-preview+json'
10+
nebulaAcceptHeader = 'application/vnd.github.nebula-preview+json'
11+
baptisteAcceptHeader = 'application/vnd.github.baptiste-preview+json'
12+
scarletWitchAcceptHeader = 'application/vnd.github.scarlet-witch-preview+json'
13+
dorianAcceptHeader = 'application/vnd.github.dorian-preview+json'
14+
londonAcceptHeader = 'application/vnd.github.london-preview+json'
915

1016
}.GetEnumerator() | ForEach-Object {
1117
Set-Variable -Scope Script -Option ReadOnly -Name $_.Key -Value $_.Value

GitHubRepositories.ps1

+138-31
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,12 @@ function Get-GitHubRepository
317317
.PARAMETER GetAllPublicRepositories
318318
If this is specified with no other parameter, then instead of returning back all
319319
repositories for the current authenticated user, it will instead return back all
320-
public repositories on GitHub.
320+
public repositories on GitHub in the order in which they were created.
321+
322+
.PARAMETER Since
323+
The ID of the last public repository that you have seen. If specified with
324+
-GetAllPublicRepositories, will only return back public repositories created _after_ this
325+
one.
321326
322327
.PARAMETER AccessToken
323328
If provided, this will be used as the AccessToken for authentication with the
@@ -340,24 +345,30 @@ function Get-GitHubRepository
340345
Gets all public repositories on GitHub.
341346
342347
.EXAMPLE
343-
Get-GitHubRepository -OctoCat OctoCat
348+
Get-GitHubRepository -OwnerName octocat
349+
350+
Gets all of the repositories for the user octocat
344351
345352
.EXAMPLE
346-
Get-GitHubRepository -Uri https://github.com/PowerShell/PowerShellForGitHub
353+
Get-GitHubRepository -Uri https://github.com/microsoft/PowerShellForGitHub
354+
355+
Gets information about the microsoft/PowerShellForGitHub repository.
347356
348357
.EXAMPLE
349358
Get-GitHubRepository -OrganizationName PowerShell
350359
360+
Gets all of the repositories in the PowerShell organization.
351361
#>
352362
[CmdletBinding(
353363
SupportsShouldProcess,
354-
DefaultParameterSetName='Elements')]
364+
DefaultParameterSetName='AuthenticatedUser')]
355365
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "", Justification="Methods called within here make use of PSShouldProcess, and the switch is passed on to them inherently.")]
366+
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "", Justification="One or more parameters (like NoStatus) are only referenced by helper methods which get access to it from the stack via Get-Variable -Scope 1.")]
356367
param(
357-
[Parameter(ParameterSetName='Elements')]
368+
[Parameter(ParameterSetName='ElementsOrUser')]
358369
[string] $OwnerName,
359370

360-
[Parameter(ParameterSetName='Elements')]
371+
[Parameter(ParameterSetName='ElementsOrUser')]
361372
[string] $RepositoryName,
362373

363374
[Parameter(
@@ -369,21 +380,36 @@ function Get-GitHubRepository
369380
[string] $OrganizationName,
370381

371382
[ValidateSet('All', 'Public', 'Private')]
383+
[Parameter(ParameterSetName='AuthenticatedUser')]
372384
[string] $Visibility,
373385

386+
[Parameter(ParameterSetName='AuthenticatedUser')]
374387
[string[]] $Affiliation,
375388

389+
[Parameter(ParameterSetName='AuthenticatedUser')]
390+
[Parameter(ParameterSetName='ElementsOrUser')]
391+
[Parameter(ParameterSetName='Organization')]
376392
[ValidateSet('All', 'Owner', 'Public', 'Private', 'Member', 'Forks', 'Sources')]
377393
[string] $Type,
378394

395+
[Parameter(ParameterSetName='AuthenticatedUser')]
396+
[Parameter(ParameterSetName='ElementsOrUser')]
397+
[Parameter(ParameterSetName='Organization')]
379398
[ValidateSet('Created', 'Updated', 'Pushed', 'FullName')]
380399
[string] $Sort,
381400

401+
[Parameter(ParameterSetName='AuthenticatedUser')]
402+
[Parameter(ParameterSetName='ElementsOrUser')]
403+
[Parameter(ParameterSetName='Organization')]
382404
[ValidateSet('Ascending', 'Descending')]
383405
[string] $Direction,
384406

407+
[Parameter(ParameterSetName='PublicRepos')]
385408
[switch] $GetAllPublicRepositories,
386409

410+
[Parameter(ParameterSetName='PublicRepos')]
411+
[int64] $Since,
412+
387413
[string] $AccessToken,
388414

389415
[switch] $NoStatus
@@ -395,36 +421,115 @@ function Get-GitHubRepository
395421
$OwnerName = $elements.ownerName
396422
$RepositoryName = $elements.repositoryName
397423

398-
$telemetryProperties = @{}
424+
$telemetryProperties = @{
425+
'UsageType' = $PSCmdlet.ParameterSetName
426+
}
399427

400428
$uriFragment = [String]::Empty
401429
$description = [String]::Empty
402-
if ((-not [String]::IsNullOrEmpty($OwnerName)) -and (-not [String]::IsNullOrEmpty($RepositoryName)))
430+
switch ($PSCmdlet.ParameterSetName)
403431
{
404-
$telemetryProperties['OwnerName'] = Get-PiiSafeString -PlainText $OwnerName
405-
$telemetryProperties['RepositoryName'] = Get-PiiSafeString -PlainText $RepositoryName
432+
{ ('ElementsOrUser', 'Uri') -contains $_ } {
433+
# This is a little tricky. Ideally we'd have two separate ParameterSets (Elements, User),
434+
# however PowerShell would be unable to disambiguate between the two, so unfortunately
435+
# we need to do some additional work here. And because fallthru doesn't appear to be
436+
# working right, we're combining both of those, along with Uri.
437+
438+
if ([String]::IsNullOrWhiteSpace($OwnerName))
439+
{
440+
$message = 'OwnerName could not be determined.'
441+
Write-Log -Message $message -Level Error
442+
throw $message
443+
}
444+
elseif ([String]::IsNullOrWhiteSpace($RepositoryName))
445+
{
446+
if ($PSCmdlet.ParameterSetName -eq 'ElementsOrUser')
447+
{
448+
$telemetryProperties['UsageType'] = 'User'
449+
$telemetryProperties['OwnerName'] = Get-PiiSafeString -PlainText $OwnerName
450+
451+
$uriFragment = "users/$OwnerName/repos"
452+
$description = "Getting repos for $OwnerName"
453+
}
454+
else
455+
{
456+
$message = 'RepositoryName could not be determined.'
457+
Write-Log -Message $message -Level Error
458+
throw $message
459+
}
460+
}
461+
else
462+
{
463+
if ($PSCmdlet.ParameterSetName -eq 'ElementsOrUser')
464+
{
465+
$telemetryProperties['UsageType'] = 'Elements'
466+
467+
if ($PSBoundParameters.ContainsKey('Type') -or
468+
$PSBoundParameters.ContainsKey('Sort') -or
469+
$PSBoundParameters.ContainsKey('Direction'))
470+
{
471+
$message = 'Unable to specify -Type, -Sort and/or -Direction when retrieving a specific repository.'
472+
Write-Log -Message $message -Level Error
473+
throw $message
474+
}
475+
}
476+
477+
$telemetryProperties['OwnerName'] = Get-PiiSafeString -PlainText $OwnerName
478+
$telemetryProperties['RepositoryName'] = Get-PiiSafeString -PlainText $RepositoryName
479+
480+
$uriFragment = "repos/$OwnerName/$RepositoryName"
481+
$description = "Getting $OwnerName/$RepositoryName"
482+
}
406483

407-
$uriFragment = "repos/$OwnerName/$RepositoryName"
408-
$description = "Getting repo $RepositoryName"
409-
}
410-
elseif ([String]::IsNullOrEmpty($OwnerName) -and [String]::IsNullOrEmpty($OrganizationName))
411-
{
412-
$uriFragment = 'user/repos'
413-
$description = 'Getting repos for current authenticated user'
414-
}
415-
elseif ([String]::IsNullOrEmpty($OwnerName))
416-
{
417-
$telemetryProperties['OrganizationName'] = Get-PiiSafeString -PlainText $OrganizationName
484+
break
485+
}
418486

419-
$uriFragment = "orgs/$OrganizationName/repos"
420-
$description = "Getting repos for $OrganizationName"
421-
}
422-
else
423-
{
424-
$telemetryProperties['OwnerName'] = Get-PiiSafeString -PlainText $OwnerName
487+
'Organization' {
488+
489+
$telemetryProperties['OrganizationName'] = Get-PiiSafeString -PlainText $OrganizationName
490+
491+
$uriFragment = "orgs/$OrganizationName/repos"
492+
$description = "Getting repos for $OrganizationName"
493+
494+
break
495+
}
496+
497+
'User' {
498+
$telemetryProperties['OwnerName'] = Get-PiiSafeString -PlainText $OwnerName
499+
500+
$uriFragment = "users/$OwnerName/repos"
501+
$description = "Getting repos for $OwnerName"
502+
503+
break
504+
}
505+
506+
'AuthenticatedUser' {
507+
if ($PSBoundParameters.ContainsKey('Type') -and
508+
($PSBoundParameters.ContainsKey('Visibility') -or
509+
$PSBoundParameters.ContainsKey('Affiliation')))
510+
{
511+
$message = 'Unable to specify -Type when using -Visibility and/or -Affiliation.'
512+
Write-Log -Message $message -Level Error
513+
throw $message
514+
}
515+
516+
$uriFragment = 'user/repos'
517+
$description = 'Getting repos for current authenticated user'
518+
519+
break
520+
}
521+
522+
'PublicRepos' {
523+
$uriFragment = 'repositories'
524+
$description = "Getting all public repositories"
425525

426-
$uriFragment = "users/$OwnerName/repos"
427-
$description = "Getting repos for $OwnerName"
526+
if ($PSBoundParameters.ContainsKey('Since'))
527+
{
528+
$description += " since $Since"
529+
}
530+
531+
break
532+
}
428533
}
429534

430535
$sortConverter = @{
@@ -448,10 +553,12 @@ function Get-GitHubRepository
448553
{
449554
$getParams += "affiliation=$($Affiliation -join ',')"
450555
}
556+
if ($PSBoundParameters.ContainsKey('Since')) { $getParams += "since=$Since" }
451557

452558
$params = @{
453559
'UriFragment' = $uriFragment + '?' + ($getParams -join '&')
454560
'Description' = $description
561+
'AcceptHeader' = "$script:nebulaAcceptHeader,$script:baptisteAcceptHeader,$script:mercyAcceptHeader"
455562
'AccessToken' = $AccessToken
456563
'TelemetryEventName' = $MyInvocation.MyCommand.Name
457564
'TelemetryProperties' = $telemetryProperties
@@ -810,7 +917,7 @@ function Get-GitHubRepositoryTopic
810917
'UriFragment' = "repos/$OwnerName/$RepositoryName/topics"
811918
'Method' = 'Get'
812919
'Description' = "Getting topics for $RepositoryName"
813-
'AcceptHeader' = 'application/vnd.github.mercy-preview+json'
920+
'AcceptHeader' = $script:mercyAcceptHeader
814921
'AccessToken' = $AccessToken
815922
'TelemetryEventName' = $MyInvocation.MyCommand.Name
816923
'TelemetryProperties' = $telemetryProperties
@@ -933,7 +1040,7 @@ function Set-GitHubRepositoryTopic
9331040
'Body' = (ConvertTo-Json -InputObject $hashBody)
9341041
'Method' = 'Put'
9351042
'Description' = $description
936-
'AcceptHeader' = 'application/vnd.github.mercy-preview+json'
1043+
'AcceptHeader' = $script:mercyAcceptHeader
9371044
'AccessToken' = $AccessToken
9381045
'TelemetryEventName' = $MyInvocation.MyCommand.Name
9391046
'TelemetryProperties' = $telemetryProperties

Tests/GitHubRepositories.tests.ps1

+101-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,107 @@ $moduleRootPath = Split-Path -Path $PSScriptRoot -Parent
1414

1515
try
1616
{
17-
Describe 'Modifying repositories' {
17+
Describe 'Getting repositories' {
18+
Context 'For authenticated user' {
19+
BeforeAll -Scriptblock {
20+
$publicRepo = New-GitHubRepository -RepositoryName ([Guid]::NewGuid().Guid) -AutoInit
21+
$privateRepo = New-GitHubRepository -RepositoryName ([Guid]::NewGuid().Guid) -AutoInit -Private
22+
23+
# Avoid PSScriptAnalyzer PSUseDeclaredVarsMoreThanAssignments
24+
$publicRepo = $publicRepo
25+
$privateRepo = $privateRepo
26+
}
27+
28+
$publicRepos = Get-GitHubRepository -Visibility Public
29+
$privateRepos = Get-GitHubRepository -Visibility Private
30+
31+
It "Should have the public repo" {
32+
$publicRepo.Name | Should BeIn $publicRepos.Name
33+
$publicRepo.Name | Should Not BeIn $privateRepos.Name
34+
}
35+
36+
It "Should have the private repo" {
37+
$privateRepo.Name | Should BeIn $privateRepos.Name
38+
$privateRepo.Name | Should Not BeIn $publicRepos.Name
39+
}
40+
41+
It 'Should not permit bad combination of parameters' {
42+
{ Get-GitHubRepository -Type All -Visibility All } | Should Throw
43+
{ Get-GitHubRepository -Type All -Affiliation Owner } | Should Throw
44+
}
45+
46+
AfterAll -ScriptBlock {
47+
Remove-GitHubRepository -Uri $publicRepo.svn_url
48+
Remove-GitHubRepository -Uri $privateRepo.svn_url
49+
}
50+
}
51+
52+
Context 'For any user' {
53+
$repos = Get-GitHubRepository -OwnerName 'octocat' -Type Public
54+
55+
It "Should have results for The Octocat" {
56+
$repos.Count | Should -BeGreaterThan 0
57+
$repos[0].owner.login | Should Be 'octocat'
58+
}
59+
}
60+
61+
Context 'For organizations' {
62+
BeforeAll -Scriptblock {
63+
$repo = New-GitHubRepository -OrganizationName $script:organizationName -RepositoryName ([Guid]::NewGuid().Guid) -AutoInit
64+
65+
# Avoid PSScriptAnalyzer PSUseDeclaredVarsMoreThanAssignments
66+
$repo = $repo
67+
}
68+
69+
$repos = Get-GitHubRepository -OrganizationName $script:organizationName -Type All
70+
It "Should have results for the organization" {
71+
$repo.name | Should BeIn $repos.name
72+
}
1873

74+
AfterAll -ScriptBlock {
75+
Remove-GitHubRepository -Uri $repo.svn_url
76+
}
77+
}
78+
79+
Context 'For public repos' {
80+
# Skipping these tests for now, as it would run for a _very_ long time.
81+
# No obviously good way to verify this.
82+
}
83+
84+
Context 'For a specific repo' {
85+
BeforeAll -Scriptblock {
86+
$repo = New-GitHubRepository -RepositoryName ([Guid]::NewGuid().Guid) -AutoInit
87+
88+
# Avoid PSScriptAnalyzer PSUseDeclaredVarsMoreThanAssignments
89+
$repo = $repo
90+
}
91+
92+
$returned = Get-GitHubRepository -Uri $repo.svn_url
93+
It "Should be a single result using Uri ParameterSet" {
94+
$returned | Should -BeOfType PSCustomObject
95+
}
96+
97+
$returned = Get-GitHubRepository -OwnerName $repo.owner.login -RepositoryName $repo.Name
98+
It "Should be a single result using Elements ParameterSet" {
99+
$returned | Should -BeOfType PSCustomObject
100+
}
101+
102+
It 'Should not permit additional parameters' {
103+
{ Get-GitHubRepository -OwnerName $repo.owner.login -RepositoryName $repo.Name -Type All } | Should Throw
104+
}
105+
106+
It 'Should require both OwnerName and RepositoryName' {
107+
{ Get-GitHubRepository -RepositoryName $repo.Name } | Should Throw
108+
{ Get-GitHubRepository -Uri "https://github.com/$script:ownerName" } | Should Throw
109+
}
110+
111+
AfterAll -ScriptBlock {
112+
Remove-GitHubRepository -Uri $repo.svn_url
113+
}
114+
}
115+
}
116+
117+
Describe 'Modifying repositories' {
19118
Context -Name 'For renaming a repository' -Fixture {
20119
BeforeEach -Scriptblock {
21120
$repo = New-GitHubRepository -RepositoryName ([Guid]::NewGuid().Guid) -AutoInit
@@ -35,7 +134,7 @@ try
35134
## cleanup temp testing repository
36135
AfterEach -Scriptblock {
37136
## variables from BeforeEach scriptblock are accessible here, but not variables from It scriptblocks, so need to make URI (instead of being able to use $renamedRepo variable from It scriptblock)
38-
Remove-GitHubRepository -Uri "$($repo.svn_url)$suffixToAddToRepo" -Verbose
137+
Remove-GitHubRepository -Uri "$($repo.svn_url)$suffixToAddToRepo"
39138
}
40139
}
41140
}

0 commit comments

Comments
 (0)