diff --git a/src/teammembers.psm1 b/src/teammembers.psm1 index 2d9d8a983..29deda476 100644 --- a/src/teammembers.psm1 +++ b/src/teammembers.psm1 @@ -27,20 +27,29 @@ function _buildURL { # Apply types to the returned objects so format and type files can # identify the object and act on it. function _applyTypes { - param($item) - $item.PSObject.TypeNames.Insert(0, 'Team.TeamMember') + param( + [Parameter(Mandatory = $true)] + $item, + [Parameter(Mandatory = $true)] + $team + ) + + # Add the team name as a NoteProperty so we can use it further down the pipeline (it's not returned from the REST call) + $item | Add-Member -MemberType NoteProperty -Name Team -Value $team + $item.PSObject.TypeNames.Insert(0, 'Team.TeamMember') } function Get-TeamMember { - [CmdletBinding(DefaultParameterSetName = 'List')] + [CmdletBinding()] param ( - [Parameter(ParameterSetName = 'List')] + [Parameter()] [int] $Top, - [Parameter(ParameterSetName = 'List')] + [Parameter()] [int] $Skip, - [Parameter(Mandatory = $true, ParameterSetName = 'List', ValueFromPipeline = $true)] + [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName=$true)] + [Alias('name')] [string] $TeamId ) @@ -59,8 +68,6 @@ function Get-TeamMember { $listurl += _appendQueryString -name "`$top" -value $top $listurl += _appendQueryString -name "`$skip" -value $skip - Write-Output $listurl - # Call the REST API if (_useWindowsAuthenticationOnPremise) { $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Uri $listurl -UseDefaultCredentials @@ -71,7 +78,7 @@ function Get-TeamMember { # Apply a Type Name so we can use custom format view and custom type extensions foreach ($item in $resp.value) { - _applyTypes -item $item + _applyTypes -item $item -team $TeamId } Write-Output $resp.value diff --git a/src/teams.psm1 b/src/teams.psm1 index 308a19cf1..9a2ddb557 100644 --- a/src/teams.psm1 +++ b/src/teams.psm1 @@ -30,8 +30,16 @@ function _buildURL { # Apply types to the returned objects so format and type files can # identify the object and act on it. function _applyTypes { - param($item) - $item.PSObject.TypeNames.Insert(0, 'Team.Team') + param( + [Parameter(Mandatory = $true)] + $item, + [Parameter(Mandatory = $true)] + $ProjectName + ) + + # Add the ProjectName as a NoteProperty so we can use it further down the pipeline (it's not returned from the REST call) + $item | Add-Member -MemberType NoteProperty -Name ProjectName -Value $ProjectName + $item.PSObject.TypeNames.Insert(0, 'Team.Team') } function Get-Team { @@ -43,7 +51,7 @@ function Get-Team { [Parameter(ParameterSetName = 'List')] [int] $Skip, - [Parameter(ParameterSetName = 'ByID', ValueFromPipeline = $true)] + [Parameter(ParameterSetName = 'ByID')] [string[]] $TeamId ) @@ -68,7 +76,7 @@ function Get-Team { $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Uri $listurl -Headers @{Authorization = "Basic $env:TEAM_PAT"} } - _applyTypes -item $resp + _applyTypes -item $resp -ProjectName $ProjectName Write-Output $resp } @@ -89,7 +97,7 @@ function Get-Team { # Apply a Type Name so we can use custom format view and custom type extensions foreach ($item in $resp.value) { - _applyTypes -item $item + _applyTypes -item $item -ProjectName $ProjectName } Write-Output $resp.value @@ -98,6 +106,7 @@ function Get-Team { } function Add-Team { + [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$TeamName, @@ -122,15 +131,17 @@ function Add-Team { $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Method Post -ContentType "application/json" -Body $body -Uri $listurl -Headers @{Authorization = "Basic $env:TEAM_PAT"} } - _applyTypes -item $resp + _applyTypes -item $resp -ProjectName $ProjectName return $resp } } function Update-Team { + [CmdletBinding()] param( - [Parameter(Mandatory = $True)] + [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $true)] + [Alias('name')] [string]$TeamToUpdate, [string]$NewTeamName, [string]$Description @@ -169,16 +180,20 @@ function Update-Team { $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Method Patch -ContentType "application/json" -Body $body -Uri $listurl -Headers @{Authorization = "Basic $env:TEAM_PAT"} } - _applyTypes -item $resp + _applyTypes -item $resp -ProjectName $ProjectName return $resp } } function Remove-Team { + [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param( - [Parameter(Mandatory = $True)] - [string]$TeamId + [Parameter(Mandatory = $True, ValueFromPipelineByPropertyName = $true)] + [Alias('name')] + [string]$TeamId, + + [switch]$Force ) DynamicParam { _buildProjectNameDynamicParam @@ -190,15 +205,17 @@ function Remove-Team { $listurl = _buildURL -ProjectName $ProjectName -TeamId $TeamId - # Call the REST API - if (_useWindowsAuthenticationOnPremise) { - $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Method Delete -Uri $listurl -UseDefaultCredentials - } - else { - $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Method Delete -Uri $listurl -Headers @{Authorization = "Basic $env:TEAM_PAT"} - } + if ($Force -or $PSCmdlet.ShouldProcess($TeamId, "Delete team")) { + # Call the REST API + if (_useWindowsAuthenticationOnPremise) { + $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Method Delete -Uri $listurl -UseDefaultCredentials + } + else { + $resp = Invoke-RestMethod -UserAgent (_getUserAgent) -Method Delete -Uri $listurl -Headers @{Authorization = "Basic $env:TEAM_PAT"} + } - return $resp + Write-Output "Deleted team $TeamId" + } } } diff --git a/src/types.ps1xml b/src/types.ps1xml index 96112622e..2e1be4650 100644 --- a/src/types.ps1xml +++ b/src/types.ps1xml @@ -1,366 +1,368 @@ - - - - - - Team.Environment - - - PSStandardMembers - - - DefaultDisplayPropertySet - - id - name - status - - - - - - - - - Team.Project - - - PSStandardMembers - - - DefaultDisplayPropertySet - - name - id - description - - - - + + + + + + Team.Environment + + + PSStandardMembers + + + DefaultDisplayPropertySet + + id + name + status + + + + + + + + + Team.Project + + + PSStandardMembers + + + DefaultDisplayPropertySet + + name + id + description + + + + projectname name - - - - - Team.Team - - - PSStandardMembers - - - DefaultDisplayPropertySet - - name - id - description - - - - - - - - - Team.TeamMember - - - PSStandardMembers - - - DefaultDisplayPropertySet - - displayName - id - uniqueName - - - - - - - - - Team.Build - - - name - buildNumber - - - queueName - $this.queue.name - - - queueID - $this.queue.id - - - definitionName - $this.definition.name - - - projectName - $this.project.name - - - repositoryType - $this.repository.type - - - requestedForUser - $this.requestedFor.displayName - - - requestedByUser - $this.requestedBy.displayName - - - lastChangedByUser - $this.lastChangedBy.displayName - - - - - - Team.Release - - - definitionName - $this.releaseDefinition.name - - - definitionId - $this.releaseDefinition.id - - - projectId - $this.projectReference.id - - - requestedForUser - $this.requestedFor.displayName - - - modifiedByUser - $this.modifiedBy.displayName - - - createdByUser - $this.createdBy.displayName - - - PSStandardMembers - - - DefaultDisplayPropertySet - - id - name - status - definitionName - - - - - - - - - Team.Queue - - - poolName - $this.pool.name - - - - - - Team.ServiceEndpoint - - - createdByUser - $this.createdBy.displayName - - - - - - Team.AzureSubscription - - - PSStandardMembers - - - DefaultDisplayPropertySet - - displayName - subscriptionId - subscriptionTenantId - - - - - - - - - Team.BuildDefinition - - - authoredByUser - $this.authoredBy.displayName - - - queueName - $this.queue.name - - - PSStandardMembers - - - DefaultDisplayPropertySet - - id - name - queueName - authoredByUser - - - - - - - - - Team.Pool - - - createdByUser - $this.createdBy.displayName - - - queueName - $this.queue.name - - - PSStandardMembers - - - DefaultDisplayPropertySet - - name - createdByUser - - - - - - - - - Team.ReleaseDefinition - - - createdByUser - $this.createdBy.displayName - - - PSStandardMembers - - - DefaultDisplayPropertySet - - id - name - createdByUser - - - - - - - - - Team.Approval - - - approverName - $this.approver.displayName - - - releaseName - $this.release.name - - - releaseDefinitionName - $this.releaseDefinition.name - - - releaseEnvironmentName - $this.releaseEnvironment.name - - - shortApprovalType - $this.approvalType.Replace('Deploy', '').Replace('p', 'P') - - - PSStandardMembers - - - DefaultDisplayPropertySet - - id - approverName - status - comments - releaseDefinitionName - releaseEnvironmentName - - - - - - - - - Team.GitRepository - - - projectName - $this.project.name - - - PSStandardMembers - - - DefaultDisplayPropertySet - - name - remoteUrl - defaultBranch - projectName - - - - - - + + + + + Team.Team + + + PSStandardMembers + + + DefaultDisplayPropertySet + + projectname + name + id + description + + + + + + + + + Team.TeamMember + + + PSStandardMembers + + + DefaultDisplayPropertySet + + team + displayName + id + uniqueName + + + + + + + + + Team.Build + + + name + buildNumber + + + queueName + $this.queue.name + + + queueID + $this.queue.id + + + definitionName + $this.definition.name + + + projectName + $this.project.name + + + repositoryType + $this.repository.type + + + requestedForUser + $this.requestedFor.displayName + + + requestedByUser + $this.requestedBy.displayName + + + lastChangedByUser + $this.lastChangedBy.displayName + + + + + + Team.Release + + + definitionName + $this.releaseDefinition.name + + + definitionId + $this.releaseDefinition.id + + + projectId + $this.projectReference.id + + + requestedForUser + $this.requestedFor.displayName + + + modifiedByUser + $this.modifiedBy.displayName + + + createdByUser + $this.createdBy.displayName + + + PSStandardMembers + + + DefaultDisplayPropertySet + + id + name + status + definitionName + + + + + + + + + Team.Queue + + + poolName + $this.pool.name + + + + + + Team.ServiceEndpoint + + + createdByUser + $this.createdBy.displayName + + + + + + Team.AzureSubscription + + + PSStandardMembers + + + DefaultDisplayPropertySet + + displayName + subscriptionId + subscriptionTenantId + + + + + + + + + Team.BuildDefinition + + + authoredByUser + $this.authoredBy.displayName + + + queueName + $this.queue.name + + + PSStandardMembers + + + DefaultDisplayPropertySet + + id + name + queueName + authoredByUser + + + + + + + + + Team.Pool + + + createdByUser + $this.createdBy.displayName + + + queueName + $this.queue.name + + + PSStandardMembers + + + DefaultDisplayPropertySet + + name + createdByUser + + + + + + + + + Team.ReleaseDefinition + + + createdByUser + $this.createdBy.displayName + + + PSStandardMembers + + + DefaultDisplayPropertySet + + id + name + createdByUser + + + + + + + + + Team.Approval + + + approverName + $this.approver.displayName + + + releaseName + $this.release.name + + + releaseDefinitionName + $this.releaseDefinition.name + + + releaseEnvironmentName + $this.releaseEnvironment.name + + + shortApprovalType + $this.approvalType.Replace('Deploy', '').Replace('p', 'P') + + + PSStandardMembers + + + DefaultDisplayPropertySet + + id + approverName + status + comments + releaseDefinitionName + releaseEnvironmentName + + + + + + + + + Team.GitRepository + + + projectName + $this.project.name + + + PSStandardMembers + + + DefaultDisplayPropertySet + + name + remoteUrl + defaultBranch + projectName + + + + + + \ No newline at end of file diff --git a/test/teammembers.Tests.ps1 b/test/teammembers.Tests.ps1 index 388f99660..78492d47d 100644 --- a/test/teammembers.Tests.ps1 +++ b/test/teammembers.Tests.ps1 @@ -2,6 +2,7 @@ Set-StrictMode -Version Latest Get-Module team | Remove-Module -Force Import-Module $PSScriptRoot\..\src\teammembers.psm1 -Force +Import-Module $PSScriptRoot\..\src\teams.psm1 -Force InModuleScope teammembers { $env:TEAM_ACCT = 'https://test.visualstudio.com' @@ -56,6 +57,19 @@ InModuleScope teammembers { } } } + + Context 'Get-Teammember for specific team, fed through pipeline' { + Mock Get-Team { return New-Object -TypeName PSObject -Prop @{projectname="TestProject"; name="TestTeam"} } + Mock Invoke-RestMethod { return @{value='teammembers'}} + + It 'Should return teammembers' { + Get-Team -ProjectName TestProject -TeamId "TestTeam" | Get-TeamMember + + Assert-MockCalled Invoke-RestMethod -Exactly 1 -ParameterFilter { + $Uri -eq 'https://test.visualstudio.com/_apis/projects/TestProject/teams/TestTeam/members?api-version=1.0' + } + } + } } } \ No newline at end of file diff --git a/test/teams.Tests.ps1 b/test/teams.Tests.ps1 index f6e36b3ac..9944893fe 100644 --- a/test/teams.Tests.ps1 +++ b/test/teams.Tests.ps1 @@ -154,11 +154,28 @@ InModuleScope teams { } } + Context 'Update-Team, fed through pipeline' { + Mock Get-Team { return New-Object -TypeName PSObject -Prop @{projectname="TestProject"; name="OldTeamName"} } + Mock Invoke-RestMethod { return @{value='teams'}} + + It 'Should update the team' { + Get-Team -ProjectName TestProject -TeamId "OldTeamName" | Update-Team -NewTeamName "NewTeamName" -Description "New Description" + + $expectedBody = '{ "name": "NewTeamName", "description": "New Description" }' + + Assert-MockCalled Invoke-RestMethod -Exactly 1 -ParameterFilter { + $Uri -eq 'https://test.visualstudio.com/_apis/projects/TestProject/teams/OldTeamName?api-version=1.0' -and + $Method -eq "Patch" -and + $Body -eq $expectedBody + } + } + } + Context 'Remove-Team' { Mock Invoke-RestMethod { return @{value='teams'}} It 'Should remove the team' { - Remove-Team -ProjectName Test -TeamId "TestTeam" + Remove-Team -ProjectName Test -TeamId "TestTeam" -Force Assert-MockCalled Invoke-RestMethod -Exactly 1 -ParameterFilter { $Uri -eq 'https://test.visualstudio.com/_apis/projects/Test/teams/TestTeam?api-version=1.0' -and @@ -166,6 +183,20 @@ InModuleScope teams { } } } + + Context 'Remove-Team, fed through pipeline' { + Mock Get-Team { return New-Object -TypeName PSObject -Prop @{projectname="TestProject"; name="TestTeam"} } + Mock Invoke-RestMethod { return @{value='teams'}} + + It 'Should remove the team' { + Get-Team -ProjectName TestProject -TeamId "TestTeam" | Remove-Team -Force + + Assert-MockCalled Invoke-RestMethod -Exactly 1 -ParameterFilter { + $Uri -eq 'https://test.visualstudio.com/_apis/projects/TestProject/teams/TestTeam?api-version=1.0' -and + $Method -eq "Delete" + } + } + } } } \ No newline at end of file