WorkItemQuery/WorkItemQuery.psm1
Function _FindQueryFolder($folder, $parent) { Write-Verbose "_FindQueryFolder: Searching for $folder under $($parent.Path)" if ($folder -is [Microsoft.TeamFoundation.WorkItemTracking.Client.QueryFolder]) { Write-Verbose "_FindQueryFolder: Returning folder immediately, since it's a QueryFolder object" return $folder } $folders = $parent | ? {$_ -Is [Microsoft.TeamFoundation.WorkItemTracking.Client.QueryFolder]} foreach($f in $folders) { if (($f.Path -like $folder) -or ($f.Name -like $folder)) { Write-Verbose "_FindQueryFolder: Found folder `"$($f.Path)`" matching `"$folder`"" return @{$f.Name = $f} } } foreach($f in $folders) { Write-Verbose "_FindQueryFolder: Starting recursive search" $result = _FindQueryFolder $folder $f if ($result) { return $result } } } Function _FindQuery($path, $parent) { Write-Verbose "_FindQuery: Searching for $path under $($parent.Path)" foreach($item in $parent) { if (($item -Is [Microsoft.TeamFoundation.WorkItemTracking.Client.QueryDefinition]) -and (($item.Path -like $path) -or ($item.Name -like $path))) { # Search immediate children Write-Verbose "_FindQuery: Found local query `"$($item.Path)`" matching `"$path`"" $item } elseif ($item -Is [Microsoft.TeamFoundation.WorkItemTracking.Client.QueryFolder]) { # Search descendants recursively Write-Verbose "_FindQuery: Starting recursive search" _FindQuery $path $item } else { Write-Verbose "_FindQuery: Skipped `"$($item.Path)`" since it doesn't match $path" } } } Function _NormalizeQueryPath($Path, $ProjectName) { if([string]::IsNullOrWhiteSpace($Path)) { return [string]::Empty } $newPath = [System.Text.RegularExpressions.Regex]::Replace($Path, '//{2,}', '/') if ($newPath.StartsWith("/")) { $newPath = $newPath.Substring(1) } if ($newPath.EndsWith('/')) { $newPath = $newPath.Substring(0, $newPath.Length-1) } if ($newPath -notlike "$ProjectName*") { $newPath = "$ProjectName/$newPath" } return $newPath } <# .SYNOPSIS Exports a saved work item query to XML. .PARAMETER Collection Specifies either a URL/name of the Team Project Collection to connect to, or a previously initialized TfsTeamProjectCollection object. When using a URL, it must be fully qualified. The format of this string is as follows: http[s]://<ComputerName>:<Port>/[<TFS-vDir>/]<CollectionName> Valid values for the Transport segment of the URI are HTTP and HTTPS. If you specify a connection URI with a Transport segment, but do not specify a port, the session is created with standards ports: 80 for HTTP and 443 for HTTPS. To connect to a Team Project Collection by using its name, a TfsConfigurationServer object must be supplied either via -Server argument or via a previous call to the Connect-TfsConfigurationServer cmdlet. For more details, see the Get-TfsTeamProjectCollection cmdlet. .INPUTS Microsoft.TeamFoundation.Client.TfsTeamProjectCollection System.String System.Uri #> Function Export-TfsWorkItemQuery { [CmdletBinding()] [OutputType([xml])] Param ( [Parameter(ValueFromPipeline=$true)] [SupportsWildcards()] [string] $Query = "*", [Parameter()] [string] $Folder, [Parameter()] [string] $Destination, [Parameter()] [string] $Encoding = "UTF-8", [Parameter()] [object] $Project, [Parameter()] [object] $Collection ) Process { if ($Destination -and (-not (Test-Path $Destination -PathType Container))) { throw "Invalid destination path $Destination" } $queries = Get-TfsWorkItemQuery -Query $Query -Folder $Folder -Project $Project -Collection $Collection if (-not $queries) { throw "Query path `"$Query`" is invalid or missing." } foreach($q in $queries) { $xml = [xml] @" <?xml version="1.0" encoding="$Encoding"?> <!-- Original Query Path: $($q.Path) --> <WorkItemQuery Version="1"> <TeamFoundationServer>$($q.Project.Store.TeamProjectCollection.Uri)</TeamFoundationServer> <TeamProject>$($q.Project.Name)</TeamProject> <Wiql><![CDATA[$($q.QueryText)]]></Wiql> </WorkItemQuery> "@ if (-not $Destination) { $xml } else { $queryPath = $q.Path.Substring($q.Path.IndexOf('/')+1) $fileName = Join-Path $Destination "$queryPath.wiql" $filePath = Split-Path $fileName -Parent if (-not (Test-Path $filePath -PathType Container)) { md $filePath -Force | Out-Null } $xml.Save($fileName) } } } } <# .SYNOPSIS Gets the definition of one or more work item saved queries. .PARAMETER Query Specifies the path of a saved query. Wildcards are supported. .PARAMETER Project Specifies either the name of the Team Project or a previously initialized Microsoft.TeamFoundation.WorkItemTracking.Client.Project object to connect to. If omitted, it defaults to the connection opened by Connect-TfsTeamProject (if any). For more details, see the Get-TfsTeamProject cmdlet. .PARAMETER Collection Specifies either a URL/name of the Team Project Collection to connect to, or a previously initialized TfsTeamProjectCollection object. When using a URL, it must be fully qualified. The format of this string is as follows: http[s]://<ComputerName>:<Port>/[<TFS-vDir>/]<CollectionName> Valid values for the Transport segment of the URI are HTTP and HTTPS. If you specify a connection URI with a Transport segment, but do not specify a port, the session is created with standards ports: 80 for HTTP and 443 for HTTPS. To connect to a Team Project Collection by using its name, a TfsConfigurationServer object must be supplied either via -Server argument or via a previous call to the Connect-TfsConfigurationServer cmdlet. For more details, see the Get-TfsTeamProjectCollection cmdlet. .INPUTS Microsoft.TeamFoundation.WorkItemTracking.Client.Project System.String #> Function Get-TfsWorkItemQuery { [CmdletBinding()] [OutputType([Microsoft.TeamFoundation.WorkItemTracking.Client.QueryDefinition])] Param ( [Parameter(Position=0)] [ValidateNotNull()] [SupportsWildcards()] [Alias("Path")] [object] $Query = '*', [Parameter()] $Folder, [Parameter(ValueFromPipeline=$true)] [object] $Project, [Parameter()] [object] $Collection ) Process { if($Query -is [Microsoft.TeamFoundation.WorkItemTracking.Client.QueryDefinition]) { return $Query } $tp = Get-TfsTeamProject -Project $Project -Collection $Collection $tpc = $tp.Store.TeamProjectCollection if ($Folder) { if (($Folder -is [string]) -and ($Folder -notlike "$($tp.Name)/*")) { $Folder = _NormalizeQueryPath $Folder $tp.Name } Write-Verbose "Get-TfsWorkItemQuery: Limiting search to folder $Folder" $folders = (_FindQueryFolder $Folder $tp.QueryHierarchy) if (-not $folders) { throw "Query folder $Folder is invalid or missing. Be sure you provided the full path (e.g. 'Shared Queries/Current Iteration') instead of just the folder name ('Current Iteration')" } $root = $folders.Values[0] } else { Write-Verbose "Get-TfsWorkItemQuery: -Folder argument missing. Searching entire team project" $root = $tp.QueryHierarchy } return _FindQuery $Query $root } } <# .SYNOPSIS Create a new work items query in the given Team Project. .PARAMETER Query Specifies the path of the new work item query. When supplying a path, use a slash ("/") between the path segments. Leading and trailing backslashes are optional. The last segment in the path will be the area name. .PARAMETER Project Specifies either the name of the Team Project or a previously initialized Microsoft.TeamFoundation.WorkItemTracking.Client.Project object to connect to. If omitted, it defaults to the connection opened by Connect-TfsTeamProject (if any). For more details, see the Get-TfsTeamProject cmdlet. .PARAMETER Collection Specifies either a URL/name of the Team Project Collection to connect to, or a previously initialized TfsTeamProjectCollection object. When using a URL, it must be fully qualified. The format of this string is as follows: http[s]://<ComputerName>:<Port>/[<TFS-vDir>/]<CollectionName> Valid values for the Transport segment of the URI are HTTP and HTTPS. If you specify a connection URI with a Transport segment, but do not specify a port, the session is created with standards ports: 80 for HTTP and 443 for HTTPS. To connect to a Team Project Collection by using its name, a TfsConfigurationServer object must be supplied either via -Server argument or via a previous call to the Connect-TfsConfigurationServer cmdlet. For more details, see the Get-TfsTeamProjectCollection cmdlet. .INPUTS System.String #> Function New-TfsWorkItemQuery { [CmdletBinding()] [OutputType([Microsoft.TeamFoundation.WorkItemTracking.Client.QueryDefinition])] Param ( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] [Alias("Name")] [Alias("Path")] [string] $Query, [Parameter()] [string] $Folder, [Parameter()] [string] $Definition, [Parameter()] [object] $Project, [Parameter()] [object] $Collection ) Process { $tp = Get-TfsTeamProject -Project $Project -Collection $Collection $tpc = $tp.Store.TeamProjectCollection $store = $tp.Store $Query = _NormalizeQueryPath "$Folder/$Query" $tp.Name $folderPath = (Split-Path $Query -Parent) -replace ('\\', '/') $queryName = (Split-Path $Query -Leaf) Write-Verbose "New-TfsWorkItemQuery: Creating query '$queryName' in folder '$folderPath'" $folder = (_FindQueryFolder $folderPath $tp.QueryHierarchy $true) if (-not $folder) { throw "Invalid or non-existent work item query folder $folderPath." } if ($Definition -match "select \*") { Write-Warning "Queries containing 'SELECT *' may not work in Visual Studio. Consider replacing * with a list of fields." } $q = New-Object 'Microsoft.TeamFoundation.WorkItemTracking.Client.QueryDefinition' -ArgumentList $queryName, $Definition $folder.Values[0].Add($q) $tp.QueryHierarchy.Save() return $q } } <# .SYNOPSIS Deletes one or more work item queries from the specified Team Project.. .PARAMETER Query Specifies the path of a saved query. Wildcards are supported. .PARAMETER Project Specifies either the name of the Team Project or a previously initialized Microsoft.TeamFoundation.WorkItemTracking.Client.Project object to connect to. If omitted, it defaults to the connection opened by Connect-TfsTeamProject (if any). For more details, see the Get-TfsTeamProject cmdlet. .PARAMETER Collection Specifies either a URL/name of the Team Project Collection to connect to, or a previously initialized TfsTeamProjectCollection object. When using a URL, it must be fully qualified. The format of this string is as follows: http[s]://<ComputerName>:<Port>/[<TFS-vDir>/]<CollectionName> Valid values for the Transport segment of the URI are HTTP and HTTPS. If you specify a connection URI with a Transport segment, but do not specify a port, the session is created with standards ports: 80 for HTTP and 443 for HTTPS. To connect to a Team Project Collection by using its name, a TfsConfigurationServer object must be supplied either via -Server argument or via a previous call to the Connect-TfsConfigurationServer cmdlet. For more details, see the Get-TfsTeamProjectCollection cmdlet. .INPUTS Microsoft.TeamFoundation.WorkItemTracking.Client.Project System.String #> Function Remove-TfsWorkItemQuery { [CmdletBinding(ConfirmImpact='High', SupportsShouldProcess=$true)] Param ( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] [Alias("Path")] [ValidateNotNull()] [object] $Query, [Parameter()] [object] $Project, [Parameter()] [object] $Collection ) Process { $queries = Get-TfsWorkItemQuery -Query $Query -Project $Project -Collection $Collection foreach($q in $queries) { if ($PSCmdlet.ShouldProcess($q.Path, "Delete Query")) { $q.Delete() } } } } <# .SYNOPSIS Changes the value of a property of an Area. .PARAMETER Area Specifies the name, URI or path of an Area. Wildcards are permitted. If omitted, all Areas in the given Team Project are returned. To supply a path, use a backslash ('\') between the path segments. Leading and trailing backslashes are optional. When supplying a URI, use URIs in the form of 'vstfs:///Classification/Node/<GUID>' (where <GUID> is the unique identifier of the given node) .PARAMETER NewName Specifies the new name of the area. Enter only a name, not a path and name. If you enter a path that is different from the path that is specified in the area parameter, Rename-Tfsarea generates an error. To rename and move an item, use the Move-Tfsarea cmdlet. .PARAMETER Project Specifies either the name of the Team Project or a previously initialized Microsoft.TeamFoundation.WorkItemTracking.Client.Project object to connect to. If omitted, it defaults to the connection opened by Connect-TfsTeamProject (if any). For more details, see the Get-TfsTeamProject cmdlet. .PARAMETER Collection Specifies either a URL/name of the Team Project Collection to connect to, or a previously initialized TfsTeamProjectCollection object. When using a URL, it must be fully qualified. The format of this string is as follows: http[s]://<ComputerName>:<Port>/[<TFS-vDir>/]<CollectionName> Valid values for the Transport segment of the URI are HTTP and HTTPS. If you specify a connection URI with a Transport segment, but do not specify a port, the session is created with standards ports: 80 for HTTP and 443 for HTTPS. To connect to a Team Project Collection by using its name, a TfsConfigurationServer object must be supplied either via -Server argument or via a previous call to the Connect-TfsConfigurationServer cmdlet. For more details, see the Get-TfsTeamProjectCollection cmdlet. .INPUTS Microsoft.TeamFoundation.WorkItemTracking.Client.Project System.String #> Function Rename-TfsWorkItemQuery { [CmdletBinding()] [OutputType([Microsoft.TeamFoundation.WorkItemTracking.Client.QueryDefinition])] Param ( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] [ValidateNotNull()] [Alias("Path")] [object] $Query, [Parameter()] [string] $NewName, [Parameter()] [string] $Definition, [Parameter()] [object] $Project, [Parameter()] [object] $Collection ) Process { Set-TfsWorkItemQuery -Query $Query -NewName $NewName -Project $Project -Collection $Collection } } <# .SYNOPSIS Changes the value of a property of a work item query. .PARAMETER Query Specifies the path of a work item saved query. .PARAMETER NewName Specifies the new name of the query. Enter only a name, not a path and name. If you enter a path that is different from the path that is specified in the area parameter, Rename-TfsWorkItemQuery generates an error. To rename and move an item, use the Move-TfsWorkItemQuery cmdlet instead. .PARAMETER Project Specifies either the name of the Team Project or a previously initialized Microsoft.TeamFoundation.WorkItemTracking.Client.Project object to connect to. If omitted, it defaults to the connection opened by Connect-TfsTeamProject (if any). For more details, see the Get-TfsTeamProject cmdlet. .PARAMETER Collection Specifies either a URL/name of the Team Project Collection to connect to, or a previously initialized TfsTeamProjectCollection object. When using a URL, it must be fully qualified. The format of this string is as follows: http[s]://<ComputerName>:<Port>/[<TFS-vDir>/]<CollectionName> Valid values for the Transport segment of the URI are HTTP and HTTPS. If you specify a connection URI with a Transport segment, but do not specify a port, the session is created with standards ports: 80 for HTTP and 443 for HTTPS. To connect to a Team Project Collection by using its name, a TfsConfigurationServer object must be supplied either via -Server argument or via a previous call to the Connect-TfsConfigurationServer cmdlet. For more details, see the Get-TfsTeamProjectCollection cmdlet. .INPUTS Microsoft.TeamFoundation.WorkItemTracking.Client.QueryDefinition System.String #> Function Set-TfsWorkItemQuery { [CmdletBinding()] [OutputType([Microsoft.TeamFoundation.WorkItemTracking.Client.QueryDefinition])] Param ( [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] [ValidateNotNull()] [Alias("Path")] [object] $Query, [Parameter()] [string] $NewName, [Parameter()] [string] $Definition, [Parameter()] [object] $Project, [Parameter()] [object] $Collection ) Process { $q = Get-TfsWorkItemQuery -Query $Query -Project $Project -Collection $Collection if (-not $q) { throw "Invalid or non-existent work item query $queries" } if ($q.Count -ne 1) { throw "Ambiguous query name '$Query'. $($q.Count) queries were found matching the specified name/pattern:`n`n - " + ($q -join "`n - ") } if ($NewName) { $q.Name = $NewName } if ($Definition) { $q.QueryText = $Definition } $q.Project.QueryHierarchy.Save() return $q } } |