From 8e55f5af03aa0ae2d402e52b7cd50ca43ded03a7 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Thu, 16 Jul 2020 11:45:59 -0700 Subject: [PATCH] Add GitHub Reactions support (#193) Adds support for `Issue` and `PullRequest` reactions by adding the following new functions: * [`Get-GitHubReaction`](https://developer.github.com/v3/reactions/#list-reactions-for-an-issue) * [`Set-GitHubReaction`](https://developer.github.com/v3/reactions/#create-reaction-for-an-issue) * [`Remove-GitHubReaction`](https://developer.github.com/v3/reactions/#delete-an-issue-reaction) --- GitHubAssignees.ps1 | 4 + GitHubBranches.ps1 | 1 + GitHubContents.ps1 | 1 + GitHubEvents.ps1 | 1 + GitHubIssueComments.ps1 | 4 + GitHubIssues.ps1 | 6 + GitHubLabels.ps1 | 8 + GitHubMilestones.ps1 | 4 + GitHubMiscellaneous.ps1 | 5 +- GitHubProjects.ps1 | 2 + GitHubPullRequests.ps1 | 2 + GitHubReactions.ps1 | 710 ++++++++++++++++++++++++++++++++ GitHubReleases.ps1 | 1 + GitHubRepositories.ps1 | 11 + GitHubRepositoryForks.ps1 | 2 + GitHubRepositoryTraffic.ps1 | 4 + GitHubTeams.ps1 | 1 + PowerShellForGitHub.psd1 | 9 +- Tests/GitHubReactions.Tests.ps1 | 118 ++++++ 19 files changed, 889 insertions(+), 5 deletions(-) create mode 100644 GitHubReactions.ps1 create mode 100644 Tests/GitHubReactions.Tests.ps1 diff --git a/GitHubAssignees.ps1 b/GitHubAssignees.ps1 index 0964573a..6bee8221 100644 --- a/GitHubAssignees.ps1 +++ b/GitHubAssignees.ps1 @@ -44,6 +44,7 @@ filter Get-GitHubAssignee GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository GitHub.User @@ -156,6 +157,7 @@ filter Test-GitHubAssignee GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository GitHub.User @@ -299,6 +301,7 @@ function Add-GitHubAssignee GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository GitHub.User @@ -476,6 +479,7 @@ function Remove-GitHubAssignee GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubBranches.ps1 b/GitHubBranches.ps1 index 2e52cf40..41e5b100 100644 --- a/GitHubBranches.ps1 +++ b/GitHubBranches.ps1 @@ -56,6 +56,7 @@ filter Get-GitHubRepositoryBranch GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubContents.ps1 b/GitHubContents.ps1 index 7ce6472a..53397540 100644 --- a/GitHubContents.ps1 +++ b/GitHubContents.ps1 @@ -71,6 +71,7 @@ GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubEvents.ps1 b/GitHubEvents.ps1 index c904bc6a..000574af 100644 --- a/GitHubEvents.ps1 +++ b/GitHubEvents.ps1 @@ -58,6 +58,7 @@ filter Get-GitHubEvent GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubIssueComments.ps1 b/GitHubIssueComments.ps1 index 1eacf806..0542581b 100644 --- a/GitHubIssueComments.ps1 +++ b/GitHubIssueComments.ps1 @@ -79,6 +79,7 @@ filter Get-GitHubIssueComment GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -343,6 +344,7 @@ filter New-GitHubIssueComment GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository GitHub.User @@ -486,6 +488,7 @@ filter Set-GitHubIssueComment GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository GitHub.User @@ -616,6 +619,7 @@ filter Remove-GitHubIssueComment GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubIssues.ps1 b/GitHubIssues.ps1 index 1010b19a..705a9da6 100644 --- a/GitHubIssues.ps1 +++ b/GitHubIssues.ps1 @@ -128,6 +128,7 @@ filter Get-GitHubIssue GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository GitHub.User @@ -431,6 +432,7 @@ filter Get-GitHubIssueTimeline GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -569,6 +571,7 @@ filter New-GitHubIssue GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -737,6 +740,7 @@ filter Set-GitHubIssue GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -887,6 +891,7 @@ filter Lock-GitHubIssue GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -1017,6 +1022,7 @@ filter Unlock-GitHubIssue GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubLabels.ps1 b/GitHubLabels.ps1 index 80f55480..dd1391ac 100644 --- a/GitHubLabels.ps1 +++ b/GitHubLabels.ps1 @@ -63,6 +63,7 @@ filter Get-GitHubLabel GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -250,6 +251,7 @@ filter New-GitHubLabel GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -391,6 +393,7 @@ filter Remove-GitHubLabel GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -548,6 +551,7 @@ filter Set-GitHubLabel GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -694,6 +698,7 @@ filter Initialize-GitHubLabel GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -833,6 +838,7 @@ function Add-GitHubIssueLabel GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -993,6 +999,7 @@ function Set-GitHubIssueLabel GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -1184,6 +1191,7 @@ filter Remove-GitHubIssueLabel GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubMilestones.ps1 b/GitHubMilestones.ps1 index ddbbd165..d2f2dc73 100644 --- a/GitHubMilestones.ps1 +++ b/GitHubMilestones.ps1 @@ -67,6 +67,7 @@ filter Get-GitHubMilestone GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -269,6 +270,7 @@ filter New-GitHubMilestone GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -450,6 +452,7 @@ filter Set-GitHubMilestone GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -630,6 +633,7 @@ filter Remove-GitHubMilestone GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubMiscellaneous.ps1 b/GitHubMiscellaneous.ps1 index a2498de5..20ecb3ee 100644 --- a/GitHubMiscellaneous.ps1 +++ b/GitHubMiscellaneous.ps1 @@ -235,7 +235,6 @@ filter Get-GitHubLicense the background, enabling the command prompt to provide status information. If not supplied here, the DefaultNoStatus configuration property value will be used. - .INPUTS [String] GitHub.Branch @@ -249,10 +248,10 @@ filter Get-GitHubLicense GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository - .OUTPUTS GitHub.License @@ -487,10 +486,10 @@ filter Get-GitHubCodeOfConduct GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository - .OUTPUTS GitHub.CodeOfConduct diff --git a/GitHubProjects.ps1 b/GitHubProjects.ps1 index 3476f263..c96dc30f 100644 --- a/GitHubProjects.ps1 +++ b/GitHubProjects.ps1 @@ -62,6 +62,7 @@ filter Get-GitHubProject GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -274,6 +275,7 @@ filter New-GitHubProject GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubPullRequests.ps1 b/GitHubPullRequests.ps1 index 09860e02..0eeb5ee4 100644 --- a/GitHubPullRequests.ps1 +++ b/GitHubPullRequests.ps1 @@ -76,6 +76,7 @@ filter Get-GitHubPullRequest GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -271,6 +272,7 @@ filter New-GitHubPullRequest GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubReactions.ps1 b/GitHubReactions.ps1 new file mode 100644 index 00000000..47c6e992 --- /dev/null +++ b/GitHubReactions.ps1 @@ -0,0 +1,710 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +@{ + GitHubReactionTypeName = 'GitHub.Reaction' + }.GetEnumerator() | ForEach-Object { + Set-Variable -Scope Script -Option ReadOnly -Name $_.Key -Value $_.Value + } + +filter Get-GitHubReaction +{ +<# + .SYNOPSIS + Retrieve reactions of a given GitHub Issue or Pull Request. + + .DESCRIPTION + Retrieve reactions of a given GitHub Issue or Pull Request. + + The Git repo for this module can be found here: http://aka.ms/PowerShellForGitHub + + .PARAMETER OwnerName + Owner of the repository. + If not supplied here, the DefaultOwnerName configuration property value will be used. + + .PARAMETER RepositoryName + Name of the repository. + If not supplied here, the DefaultRepositoryName configuration property value will be used. + + .PARAMETER Uri + Uri for the repository. + The OwnerName and RepositoryName will be extracted from here instead of needing to provide + them individually. + + .PARAMETER Issue + The issue number. + + .PARAMETER PullRequest + The pull request number. + + .PARAMETER ReactionType + The type of reaction you want to retrieve. This is also called the 'content' in + the GitHub API. Valid options are based off: + https://developer.github.com/v3/reactions/#reaction-types + + .PARAMETER AccessToken + If provided, this will be used as the AccessToken for authentication with the + REST Api. Otherwise, will attempt to use the configured value or will run + unauthenticated. + + .PARAMETER NoStatus + If this switch is specified, long-running commands will run on the main thread + with no commandline status update. When not specified, those commands run in + the background, enabling the command prompt to provide status information. + If not supplied here, the DefaultNoStatus configuration property value will be used. + + .INPUTS + GitHub.Branch + GitHub.Content + GitHub.Event + GitHub.Issue + GitHub.IssueComment + GitHub.Label + GitHub.Milestone + GitHub.PullRequest + GitHub.Project + GitHub.ProjectCard + GitHub.ProjectColumn + GitHub.Reaction + GitHub.Release + GitHub.Repository + + .OUTPUTS + GitHub.Reaction + + .EXAMPLE + Get-GitHubReaction -OwnerName Microsoft -RepositoryName PowerShellForGitHub -Issue 157 + + Gets the reactions for issue 157 from the Microsoft\PowerShellForGitHub + project. + + .EXAMPLE + Get-GitHubReaction -OwnerName Microsoft -RepositoryName PowerShellForGitHub -Issue 157 -ReactionType eyes + + Gets the 'eyes' reactions for issue 157 from the Microsoft\PowerShellForGitHub + project. + + .EXAMPLE + Get-GitHubIssue -OwnerName Microsoft -RepositoryName PowerShellForGitHub -Issue 157 | Get-GitHubReaction + + Gets a GitHub issue and pipe it into Get-GitHubReaction to get all + the reactions for that issue. + + .EXAMPLE + Get-GitHubPullRequest -Uri https://github.com/microsoft/PowerShellForGitHub -PullRequest 193 | Get-GitHubReaction + + Gets a GitHub pull request and pipes it into Get-GitHubReaction + to get all the reactions for that pull request. + + .NOTES + Currently, issue comments, pull request comments and commit comments are not supported. +#> + [CmdletBinding( + SupportsShouldProcess, + DefaultParameterSetName='ElementsIssue')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "", Justification="Methods called within here make use of PSShouldProcess, and the switch is passed on to them inherently.")] + [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.")] + param( + [Parameter( + Mandatory, + ParameterSetName='ElementsIssue')] + [Parameter( + Mandatory, + ParameterSetName='ElementsPullRequest')] + [string] $OwnerName, + + [Parameter( + Mandatory, + ParameterSetName='ElementsIssue')] + [Parameter( + Mandatory, + ParameterSetName='ElementsPullRequest')] + [string] $RepositoryName, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='UriIssue')] + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='UriPullRequest')] + [Alias('RepositoryUrl')] + [string] $Uri, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='ElementsIssue')] + [Parameter( + Mandatory, + ParameterSetName='UriIssue', + ValueFromPipelineByPropertyName)] + [Alias('IssueNumber')] + [int64] $Issue, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='ElementsPullRequest')] + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='UriPullRequest')] + [Alias('PullRequestNumber')] + [int64] $PullRequest, + + [ValidateSet('+1', '-1', 'Laugh', 'Confused', 'Heart', 'Hooray', 'Rocket', 'Eyes')] + [string] $ReactionType, + + [string] $AccessToken, + + [switch] $NoStatus + ) + + Write-InvocationLog + + $elements = Resolve-RepositoryElements + $OwnerName = $elements.ownerName + $RepositoryName = $elements.repositoryName + + $telemetryProperties = @{ + 'OwnerName' = (Get-PiiSafeString -PlainText $OwnerName) + 'RepositoryName' = (Get-PiiSafeString -PlainText $RepositoryName) + } + + $splatForAddedProperties = @{ + OwnerName = $OwnerName + Repository = $RepositoryName + } + + if ($Issue) + { + $splatForAddedProperties.Issue = $Issue + $targetObjectNumber = $Issue + $targetObjectTypeName = 'Issue' + $uriFragment = "/repos/$OwnerName/$RepositoryName/issues/$targetObjectNumber/reactions" + } + else + { + # Pull Request + $splatForAddedProperties.PullRequest = $PullRequest + $targetObjectNumber = $PullRequest + $targetObjectTypeName = 'Pull Request' + $uriFragment = "/repos/$OwnerName/$RepositoryName/issues/$targetObjectNumber/reactions" + } + + if ($PSBoundParameters.ContainsKey('ReactionType')) + { + $uriFragment += "?content=" + [Uri]::EscapeDataString($ReactionType.ToLower()) + } + + $description = "Getting reactions for $targetObjectTypeName $targetObjectNumber in $RepositoryName" + + $params = @{ + 'UriFragment' = $uriFragment + 'Description' = $description + 'AcceptHeader' = $script:squirrelGirlAcceptHeader + 'AccessToken' = $AccessToken + 'TelemetryEventName' = $MyInvocation.MyCommand.Name + 'TelemetryProperties' = $telemetryProperties + 'NoStatus' = (Resolve-ParameterWithDefaultConfigurationValue -Name NoStatus -ConfigValueName DefaultNoStatus) + } + + $result = Invoke-GHRestMethodMultipleResult @params + return ($result | Add-GitHubReactionAdditionalProperties @splatForAddedProperties) +} + +filter Set-GitHubReaction +{ +<# + .SYNOPSIS + Sets a reaction of a given GitHub Issue or Pull Request. + + .DESCRIPTION + Sets a reaction of a given GitHub Issue or Pull Request. + + The Git repo for this module can be found here: http://aka.ms/PowerShellForGitHub + + .PARAMETER OwnerName + Owner of the repository. + If not supplied here, the DefaultOwnerName configuration property value will be used. + + .PARAMETER RepositoryName + Name of the repository. + If not supplied here, the DefaultRepositoryName configuration property value will be used. + + .PARAMETER Uri + Uri for the repository. + The OwnerName and RepositoryName will be extracted from here instead of needing to provide + them individually. + + .PARAMETER Issue + The issue number. + + .PARAMETER PullRequest + The pull request number. + + .PARAMETER ReactionType + The type of reaction you want to set. This is aslo called the 'content' in the GitHub API. + Valid options are based off: https://developer.github.com/v3/reactions/#reaction-types + + .PARAMETER AccessToken + If provided, this will be used as the AccessToken for authentication with the + REST Api. Otherwise, will attempt to use the configured value or will run unauthenticated. + + .PARAMETER NoStatus + If this switch is specified, long-running commands will run on the main thread + with no commandline status update. When not specified, those commands run in + the background, enabling the command prompt to provide status information. + If not supplied here, the DefaultNoStatus configuration property value will be used. + + .INPUTS + GitHub.Branch + GitHub.Content + GitHub.Event + GitHub.Issue + GitHub.IssueComment + GitHub.Label + GitHub.Milestone + GitHub.PullRequest + GitHub.Project + GitHub.ProjectCard + GitHub.ProjectColumn + GitHub.Reaction + GitHub.Release + GitHub.Repository + + .OUTPUTS + GitHub.Reaction + + .EXAMPLE + Set-GitHubReaction -OwnerName PowerShell -RepositoryName PowerShell -Issue 12626 -ReactionType rocket + + Sets the 'rocket' reaction for issue 12626 of the PowerShell\PowerShell project. + + .EXAMPLE + Get-GitHubPullRequest -Uri https://github.com/microsoft/PowerShellForGitHub -PullRequest 193 | Set-GitHubReaction -ReactionType Heart + + Gets a GitHub pull request and pipes it into Set-GitHubReaction to set the + 'heart' reaction for that pull request. + + .NOTES + Currently, issue comments, pull request comments and commit comments are not supported. +#> + [CmdletBinding( + SupportsShouldProcess, + DefaultParameterSetName='ElementsIssue')] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "", Justification="Methods called within here make use of PSShouldProcess, and the switch is passed on to them inherently.")] + [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.")] + param( + [Parameter( + Mandatory, + ParameterSetName='ElementsIssue')] + [Parameter( + Mandatory, + ParameterSetName='ElementsPullRequest')] + [string] $OwnerName, + + [Parameter( + Mandatory, + ParameterSetName='ElementsIssue')] + [Parameter( + Mandatory, + ParameterSetName='ElementsPullRequest')] + [string] $RepositoryName, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='UriIssue')] + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='UriPullRequest')] + [Alias('RepositoryUrl')] + [string] $Uri, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='ElementsIssue')] + [Parameter( + Mandatory, + ParameterSetName='UriIssue', + ValueFromPipelineByPropertyName)] + [Alias('IssueNumber')] + [int64] $Issue, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='ElementsPullRequest')] + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='UriPullRequest')] + [Alias('PullRequestNumber')] + [int64] $PullRequest, + + [ValidateSet('+1', '-1', 'Laugh', 'Confused', 'Heart', 'Hooray', 'Rocket', 'Eyes')] + [Parameter(Mandatory)] + [string] $ReactionType, + + [string] $AccessToken, + + [switch] $NoStatus + ) + + Write-InvocationLog + + $elements = Resolve-RepositoryElements + $OwnerName = $elements.ownerName + $RepositoryName = $elements.repositoryName + + $telemetryProperties = @{ + 'OwnerName' = (Get-PiiSafeString -PlainText $OwnerName) + 'RepositoryName' = (Get-PiiSafeString -PlainText $RepositoryName) + } + + $splatForAddedProperties = @{ + OwnerName = $OwnerName + Repository = $RepositoryName + } + + if ($Issue) + { + $splatForAddedProperties.Issue = $Issue + $targetObjectNumber = $Issue + $targetObjectTypeName = 'Issue' + $uriFragment = "/repos/$OwnerName/$RepositoryName/issues/$targetObjectNumber/reactions" + } + else + { + # Pull request + $splatForAddedProperties.PullRequest = $PullRequest + $targetObjectNumber = $PullRequest + $targetObjectTypeName = 'Pull Request' + $uriFragment = "/repos/$OwnerName/$RepositoryName/issues/$targetObjectNumber/reactions" + } + + $description = "Setting reaction $ReactionType for $targetObjectTypeName $targetObjectNumber in $RepositoryName" + + $params = @{ + 'UriFragment' = $uriFragment + 'Description' = $description + 'Method' = 'Post' + 'Body' = ConvertTo-Json -InputObject @{ content = $ReactionType.ToLower() } + 'AcceptHeader' = $script:squirrelGirlAcceptHeader + 'AccessToken' = $AccessToken + 'TelemetryEventName' = $MyInvocation.MyCommand.Name + 'TelemetryProperties' = $telemetryProperties + 'NoStatus' = (Resolve-ParameterWithDefaultConfigurationValue -Name NoStatus -ConfigValueName DefaultNoStatus) + } + + $result = Invoke-GHRestMethod @params + + return ($result | Add-GitHubReactionAdditionalProperties @splatForAddedProperties) +} + +filter Remove-GitHubReaction +{ +<# + .SYNOPSIS + Removes a reaction on a given GitHub Issue or Pull Request. + + .DESCRIPTION + Removes a reaction on a given GitHub Issue or Pull Request. + + The Git repo for this module can be found here: http://aka.ms/PowerShellForGitHub + + .PARAMETER OwnerName + Owner of the repository. + If not supplied here, the DefaultOwnerName configuration property value will be used. + + .PARAMETER RepositoryName + Name of the repository. + If not supplied here, the DefaultRepositoryName configuration property value will be used. + + .PARAMETER Uri + Uri for the repository. + The OwnerName and RepositoryName will be extracted from here instead of needing to provide + them individually. + + .PARAMETER Issue + The issue number. + + .PARAMETER PullRequest + The pull request number. + + .PARAMETER ReactionId + The Id of the reaction. You can get this from using Get-GitHubReaction. + + .PARAMETER Force + If this switch is specified, you will not be prompted for confirmation of command execution. + + .PARAMETER AccessToken + If provided, this will be used as the AccessToken for authentication with the + REST Api. Otherwise, will attempt to use the configured value or will run unauthenticated. + + .PARAMETER NoStatus + If this switch is specified, long-running commands will run on the main thread + with no commandline status update. When not specified, those commands run in + the background, enabling the command prompt to provide status information. + If not supplied here, the DefaultNoStatus configuration property value will be used. + + .INPUTS + GitHub.Branch + GitHub.Content + GitHub.Event + GitHub.Issue + GitHub.IssueComment + GitHub.Label + GitHub.Milestone + GitHub.PullRequest + GitHub.Project + GitHub.ProjectCard + GitHub.ProjectColumn + GitHub.Reaction + GitHub.Release + GitHub.Repository + + .OUTPUTS + None + + .EXAMPLE + Remove-GitHubReaction -OwnerName PowerShell -RepositoryName PowerShell -Issue 12626 ` + -ReactionId 1234 + + Remove a reaction by Id on Issue 12626 from the PowerShell\PowerShell project + interactively. + + .EXAMPLE + Remove-GitHubReaction -OwnerName PowerShell -RepositoryName PowerShell -Issue 12626 -ReactionId 1234 -Confirm:$false + + Remove a reaction by Id on Issue 12626 from the PowerShell\PowerShell project + non-interactively. + + .EXAMPLE + Get-GitHubReaction -OwnerName PowerShell -RepositoryName PowerShell -Issue 12626 -ReactionType rocket | Remove-GitHubReaction -Confirm:$false + + Gets a reaction using Get-GitHubReaction and pipes it into Remove-GitHubReaction. + + .NOTES + Currently, issue comments, pull request comments and commit comments are not supported. +#> + [CmdletBinding( + SupportsShouldProcess, + DefaultParameterSetName='ElementsIssue', + ConfirmImpact='High')] + [Alias('Delete-GitHubReaction')] + [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.")] + param( + [Parameter( + Mandatory, + ParameterSetName='ElementsIssue')] + [Parameter( + Mandatory, + ParameterSetName='ElementsPullRequest')] + [string] $OwnerName, + + [Parameter( + Mandatory, + ParameterSetName='ElementsIssue')] + [Parameter( + Mandatory, + ParameterSetName='ElementsPullRequest')] + [string] $RepositoryName, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='UriIssue')] + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='UriPullRequest')] + [Alias('RepositoryUrl')] + [string] $Uri, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='ElementsIssue')] + [Parameter( + Mandatory, + ParameterSetName='UriIssue', + ValueFromPipelineByPropertyName)] + [Alias('IssueNumber')] + [int64] $Issue, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='ElementsPullRequest')] + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ParameterSetName='UriPullRequest')] + [Alias('PullRequestNumber')] + [int64] $PullRequest, + + [Parameter( + Mandatory, + ValueFromPipelineByPropertyName, + ValueFromPipeline)] + [int64] $ReactionId, + + [Parameter()] + [switch] $Force, + + [string] $AccessToken, + + [switch] $NoStatus + ) + + Write-InvocationLog + + $elements = Resolve-RepositoryElements + $OwnerName = $elements.ownerName + $RepositoryName = $elements.repositoryName + + $telemetryProperties = @{ + 'OwnerName' = (Get-PiiSafeString -PlainText $OwnerName) + 'RepositoryName' = (Get-PiiSafeString -PlainText $RepositoryName) + } + + if ($Issue) + { + $targetObjectNumber = $Issue + $targetObjectTypeName = 'Issue' + $uriFragment = "/repos/$OwnerName/$RepositoryName/issues/$targetObjectNumber/reactions/$ReactionId" + } + else + { + # Pull request + $targetObjectNumber = $PullRequest + $targetObjectTypeName = 'Pull Request' + $uriFragment = "/repos/$OwnerName/$RepositoryName/issues/$targetObjectNumber/reactions/$ReactionId" + } + + $description = "Removing reaction $ReactionId for $targetObjectTypeName $targetObjectNumber in $RepositoryName" + + if ($Force -and (-not $Confirm)) + { + $ConfirmPreference = 'None' + } + + if ($PSCmdlet.ShouldProcess( + $ReactionId, + "Removing reaction for $targetObjectTypeName $targetObjectNumber in $RepositoryName")) + { + $params = @{ + 'UriFragment' = $uriFragment + 'Description' = $description + 'Method' = 'Delete' + 'AcceptHeader' = $script:squirrelGirlAcceptHeader + 'AccessToken' = $AccessToken + 'TelemetryEventName' = $MyInvocation.MyCommand.Name + 'TelemetryProperties' = $telemetryProperties + 'NoStatus' = (Resolve-ParameterWithDefaultConfigurationValue -Name NoStatus -ConfigValueName DefaultNoStatus) + } + + return Invoke-GHRestMethod @params + } +} + +filter Add-GitHubReactionAdditionalProperties +{ +<# + .SYNOPSIS + Adds type name and additional properties to ease pipelining to GitHub Reaction objects. + + .PARAMETER InputObject + The GitHub object to add additional properties to. + + .PARAMETER TypeName + The type that should be assigned to the object. + + .PARAMETER OwnerName + Owner of the repository. + + .PARAMETER RepositoryName + Name of the repository. + + .PARAMETER Issue + The issue number. + + .PARAMETER PullRequest + The pull request number. + + .INPUTS + [PSCustomObject] + + .OUTPUTS + GitHub.Reaction +#> + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "", Justification="Internal helper that is definitely adding more than one property.")] + param( + [Parameter( + Mandatory, + ValueFromPipeline)] + [AllowNull()] + [AllowEmptyCollection()] + [PSCustomObject[]] $InputObject, + + [ValidateNotNullOrEmpty()] + [string] $TypeName = $script:GitHubReactionTypeName, + + [Parameter(Mandatory)] + [string] $OwnerName, + + [Parameter(Mandatory)] + [string] $RepositoryName, + + [Parameter( + Mandatory, + ParameterSetName='Issue')] + [Alias('IssueNumber')] + [int64] $Issue, + + [Parameter( + Mandatory, + ParameterSetName='PullRequest')] + [Alias('PullRequestNumber')] + [int64] $PullRequest + ) + + foreach ($item in $InputObject) + { + $item.PSObject.TypeNames.Insert(0, $TypeName) + + if (-not (Get-GitHubConfiguration -Name DisablePipelineSupport)) + { + $repositoryUrl = Join-GitHubUri -OwnerName $OwnerName -RepositoryName $RepositoryName + Add-Member -InputObject $item -Name 'RepositoryUrl' -Value $repositoryUrl -MemberType NoteProperty -Force + Add-Member -InputObject $item -Name 'ReactionId' -Value $item.id -MemberType NoteProperty -Force + + if ($PullRequest) + { + Add-Member -InputObject $item -Name 'PullRequestNumber' -Value $PullRequest -MemberType NoteProperty -Force + } + else + { + # Issue + Add-Member -InputObject $item -Name 'IssueNumber' -Value $Issue -MemberType NoteProperty -Force + } + + @('assignee', 'assignees', 'user') | + ForEach-Object { + if ($null -ne $item.$_) + { + $null = Add-GitHubUserAdditionalProperties -InputObject $item.$_ + } + } + } + + Write-Output $item + } +} diff --git a/GitHubReleases.ps1 b/GitHubReleases.ps1 index 997faff6..7e5cf6c9 100644 --- a/GitHubReleases.ps1 +++ b/GitHubReleases.ps1 @@ -65,6 +65,7 @@ filter Get-GitHubRelease GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubRepositories.ps1 b/GitHubRepositories.ps1 index 847e82b5..6a3fbc89 100644 --- a/GitHubRepositories.ps1 +++ b/GitHubRepositories.ps1 @@ -448,6 +448,7 @@ filter Remove-GitHubRepository GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -614,6 +615,7 @@ filter Get-GitHubRepository GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -938,6 +940,7 @@ filter Rename-GitHubRepository GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -1119,6 +1122,7 @@ filter Set-GitHubRepository GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -1303,6 +1307,7 @@ filter Get-GitHubRepositoryTopic GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -1418,6 +1423,7 @@ function Set-GitHubRepositoryTopic GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -1605,6 +1611,7 @@ filter Get-GitHubRepositoryContributor GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -1757,6 +1764,7 @@ filter Get-GitHubRepositoryCollaborator GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -1875,6 +1883,7 @@ filter Get-GitHubRepositoryLanguage GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -1983,6 +1992,7 @@ filter Get-GitHubRepositoryTag GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -2097,6 +2107,7 @@ filter Move-GitHubRepositoryOwnership GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubRepositoryForks.ps1 b/GitHubRepositoryForks.ps1 index 98e311d2..23cc63ae 100644 --- a/GitHubRepositoryForks.ps1 +++ b/GitHubRepositoryForks.ps1 @@ -50,6 +50,7 @@ filter Get-GitHubRepositoryFork GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -167,6 +168,7 @@ filter New-GitHubRepositoryFork GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubRepositoryTraffic.ps1 b/GitHubRepositoryTraffic.ps1 index d3c26ddd..0d32c21e 100644 --- a/GitHubRepositoryTraffic.ps1 +++ b/GitHubRepositoryTraffic.ps1 @@ -56,6 +56,7 @@ filter Get-GitHubReferrerTraffic GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -169,6 +170,7 @@ filter Get-GitHubPathTraffic GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -289,6 +291,7 @@ filter Get-GitHubViewTraffic GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository @@ -413,6 +416,7 @@ filter Get-GitHubCloneTraffic GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository diff --git a/GitHubTeams.ps1 b/GitHubTeams.ps1 index 54b1f809..44c94399 100644 --- a/GitHubTeams.ps1 +++ b/GitHubTeams.ps1 @@ -60,6 +60,7 @@ filter Get-GitHubTeam GitHub.Project GitHub.ProjectCard GitHub.ProjectColumn + GitHub.Reaction GitHub.Release GitHub.Repository GitHub.Team diff --git a/PowerShellForGitHub.psd1 b/PowerShellForGitHub.psd1 index a15992a6..4be54289 100644 --- a/PowerShellForGitHub.psd1 +++ b/PowerShellForGitHub.psd1 @@ -41,6 +41,7 @@ 'GitHubProjectCards.ps1', 'GitHubProjectColumns.ps1', 'GitHubPullRequests.ps1', + 'GitHubReactions.ps1', 'GitHubReleases.ps1', 'GitHubRepositories.ps1', 'GitHubRepositoryForks.ps1', @@ -85,6 +86,7 @@ 'Get-GitHubProjectColumn', 'Get-GitHubPullRequest', 'Get-GitHubRateLimit', + 'Get-GitHubReaction', 'Get-GitHubReferrerTraffic', 'Get-GitHubRelease', 'Get-GitHubRepository', @@ -131,6 +133,7 @@ 'Remove-GitHubProject', 'Remove-GitHubProjectCard', 'Remove-GitHubProjectColumn', + 'Remove-GitHubReaction', 'Remove-GitHubRepository', 'Remove-GitHubRepositoryBranch' 'Rename-GitHubRepository', @@ -148,7 +151,8 @@ 'Set-GitHubProject', 'Set-GitHubProjectCard', 'Set-GitHubProjectColumn', - 'Set-GitHubRepository' + 'Set-GitHubReaction', + 'Set-GitHubRepository', 'Set-GitHubRepositoryTopic', 'Split-GitHubUri', 'Test-GitHubAssignee', @@ -166,7 +170,8 @@ 'Delete-GitHubMilestone', 'Delete-GitHubProject', 'Delete-GitHubProjectCard', - 'Delete-GitHubProjectColumn' + 'Delete-GitHubProjectColumn', + 'Delete-GitHubReaction', 'Delete-GitHubRepository', 'Delete-GitHubRepositoryBranch', 'Get-GitHubBranch', diff --git a/Tests/GitHubReactions.Tests.ps1 b/Tests/GitHubReactions.Tests.ps1 new file mode 100644 index 00000000..703b7f43 --- /dev/null +++ b/Tests/GitHubReactions.Tests.ps1 @@ -0,0 +1,118 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +<# +.Synopsis + Tests for GitHubReactions.ps1 module +#> + +[CmdletBinding()] +[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '', + Justification='Suppress false positives in Pester code blocks')] +param() + +# This is common test code setup logic for all Pester test files +$moduleRootPath = Split-Path -Path $PSScriptRoot -Parent +. (Join-Path -Path $moduleRootPath -ChildPath 'Tests\Common.ps1') + +try +{ + # Define Script-scoped, readonly, hidden variables. + @{ + defaultIssueTitle = "Test Title" + defaultCommentBody = "This is a test body." + defaultReactionType = "+1" + otherReactionType = "eyes" + }.GetEnumerator() | ForEach-Object { + Set-Variable -Force -Scope Script -Option ReadOnly -Visibility Private -Name $_.Key -Value $_.Value + } + + Describe 'Creating, modifying and deleting reactions' { + BeforeAll { + $repo = New-GitHubRepository -RepositoryName ([Guid]::NewGuid().Guid) -AutoInit + $issue = New-GitHubIssue -Uri $repo.svn_url -Title $defaultIssueTitle + $issueComment = $issue | New-GitHubIssueComment -Body "Foo" + } + + Context 'For creating a reaction' { + Set-GitHubReaction -Uri $repo.svn_url -Issue $issue.IssueNumber -ReactionType $defaultReactionType + $existingReaction = Get-GitHubReaction -Uri $repo.svn_url -Issue $issue.IssueNumber + + It "Should have the expected reaction type" { + $existingReaction.content | Should -Be $defaultReactionType + } + } + + Context 'For getting reactions from an issue' { + Get-GitHubIssue -Uri $repo.svn_url -Issue $issue.IssueNumber | Set-GitHubReaction -ReactionType $otherReactionType + $allReactions = Get-GitHubReaction -Uri $repo.svn_url -Issue $issue.IssueNumber + $specificReactions = Get-GitHubReaction -Uri $repo.svn_url -Issue $issue.IssueNumber -ReactionType $otherReactionType + + It 'Should have the expected number of reactions' { + $allReactions.Count | Should -Be 2 + $specificReactions | Measure-Object | Select-Object -ExpandProperty Count | Should -Be 1 + } + + It 'Should have the expected reaction content' { + $specificReactions.content | Should -Be $otherReactionType + $specificReactions.RepositoryUrl | Should -Be $repo.RepositoryUrl + $specificReactions.IssueNumber | Should -Be $issue.IssueNumber + $specificReactions.ReactionId | Should -Be $specificReactions.id + $specificReactions.PSObject.TypeNames[0] | Should -Be 'GitHub.Reaction' + } + } + + Context 'For getting reactions from a pull request' { + # TODO: there are currently PRs out to add the ability to create new branches and add content to a repo. + # When those go in, this test can be refactored to use those so the test is more reliable using a test PR. + $url = 'https://github.com/microsoft/PowerShellForGitHub' + $pr = Get-GitHubPullRequest -Uri $url -PullRequest 193 + + $allReactions = $pr | Get-GitHubReaction + $specificReactions = $pr | Get-GitHubReaction -ReactionType $otherReactionType + + It 'Should have the expected number of reactions' { + $allReactions.Count | Should -Be 2 + $specificReactions | Measure-Object | Select-Object -ExpandProperty Count | Should -Be 1 + } + + It 'Should have the expected reaction content' { + $specificReactions.content | Should -Be $otherReactionType + $specificReactions.RepositoryUrl | Should -Be $url + $specificReactions.PullRequestNumber | Should -Be $pr.PullRequestNumber + $specificReactions.ReactionId | Should -Be $specificReactions.id + $specificReactions.PSObject.TypeNames[0] | Should -Be 'GitHub.Reaction' + } + } + + Context 'For getting reactions from an Issue and deleting them' { + $existingReactions = @(Get-GitHubReaction -Uri $repo.svn_url -Issue $issue.number) + + It 'Should have the expected number of reactions' { + $existingReactions.Count | Should -Be 2 + } + + $existingReactions | Remove-GitHubReaction -Force + + $existingReactions = @(Get-GitHubReaction -Uri $repo.svn_url -Issue $issue.number) + + It 'Should have no reactions' { + $existingReactions + $existingReactions.Count | Should -Be 0 + } + } + + AfterAll { + Remove-GitHubRepository -Uri $repo.svn_url -Confirm:$false + } + } +} +finally +{ + if (Test-Path -Path $script:originalConfigFile -PathType Leaf) + { + # Restore the user's configuration to its pre-test state + Restore-GitHubConfiguration -Path $script:originalConfigFile + $script:originalConfigFile = $null + } +}