From c92ef9fca6366e4b3be8986d121d1ad7c565f04f Mon Sep 17 00:00:00 2001 From: James Brundage Date: Mon, 12 Jul 2021 12:38:59 -0700 Subject: [PATCH 01/15] Get-ADOWorkItem: Support for -SharedQuery (#117) --- Get-ADOWorkItem.ps1 | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/Get-ADOWorkItem.ps1 b/Get-ADOWorkItem.ps1 index 1044c3fa..082c2bc5 100644 --- a/Get-ADOWorkItem.ps1 +++ b/Get-ADOWorkItem.ps1 @@ -93,6 +93,7 @@ # If provided, will only return the first N results from a query. [Parameter(ParameterSetName='/{Organization}/{Project}/{Team}/_apis/wit/wiql',ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName='/{Organization}/{Project}/_apis/wit/queries',ValueFromPipelineByPropertyName)] [Alias('Top')] [uint32] $First, @@ -103,6 +104,32 @@ [switch] $WorkItemType, + # If set, will return work item shared queries + [Parameter(Mandatory,ParameterSetName='/{Organization}/{Project}/_apis/wit/queries',ValueFromPipelineByPropertyName)] + [switch] + $SharedQuery, + + # If set, will return shared queries that have been deleted. + [Parameter(ParameterSetName='/{Organization}/{Project}/_apis/wit/queries',ValueFromPipelineByPropertyName)] + [switch] + $IncludeDeleted, + + # If provided, will return shared queries up to a given depth. + [Parameter(ParameterSetName='/{Organization}/{Project}/_apis/wit/queries',ValueFromPipelineByPropertyName)] + [int] + $Depth, + + # If provided, will filter the shared queries returned + [Parameter(ParameterSetName='/{Organization}/{Project}/_apis/wit/queries',ValueFromPipelineByPropertyName)] + [int] + $SharedQueryFilter, + + # Determines how data from shared queries will be expanded. By default, expands all data. + [Parameter(ParameterSetName='/{Organization}/{Project}/_apis/wit/queries',ValueFromPipelineByPropertyName)] + [ValidateSet('All','Clauses','Minimal','None', 'Wiql')] + [string] + $ExpandSharedQuery = 'All', + # One or more fields. [Alias('Fields','Select')] [string[]] @@ -181,6 +208,20 @@ $selfSplat.Query = "Select [System.ID] from WorkItems Where [System.Title] contains '$title'" Get-ADOWorkItem @selfSplat } + elseif ($psCmdlet.ParameterSetName -eq '/{Organization}/{Project}/_apis/wit/queries') { + $myInvokeParams = @{} + $invokeParams + $myInvokeParams.Url = "$Server".TrimEnd('/') + $psCmdlet.ParameterSetName + + $myInvokeParams.QueryParameter = @{'$expand'= $ExpandSharedQuery} + $myInvokeParams.UrlParameter = @{} + $psBoundParameters + if ($IncludeDeleted) { $myInvokeParams.QueryParameter.'$includeDeleted' = $true } + if ($First) { $myInvokeParams.QueryParameter.'$top' = $First} + Invoke-ADORestAPI @myInvokeParams -PSTypeName @( + "$Organization.$Project.SharedQuery" # * $Organization.$Project.SharedQuery + "$Organization.SharedQuery" # * $Organization.SharedQuery + "PSDevOps.SharedQuery" # * PSDevOps.SharedQuery + ) + } elseif ( $PSCmdlet.ParameterSetName -in '/{Organization}/{Project}/_apis/wit/workitems/{id}', @@ -198,7 +239,7 @@ elseif ($PSCmdlet.ParameterSetName -eq '/{Organization}/{Project}/{Team}/_apis/wit/wiql') { $uri = "$Server".TrimEnd('/') + (. $ReplaceRouteParameter $PSCmdlet.ParameterSetName) + '?' - $uri += + $uri += @(if ($First) { "`$top=$First" } From c78d920dd14b8718d95571d17333ccd6a8d3f4d0 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Mon, 12 Jul 2021 12:40:14 -0700 Subject: [PATCH 02/15] Adding type definition for PSDevOps.SharedQuery --- Types/PSDevOps.SharedQuery/Alias.psd1 | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Types/PSDevOps.SharedQuery/Alias.psd1 diff --git a/Types/PSDevOps.SharedQuery/Alias.psd1 b/Types/PSDevOps.SharedQuery/Alias.psd1 new file mode 100644 index 00000000..6aa24e7d --- /dev/null +++ b/Types/PSDevOps.SharedQuery/Alias.psd1 @@ -0,0 +1,3 @@ +@{ + QueryID = 'ID' +} From 3cb9c5200c6b922b03f273e522d8ba98b012aef8 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Mon, 12 Jul 2021 12:40:48 -0700 Subject: [PATCH 03/15] Updating types file --- PSDevOps.types.ps1xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/PSDevOps.types.ps1xml b/PSDevOps.types.ps1xml index 2fec89db..78d9f20f 100644 --- a/PSDevOps.types.ps1xml +++ b/PSDevOps.types.ps1xml @@ -1083,6 +1083,24 @@ $bitMask + + PSDevOps.SharedQuery + + + QueryID + ID + + + + + Deserialized.PSDevOps.SharedQuery + + + QueryID + ID + + + PSDevOps.State From 22a4bb0dbc17580fb6cb08f61a647c3b2c602497 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Tue, 13 Jul 2021 01:43:05 -0700 Subject: [PATCH 04/15] Get-ADOWorkItem: Passing along .Organization and .Project in additional scenarios --- Get-ADOWorkItem.ps1 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Get-ADOWorkItem.ps1 b/Get-ADOWorkItem.ps1 index 082c2bc5..814e31ea 100644 --- a/Get-ADOWorkItem.ps1 +++ b/Get-ADOWorkItem.ps1 @@ -211,11 +211,12 @@ elseif ($psCmdlet.ParameterSetName -eq '/{Organization}/{Project}/_apis/wit/queries') { $myInvokeParams = @{} + $invokeParams $myInvokeParams.Url = "$Server".TrimEnd('/') + $psCmdlet.ParameterSetName - + $myInvokeParams.QueryParameter = @{'$expand'= $ExpandSharedQuery} $myInvokeParams.UrlParameter = @{} + $psBoundParameters if ($IncludeDeleted) { $myInvokeParams.QueryParameter.'$includeDeleted' = $true } - if ($First) { $myInvokeParams.QueryParameter.'$top' = $First} + if ($First) { $myInvokeParams.QueryParameter.'$top' = $First} + $myInvokeParams.Property = @{Organization = $Organization;Project=$Project} Invoke-ADORestAPI @myInvokeParams -PSTypeName @( "$Organization.$Project.SharedQuery" # * $Organization.$Project.SharedQuery "$Organization.SharedQuery" # * $Organization.SharedQuery @@ -239,7 +240,7 @@ elseif ($PSCmdlet.ParameterSetName -eq '/{Organization}/{Project}/{Team}/_apis/wit/wiql') { $uri = "$Server".TrimEnd('/') + (. $ReplaceRouteParameter $PSCmdlet.ParameterSetName) + '?' - $uri += + $uri += @(if ($First) { "`$top=$First" } @@ -302,6 +303,7 @@ "api-version=$ApiVersion" } $invokeParams.Uri = $uri + $invokeParams.Property = @{Organization = $Organization} $workItemTypes = Invoke-ADORestAPI @invokeParams $workItemTypes -replace '"":', '"_blank":' | ConvertFrom-Json | From 94a161b649c7efcede4bd017db79554a879b2114 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Tue, 13 Jul 2021 01:43:41 -0700 Subject: [PATCH 05/15] Remove-ADOWorkItem: Support for SharedQueries (-QueryID). Fixes #117. --- Remove-ADOWorkItem.ps1 | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Remove-ADOWorkItem.ps1 b/Remove-ADOWorkItem.ps1 index 7f02eb33..84a13eb6 100644 --- a/Remove-ADOWorkItem.ps1 +++ b/Remove-ADOWorkItem.ps1 @@ -38,6 +38,12 @@ [string] $Query, + # If set, will return work item shared queries + [Parameter(Mandatory,ParameterSetName='/{Organization}/{Project}/_apis/wit/queries/{QueryID}',ValueFromPipelineByPropertyName)] + [string] + $QueryID, + + # The server. By default https://dev.azure.com/. # To use against TFS, provide the tfs server URL (e.g. http://tfsserver:8080/tfs). [Parameter(ValueFromPipelineByPropertyName)] @@ -58,7 +64,12 @@ } process { - if ($PSCmdlet.ParameterSetName -eq 'ByID') { # If we're removing by ID + $psParameterSet = $PSCmdlet.ParameterSetName + $in = $_ + if ($in.QueryID) { + $psParameterSet = '/{Organization}/{Project}/_apis/wit/queries/{QueryID}' + } + if ($psParameterSet -eq 'ByID') { # If we're removing by ID $uriBase = "$Server".TrimEnd('/'), $Organization, $Project -join '/' $uri = $uriBase, "_apis/wit/workitems", "${ID}?" -join '/' @@ -75,7 +86,7 @@ $invokeParams.Method = 'DELETE' if (-not $PSCmdlet.ShouldProcess("Remove Work Item $ID")) { return } Invoke-ADORestAPI @invokeParams - } elseif ($PSCmdlet.ParameterSetName -eq 'ByQuery') { + } elseif ($psParameterSet -eq 'ByQuery') { $uri = "$Server".TrimEnd('/'), $Organization, $Project, "_apis/wit/wiql?" -join '/' @@ -99,5 +110,15 @@ Write-Progress "Updating Work Items" "Complete" -Completed -Id $progId } + elseif ($psParameterSet -eq '/{Organization}/{Project}/_apis/wit/queries/{QueryID}') { + $invokeParams.Method = "DELETE" + $invokeParams["Uri"] = "$Server".TrimEnd('/') + $psParameterSet + $invokeParams.QueryParameter = @{"api-version"="$ApiVersion"} + if ($WhatIfPreference) { + $invokeParams.Remove('PersonalAccessToken') + return $invokeParams + } + Invoke-ADORestAPi @invokeParams + } } } From d158318cb67e81a1adb2f76d7bf0f5a935a3624f Mon Sep 17 00:00:00 2001 From: James Brundage Date: Sat, 25 Sep 2021 23:39:09 -0700 Subject: [PATCH 06/15] Get-ADOWorkItem: Support for -Depth with -SharedQuery --- Get-ADOWorkItem.ps1 | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Get-ADOWorkItem.ps1 b/Get-ADOWorkItem.ps1 index 814e31ea..86a021a5 100644 --- a/Get-ADOWorkItem.ps1 +++ b/Get-ADOWorkItem.ps1 @@ -116,6 +116,7 @@ # If provided, will return shared queries up to a given depth. [Parameter(ParameterSetName='/{Organization}/{Project}/_apis/wit/queries',ValueFromPipelineByPropertyName)] + [ValidateRange(0,2)] [int] $Depth, @@ -215,13 +216,18 @@ $myInvokeParams.QueryParameter = @{'$expand'= $ExpandSharedQuery} $myInvokeParams.UrlParameter = @{} + $psBoundParameters if ($IncludeDeleted) { $myInvokeParams.QueryParameter.'$includeDeleted' = $true } - if ($First) { $myInvokeParams.QueryParameter.'$top' = $First} + if ($First) { $myInvokeParams.QueryParameter.'$top' = $First} + if ($Depth) { $myInvokeParams.QueryParameter.'$depth' = $Depth} $myInvokeParams.Property = @{Organization = $Organization;Project=$Project} Invoke-ADORestAPI @myInvokeParams -PSTypeName @( "$Organization.$Project.SharedQuery" # * $Organization.$Project.SharedQuery "$Organization.SharedQuery" # * $Organization.SharedQuery "PSDevOps.SharedQuery" # * PSDevOps.SharedQuery - ) + ) -DecorateProperty @{Children= + "$Organization.$Project.SharedQuery", # * $Organization.$Project.SharedQuery + "$Organization.SharedQuery", # * $Organization.SharedQuery + "PSDevOps.SharedQuery" # * PSDevOps.SharedQuery + } } elseif ( $PSCmdlet.ParameterSetName -in From 05f1166bf7e46aeed6e49fdee7fba6ef2f9b7e09 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Mon, 27 Sep 2021 19:57:19 -0700 Subject: [PATCH 07/15] Get-ADOWorkItem: Unrolling results for Shared Queries --- Get-ADOWorkItem.ps1 | 45 +++++++++++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 14 deletions(-) diff --git a/Get-ADOWorkItem.ps1 b/Get-ADOWorkItem.ps1 index 86a021a5..ac79b474 100644 --- a/Get-ADOWorkItem.ps1 +++ b/Get-ADOWorkItem.ps1 @@ -198,6 +198,31 @@ } #endregion Output Work Item + + #region ExpandSharedQueries + $expandSharedQueries = { + param([Parameter(ValueFromPipeline)]$node) + process { + if (-not $node) { return } + $node.pstypenames.clear() + foreach ($typeName in "$organization.SharedQuery", + "$organization.$Project.SharedQuery", + "PSDevOps.SharedQuery" + ) { + $node.pstypenames.Add($typeName) + } + $node | + Add-Member NoteProperty Organization $organization -Force -PassThru | + Add-Member NoteProperty Project $Project -Force -PassThru | + Add-Member NoteProperty Server $Server -Force -PassThru + if ($node.haschildren) { + $node.children | + & $MyInvocation.MyCommand.ScriptBlock + } + } + } + #endregion ExpandSharedQueries + $allIDS = [Collections.ArrayList]::new() } @@ -212,22 +237,15 @@ elseif ($psCmdlet.ParameterSetName -eq '/{Organization}/{Project}/_apis/wit/queries') { $myInvokeParams = @{} + $invokeParams $myInvokeParams.Url = "$Server".TrimEnd('/') + $psCmdlet.ParameterSetName - + $myInvokeParams.QueryParameter = @{'$expand'= $ExpandSharedQuery} $myInvokeParams.UrlParameter = @{} + $psBoundParameters if ($IncludeDeleted) { $myInvokeParams.QueryParameter.'$includeDeleted' = $true } - if ($First) { $myInvokeParams.QueryParameter.'$top' = $First} - if ($Depth) { $myInvokeParams.QueryParameter.'$depth' = $Depth} + if ($First) { $myInvokeParams.QueryParameter.'$top' = $First} + if ($Depth) { $myInvokeParams.QueryParameter.'$depth' = $Depth} $myInvokeParams.Property = @{Organization = $Organization;Project=$Project} - Invoke-ADORestAPI @myInvokeParams -PSTypeName @( - "$Organization.$Project.SharedQuery" # * $Organization.$Project.SharedQuery - "$Organization.SharedQuery" # * $Organization.SharedQuery - "PSDevOps.SharedQuery" # * PSDevOps.SharedQuery - ) -DecorateProperty @{Children= - "$Organization.$Project.SharedQuery", # * $Organization.$Project.SharedQuery - "$Organization.SharedQuery", # * $Organization.SharedQuery - "PSDevOps.SharedQuery" # * PSDevOps.SharedQuery - } + Invoke-ADORestAPI @myInvokeParams | & $expandSharedQueries + return } elseif ( $PSCmdlet.ParameterSetName -in @@ -246,7 +264,7 @@ elseif ($PSCmdlet.ParameterSetName -eq '/{Organization}/{Project}/{Team}/_apis/wit/wiql') { $uri = "$Server".TrimEnd('/') + (. $ReplaceRouteParameter $PSCmdlet.ParameterSetName) + '?' - $uri += + $uri += @(if ($First) { "`$top=$First" } @@ -262,7 +280,6 @@ $realQuery += ' AND ' } - $realQuery += @( if ($Project) { From 4e6b72a2cb72430a3669a43f4a626853754d1924 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Mon, 27 Sep 2021 19:58:26 -0700 Subject: [PATCH 08/15] New-ADOWorkItem: Support for Shared Queries (Fixing #117). --- New-ADOWorkItem.ps1 | 129 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 118 insertions(+), 11 deletions(-) diff --git a/New-ADOWorkItem.ps1 b/New-ADOWorkItem.ps1 index 9128a4a5..9768f48e 100644 --- a/New-ADOWorkItem.ps1 +++ b/New-ADOWorkItem.ps1 @@ -11,22 +11,55 @@ .Link Invoke-ADORestAPI #> - [CmdletBinding(DefaultParameterSetName='ByID',SupportsShouldProcess=$true)] + [CmdletBinding(DefaultParameterSetName='WorkItem',SupportsShouldProcess=$true)] [OutputType('PSDevOps.WorkItem')] param( # The InputObject - [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)] + [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName,ParameterSetName='WorkItem')] [PSObject] $InputObject, # The type of the work item. - [Parameter(Mandatory, ValueFromPipelineByPropertyName)] + [Parameter(Mandatory, ParameterSetName='WorkItem',ValueFromPipelineByPropertyName)] [Alias('WorkItemType')] [string] $Type, + # If set, will create a shared query for work items. The -InputObject will be passed to the body. + [Parameter(Mandatory,ParameterSetName='SharedQuery',ValueFromPipelineByPropertyName)] + [string] + $QueryName, + + # If provided, will create shared queries beneath a given folder. + [Parameter(ParameterSetName='SharedQuery',ValueFromPipelineByPropertyName)] + [Parameter(ParameterSetName='SharedQueryFolder',ValueFromPipelineByPropertyName)] + [string] + $QueryPath, + + # If provided, create a shared query with a given WIQL. + [Parameter(Mandatory, ParameterSetName='SharedQuery',ValueFromPipelineByPropertyName)] + [string] + $WIQL, + + # If provided, the shared query created may be hierchical + [Parameter(ParameterSetName='SharedQuery',ValueFromPipelineByPropertyName)] + [ValidateSet('Flat','OneHop', 'Tree')] + [string] + $QueryType, + + # The recursion option for use in a tree query. + [Parameter(ParameterSetName='SharedQuery',ValueFromPipelineByPropertyName)] + [ValidateSet('childFirst','parentFirst')] + [string] + $QueryRecursiveOption, + + # If provided, create a shared query folder. + [Parameter(Mandatory, ParameterSetName='SharedQueryFolder',ValueFromPipelineByPropertyName)] + [string] + $FolderName, + # The work item ParentID - [Parameter(ValueFromPipelineByPropertyName)] + [Parameter(ValueFromPipelineByPropertyName,ParameterSetName='WorkItem')] [string] $ParentID, @@ -42,13 +75,13 @@ $Project, # A collection of relationships for the work item. - [Parameter(ValueFromPipelineByPropertyName)] + [Parameter(ValueFromPipelineByPropertyName,ParameterSetName='WorkItem')] [Alias('Relationships')] [Collections.IDictionary] $Relationship, # A list of comments to be added to the work item. - [Parameter(ValueFromPipelineByPropertyName)] + [Parameter(ValueFromPipelineByPropertyName,ParameterSetName='WorkItem')] [PSObject[]] $Comment, @@ -58,19 +91,19 @@ $Tag, # If set, will not validate rules. - [Parameter(ValueFromPipelineByPropertyName)] + [Parameter(ValueFromPipelineByPropertyName,ParameterSetName='WorkItem')] [Alias('BypassRules','NoRules','NoRule')] [switch] $BypassRule, # If set, will only validate rules, but will not update the work item. - [Parameter(ValueFromPipelineByPropertyName)] + [Parameter(ValueFromPipelineByPropertyName,ParameterSetName='WorkItem')] [Alias('ValidateRules','ValidateRule','CheckRule','CheckRules')] [switch] $ValidateOnly, # If set, will only validate rules, but will not update the work item. - [Parameter(ValueFromPipelineByPropertyName)] + [Parameter(ValueFromPipelineByPropertyName,ParameterSetName='WorkItem')] [Alias('SuppressNotifications','SkipNotification','SkipNotifications','NoNotify')] [switch] $SupressNotification, @@ -160,6 +193,9 @@ } } #endregion Output Work Item + + + $q = [Collections.Queue]::new() } @@ -177,13 +213,84 @@ $c++ Write-Progress "Creating" "$type [$c/$t]" -PercentComplete ($c * 100 / $t) -Id $progId - + $orgAndProject = @{Organization=$Organization;Project=$Project} $validFields = if ($script:ADOFieldCache.$uribase) { $script:ADOFieldCache.$uribase } else { - Get-ADOField -Organization $Organization -Project $Project -Server $Server @invokeParams + Get-ADOField @orgAndProject -Server $Server @invokeParams + } + + if ($psParameterSet -in 'SharedQuery', 'SharedQueryFolder') { + if ($Server -ne 'https://dev.azure.com/' -and + -not $PSBoundParameters.ApiVersion) { + $ApiVersion = '2.0' + } + + $queryPathParts = @($QueryPath -split '/') + $sharedQueries = $null + foreach ($qp in $queryPathParts) { + if (-not ($qp -as [guid])) { + $sharedQueries = Get-ADOWorkItem -SharedQuery @orgAndProject -Depth 2 + break + } + } + + if ($sharedQueries) { + $queryPathId = $sharedQueries | + Where-Object Path -eq $QueryPath | + Select-Object -ExpandProperty ID + if (-not $queryPathId) { + Write-Error "Unable to find Query Path '$QueryPath'" + continue + } else { + $QueryPath = $queryPathId + } + } + + $uri = $uriBase, "_apis/wit/queries", $(if ($QueryPath) { $QueryPath }) -ne '' -join '/' + $uri = $uri.ToString().TrimEnd('/') + $uri += '?' + + @( + if ($ApiVersion) {"api-version=$ApiVersion" } + ) -join '&' + $invokeParams.uri = $uri + + $queryObject = @{} + if ($psParameterSet -eq 'SharedQueryFolder') { + $queryObject['name'] = $FolderName + $queryObject['isFolder'] = $true + if ($QueryType) { + $queryObject['queryType'] = $QueryType } + if ($queryRecursionOption) { + $queryObject['queryRecursionOption'] = $queryRecursionOption + } + } else { + $queryObject['name'] = $QueryName + $queryObject['wiql'] = $WIQL + } + + $invokeParams.Body = ConvertTo-Json $queryObject -Depth 100 + $invokeParams.Method = 'POST' + $invokeParams.ContentType = 'application/json' + $invokeParams.PSTypeName = @( + "$Organization.$psParameterSet" + "$Organization.$project.$psParameterSet" + "PSDevOps.$psParameterSet" + ) + if ($WhatIfPreference) { + $invokeParams.Remove('PersonalAccessToken') + $invokeParams + continue + } + + if (-not $PSCmdlet.ShouldProcess("POST $uri with $($invokeParams.body)")) { continue } + $restResponse = Invoke-ADORestAPI @invokeParams 2>&1 + $restResponse + continue + } + $validFieldTable = $validFields | Group-Object ReferenceName -AsHashTable $uri = $uriBase, "_apis/wit/workitems", "`$$($Type)?" -join '/' From 0afd5308ca36bdeae39ed5d2b3606b2d6ae50df3 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Mon, 27 Sep 2021 19:59:10 -0700 Subject: [PATCH 09/15] Adding formatting for Shared Queries --- Formatting/PSDevOps.SharedQuery.format.ps1 | 1 + PSDevOps.format.ps1xml | 35 ++++++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 Formatting/PSDevOps.SharedQuery.format.ps1 diff --git a/Formatting/PSDevOps.SharedQuery.format.ps1 b/Formatting/PSDevOps.SharedQuery.format.ps1 new file mode 100644 index 00000000..78326ab2 --- /dev/null +++ b/Formatting/PSDevOps.SharedQuery.format.ps1 @@ -0,0 +1 @@ +Write-FormatView -TypeName PSDevOps.SharedQuery -Property IsPublic, Path, Wiql -Wrap -GroupByProperty Project diff --git a/PSDevOps.format.ps1xml b/PSDevOps.format.ps1xml index 9b673974..fc1344da 100644 --- a/PSDevOps.format.ps1xml +++ b/PSDevOps.format.ps1xml @@ -973,6 +973,41 @@ $($ParentNode.CustomControl.CustomEntries.CustomEntry.CustomItem.ExpressionBindi + + PSDevOps.SharedQuery + + PSDevOps.SharedQuery + + + Project + + + + + + + + + + + + + + + + IsPublic + + + Path + + + Wiql + + + + + + PSDevOps.Team From cc8cf7100e422dc735e5c4d95496c6a19bf92e5c Mon Sep 17 00:00:00 2001 From: James Brundage Date: Mon, 27 Sep 2021 20:05:25 -0700 Subject: [PATCH 10/15] Tests for Shared Queries --- PSDevOps.tests.ps1 | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/PSDevOps.tests.ps1 b/PSDevOps.tests.ps1 index 33080cc3..c5e09be0 100644 --- a/PSDevOps.tests.ps1 +++ b/PSDevOps.tests.ps1 @@ -1,4 +1,4 @@ -param( +param( [string] $TestOrg = 'StartAutomating', [string] @@ -291,7 +291,7 @@ describe 'Calling REST APIs' { } it 'Can set team -DefaultAreaPath and -AreaPath' { - $whatIf = + $whatIf = Get-ADOTeam -Organization StartAutomating -Project PSDevOps -TeamID 'PSDevOps Team' -PersonalAccessToken $testPat | Set-ADOTeam -DefaultAreaPath "MyAreaPath" -WhatIf -AreaPath "An\AreaPath", "Another\AreaPath" @@ -299,8 +299,8 @@ describe 'Calling REST APIs' { $whatIf.Uri | Should -BeLike '*teamFieldvalue*' $whatIf.Body.defaultValue | Should -Be MyAreaPath - - } + + } } context Repositories { @@ -693,6 +693,18 @@ describe 'Working with Work Items' { $queryResults = Get-ADOWorkItem -Organization StartAutomating -Project PSDevOps -Query 'Select [System.ID] from WorkItems Where [System.WorkItemType] = "Epic"' -PersonalAccessToken $testPat -ApiVersion '3.0' $queryResults[0].'System.WorkItemType' | should -be Epic } + + it 'Can get shared queries' { + $sharedQueries = Get-ADOWorkItem -Organization StartAutomating -Project PSDevOps -SharedQuery -PersonalAccessToken $testPat -Depth 2 + $sharedQueryWiql = $sharedQueries | Where-Object Wiql | Select-Object -ExpandProperty Wiql + $sharedQueryWiql | Should -BeLike '*select*from*workitems' + } + + it 'Can create shared queries' { + $testWiql = "select * from WorkItems" + $NewSharedQuery = New-ADOWorkItem -Organization StartAutomating -Project PSDevOps -WIQL "select * from WorkItems" -QueryName Test -QueryPath "Shared Queries" -PersonalAccessToken $testPat -WhatIf + $NewSharedQuery.body.wiql | Should -Be $testWiql + } } @@ -708,6 +720,8 @@ describe 'Working with Work Items' { Remove-ADOWorkItem @splat -Query "select [System.ID] from WorkItems Where [System.Title] = 'Test-WorkItem'" -Confirm:$false } + + it 'Can get work proccesses' { Get-ADOWorkProcess -Organization $TestOrg -PersonalAccessToken $testPat | Select-Object -First 1 -ExpandProperty name | From 463982f76d9db139b0ccf22a0d097f0c6b2414eb Mon Sep 17 00:00:00 2001 From: James Brundage Date: Mon, 27 Sep 2021 20:12:02 -0700 Subject: [PATCH 11/15] New-ADOWorkItem: Allowing shared queries to use -ValidateOnly --- New-ADOWorkItem.ps1 | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/New-ADOWorkItem.ps1 b/New-ADOWorkItem.ps1 index 9768f48e..e1a32cf7 100644 --- a/New-ADOWorkItem.ps1 +++ b/New-ADOWorkItem.ps1 @@ -15,7 +15,7 @@ [OutputType('PSDevOps.WorkItem')] param( # The InputObject - [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName,ParameterSetName='WorkItem')] + [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName,ParameterSetName='WorkItem')] [PSObject] $InputObject, @@ -51,7 +51,7 @@ [Parameter(ParameterSetName='SharedQuery',ValueFromPipelineByPropertyName)] [ValidateSet('childFirst','parentFirst')] [string] - $QueryRecursiveOption, + $QueryRecursiveOption, # If provided, create a shared query folder. [Parameter(Mandatory, ParameterSetName='SharedQueryFolder',ValueFromPipelineByPropertyName)] @@ -97,7 +97,7 @@ $BypassRule, # If set, will only validate rules, but will not update the work item. - [Parameter(ValueFromPipelineByPropertyName,ParameterSetName='WorkItem')] + [Parameter(ValueFromPipelineByPropertyName)] [Alias('ValidateRules','ValidateRule','CheckRule','CheckRules')] [switch] $ValidateOnly, @@ -194,7 +194,7 @@ } #endregion Output Work Item - + $q = [Collections.Queue]::new() } @@ -228,8 +228,8 @@ } $queryPathParts = @($QueryPath -split '/') - $sharedQueries = $null - foreach ($qp in $queryPathParts) { + $sharedQueries = $null + foreach ($qp in $queryPathParts) { if (-not ($qp -as [guid])) { $sharedQueries = Get-ADOWorkItem -SharedQuery @orgAndProject -Depth 2 break @@ -237,7 +237,7 @@ } if ($sharedQueries) { - $queryPathId = $sharedQueries | + $queryPathId = $sharedQueries | Where-Object Path -eq $QueryPath | Select-Object -ExpandProperty ID if (-not $queryPathId) { @@ -246,16 +246,17 @@ } else { $QueryPath = $queryPathId } - } + } $uri = $uriBase, "_apis/wit/queries", $(if ($QueryPath) { $QueryPath }) -ne '' -join '/' $uri = $uri.ToString().TrimEnd('/') - $uri += '?' + - @( - if ($ApiVersion) {"api-version=$ApiVersion" } - ) -join '&' + $uri += '?' + + (@( + if ($ApiVersion) { "api-version=$ApiVersion" } + if ($validateOnly) { "validateWiqlOnly=true" } + ) -join '&') $invokeParams.uri = $uri - + $queryObject = @{} if ($psParameterSet -eq 'SharedQueryFolder') { $queryObject['name'] = $FolderName @@ -266,9 +267,11 @@ if ($queryRecursionOption) { $queryObject['queryRecursionOption'] = $queryRecursionOption } + } else { $queryObject['name'] = $QueryName $queryObject['wiql'] = $WIQL + } $invokeParams.Body = ConvertTo-Json $queryObject -Depth 100 @@ -284,7 +287,7 @@ $invokeParams continue } - + if (-not $PSCmdlet.ShouldProcess("POST $uri with $($invokeParams.body)")) { continue } $restResponse = Invoke-ADORestAPI @invokeParams 2>&1 $restResponse From 6271a92e7e270f224d6f020d83dc92da49db45fb Mon Sep 17 00:00:00 2001 From: James Brundage Date: Mon, 27 Sep 2021 20:12:17 -0700 Subject: [PATCH 12/15] Minor shared queries test cleanup --- PSDevOps.tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PSDevOps.tests.ps1 b/PSDevOps.tests.ps1 index c5e09be0..79fdf88b 100644 --- a/PSDevOps.tests.ps1 +++ b/PSDevOps.tests.ps1 @@ -702,7 +702,7 @@ describe 'Working with Work Items' { it 'Can create shared queries' { $testWiql = "select * from WorkItems" - $NewSharedQuery = New-ADOWorkItem -Organization StartAutomating -Project PSDevOps -WIQL "select * from WorkItems" -QueryName Test -QueryPath "Shared Queries" -PersonalAccessToken $testPat -WhatIf + $NewSharedQuery = New-ADOWorkItem -Organization StartAutomating -Project PSDevOps -WIQL $testWiql -QueryName Test -QueryPath "Shared Queries" -PersonalAccessToken $testPat -WhatIf $NewSharedQuery.body.wiql | Should -Be $testWiql } } From 93f4053fd79b8235888fb67c1b42d316fa3a0f01 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Mon, 27 Sep 2021 20:24:08 -0700 Subject: [PATCH 13/15] New-ADOWorkItem: Minor fix when -QueryPath is not passed --- New-ADOWorkItem.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/New-ADOWorkItem.ps1 b/New-ADOWorkItem.ps1 index e1a32cf7..8478682b 100644 --- a/New-ADOWorkItem.ps1 +++ b/New-ADOWorkItem.ps1 @@ -230,6 +230,7 @@ $queryPathParts = @($QueryPath -split '/') $sharedQueries = $null foreach ($qp in $queryPathParts) { + if (-not $qp) { continue } if (-not ($qp -as [guid])) { $sharedQueries = Get-ADOWorkItem -SharedQuery @orgAndProject -Depth 2 break From 83dc78d3dcc3ae325be041977d308a8ee69133a2 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Mon, 27 Sep 2021 20:24:34 -0700 Subject: [PATCH 14/15] Scaling back shared query automated tests ( build account cannot get shared queries) --- PSDevOps.tests.ps1 | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/PSDevOps.tests.ps1 b/PSDevOps.tests.ps1 index 79fdf88b..2620ca80 100644 --- a/PSDevOps.tests.ps1 +++ b/PSDevOps.tests.ps1 @@ -692,17 +692,11 @@ describe 'Working with Work Items' { it 'Will not use workitemsbatch when using an old version of the REST api' { $queryResults = Get-ADOWorkItem -Organization StartAutomating -Project PSDevOps -Query 'Select [System.ID] from WorkItems Where [System.WorkItemType] = "Epic"' -PersonalAccessToken $testPat -ApiVersion '3.0' $queryResults[0].'System.WorkItemType' | should -be Epic - } - - it 'Can get shared queries' { - $sharedQueries = Get-ADOWorkItem -Organization StartAutomating -Project PSDevOps -SharedQuery -PersonalAccessToken $testPat -Depth 2 - $sharedQueryWiql = $sharedQueries | Where-Object Wiql | Select-Object -ExpandProperty Wiql - $sharedQueryWiql | Should -BeLike '*select*from*workitems' - } + } it 'Can create shared queries' { $testWiql = "select * from WorkItems" - $NewSharedQuery = New-ADOWorkItem -Organization StartAutomating -Project PSDevOps -WIQL $testWiql -QueryName Test -QueryPath "Shared Queries" -PersonalAccessToken $testPat -WhatIf + $NewSharedQuery = New-ADOWorkItem -Organization StartAutomating -Project PSDevOps -WIQL $testWiql -QueryName Test -PersonalAccessToken $testPat -WhatIf $NewSharedQuery.body.wiql | Should -Be $testWiql } } From 016f15b8faebfb6a866ce1cae24690bad3426123 Mon Sep 17 00:00:00 2001 From: James Brundage Date: Mon, 27 Sep 2021 21:04:03 -0700 Subject: [PATCH 15/15] Removing Shared Queries Test (unknown pipeline issues, works fine locally) --- PSDevOps.tests.ps1 | 8 -------- 1 file changed, 8 deletions(-) diff --git a/PSDevOps.tests.ps1 b/PSDevOps.tests.ps1 index 2620ca80..58e5374a 100644 --- a/PSDevOps.tests.ps1 +++ b/PSDevOps.tests.ps1 @@ -693,16 +693,8 @@ describe 'Working with Work Items' { $queryResults = Get-ADOWorkItem -Organization StartAutomating -Project PSDevOps -Query 'Select [System.ID] from WorkItems Where [System.WorkItemType] = "Epic"' -PersonalAccessToken $testPat -ApiVersion '3.0' $queryResults[0].'System.WorkItemType' | should -be Epic } - - it 'Can create shared queries' { - $testWiql = "select * from WorkItems" - $NewSharedQuery = New-ADOWorkItem -Organization StartAutomating -Project PSDevOps -WIQL $testWiql -QueryName Test -PersonalAccessToken $testPat -WhatIf - $NewSharedQuery.body.wiql | Should -Be $testWiql - } } - - it 'Can create, update, and remove a work item' { $splat = @{Organization = $TestOrg; Project = $TestProject; PersonalAccessToken = $testPat } $wi = New-ADOWorkItem -InputObject @{Title = 'Test-WorkItem' } -Type Issue -ParentID 1 @splat -Tag 'PSDevOpsUnitTest' -Comment 'Added while unit testing'