Skip to content

Commit 30a1414

Browse files
committed
Fix the behavior of Get-GitHubRepository
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 30a1414

File tree

3 files changed

+231
-33
lines changed

3 files changed

+231
-33
lines changed

GitHubCore.ps1

Lines changed: 6 additions & 0 deletions
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

Lines changed: 129 additions & 31 deletions
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,106 @@ 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"
425502

426-
$uriFragment = "users/$OwnerName/repos"
427-
$description = "Getting repos for $OwnerName"
503+
break
504+
}
505+
506+
'AuthenticatedUser' {
507+
$uriFragment = 'user/repos'
508+
$description = 'Getting repos for current authenticated user'
509+
510+
break
511+
}
512+
513+
'PublicRepos' {
514+
$uriFragment = 'repositories'
515+
$description = "Getting all public repositories"
516+
517+
if ($PSBoundParameters.ContainsKey('Since'))
518+
{
519+
$description += " since $Since"
520+
}
521+
522+
break
523+
}
428524
}
429525

430526
$sortConverter = @{
@@ -448,10 +544,12 @@ function Get-GitHubRepository
448544
{
449545
$getParams += "affiliation=$($Affiliation -join ',')"
450546
}
547+
if ($PSBoundParameters.ContainsKey('Since')) { $getParams += "since=$Since" }
451548

452549
$params = @{
453550
'UriFragment' = $uriFragment + '?' + ($getParams -join '&')
454551
'Description' = $description
552+
'AcceptHeader' = "$script:nebulaAcceptHeader,$script:baptisteAcceptHeader,$script:mercyAcceptHeader"
455553
'AccessToken' = $AccessToken
456554
'TelemetryEventName' = $MyInvocation.MyCommand.Name
457555
'TelemetryProperties' = $telemetryProperties
@@ -810,7 +908,7 @@ function Get-GitHubRepositoryTopic
810908
'UriFragment' = "repos/$OwnerName/$RepositoryName/topics"
811909
'Method' = 'Get'
812910
'Description' = "Getting topics for $RepositoryName"
813-
'AcceptHeader' = 'application/vnd.github.mercy-preview+json'
911+
'AcceptHeader' = $script:mercyAcceptHeader
814912
'AccessToken' = $AccessToken
815913
'TelemetryEventName' = $MyInvocation.MyCommand.Name
816914
'TelemetryProperties' = $telemetryProperties
@@ -933,7 +1031,7 @@ function Set-GitHubRepositoryTopic
9331031
'Body' = (ConvertTo-Json -InputObject $hashBody)
9341032
'Method' = 'Put'
9351033
'Description' = $description
936-
'AcceptHeader' = 'application/vnd.github.mercy-preview+json'
1034+
'AcceptHeader' = $script:mercyAcceptHeader
9371035
'AccessToken' = $AccessToken
9381036
'TelemetryEventName' = $MyInvocation.MyCommand.Name
9391037
'TelemetryProperties' = $telemetryProperties

Tests/GitHubRepositories.tests.ps1

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,102 @@ $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+
AfterAll -ScriptBlock {
42+
Remove-GitHubRepository -Uri $publicRepo.svn_url
43+
Remove-GitHubRepository -Uri $privateRepo.svn_url
44+
}
45+
}
46+
47+
Context 'For any user' {
48+
$repos = Get-GitHubRepository -OwnerName 'octocat' -Type Public
49+
50+
It "Should have results for The Octocat" {
51+
$repos.Count | Should -BeGreaterThan 0
52+
$repos[0].owner.login | Should Be 'octocat'
53+
}
54+
}
55+
56+
Context 'For organizations' {
57+
BeforeAll -Scriptblock {
58+
$repo = New-GitHubRepository -OrganizationName $script:organizationName -RepositoryName ([Guid]::NewGuid().Guid) -AutoInit
59+
60+
# Avoid PSScriptAnalyzer PSUseDeclaredVarsMoreThanAssignments
61+
$repo = $repo
62+
}
63+
64+
$repos = Get-GitHubRepository -OrganizationName $script:organizationName -Type All
65+
It "Should have results for the organization" {
66+
$repo.name | Should BeIn $repos.name
67+
}
68+
69+
AfterAll -ScriptBlock {
70+
Remove-GitHubRepository -Uri $repo.svn_url
71+
}
72+
}
73+
74+
Context 'For public repos' {
75+
# Skipping these tests for now, as it would run for a _very_ long time.
76+
# No obviously good way to verify this.
77+
}
78+
79+
Context 'For a specific repo' {
80+
BeforeAll -Scriptblock {
81+
$repo = New-GitHubRepository -RepositoryName ([Guid]::NewGuid().Guid) -AutoInit
1882

83+
# Avoid PSScriptAnalyzer PSUseDeclaredVarsMoreThanAssignments
84+
$repo = $repo
85+
}
86+
87+
$returned = Get-GitHubRepository -Uri $repo.svn_url
88+
It "Should be a single result using Uri ParameterSet" {
89+
$returned | Should -BeOfType PSCustomObject
90+
}
91+
92+
$returned = Get-GitHubRepository -OwnerName $repo.owner.login -RepositoryName $repo.Name
93+
It "Should be a single result using Elements ParameterSet" {
94+
$returned | Should -BeOfType PSCustomObject
95+
}
96+
97+
It 'Should not permit additional parameters' {
98+
{ Get-GitHubRepository -OwnerName $repo.owner.login -RepositoryName $repo.Name -Type All } | Should Throw
99+
}
100+
101+
It 'Should require both OwnerName and RepositoryName' {
102+
{ Get-GitHubRepository -RepositoryName $repo.Name } | Should Throw
103+
{ Get-GitHubRepository -Uri "https://github.com/$script:ownerName" } | Should Throw
104+
}
105+
106+
AfterAll -ScriptBlock {
107+
Remove-GitHubRepository -Uri $repo.svn_url
108+
}
109+
}
110+
}
111+
112+
Describe 'Modifying repositories' {
19113
Context -Name 'For renaming a repository' -Fixture {
20114
BeforeEach -Scriptblock {
21115
$repo = New-GitHubRepository -RepositoryName ([Guid]::NewGuid().Guid) -AutoInit
@@ -35,7 +129,7 @@ try
35129
## cleanup temp testing repository
36130
AfterEach -Scriptblock {
37131
## 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
132+
Remove-GitHubRepository -Uri "$($repo.svn_url)$suffixToAddToRepo"
39133
}
40134
}
41135
}

0 commit comments

Comments
 (0)