Bad JSON escape sequence in powershell trying to edit a wiql - azure

I've been struggeling all day trying to make a script that changes the value of wiql.
After som debugging and testing I found out that it's actually the wiql that fails, even tho I get it from Azure Devops rest api.
select [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State], [System.Tags] from WorkItems where [System.TeamProject] = #project and [System.WorkItemType] = 'User Story' and [System.AreaPath] under 'error\wrong\folder' and [System.CreatedDate] <= '2022-12-16T00:00:00.0000000' and ([System.IterationPath] under '
folder\folder' or [System.IterationPath] under 'folder\folder')
It fails at "and [System.AreaPath] under 'error\wrong\folder' "
I removed one and one line and after I remove this I only got select [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State], [System.Tags] from WorkItems where [System.TeamProject] = #project and [System.WorkItemType] = 'User Story' it works.
Else I just get
Invoke-RestMethod : {"count":1,"value":{"Message":"Bad JSON escape sequence: \\S. Path 'wiql', line 2, position 245.\r\nUnexpected character encountered while parsing value: A. Path 'wiql', line 2, position 245.\r\n"}}
At line:1 char:13
+ $response = Invoke-RestMethod -Method Patch -Uri $testurl -Body $body ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Help`?
Tried every combo but cant get it to work

Related

Invoke-RestMethod in Azure Function not working

I am facing an issue with my Azure function (Powershell, timer trigger on Linux) which extracts values from Power Bi Service and pass them to a DevOps Pipeline when triggering it.
The triggering is done through Invoke-RestMethod.
The function works fine until it gets values which contain special german characters (Ü, Ö, Ä,...) then the body of the request is somehow broken and the function fails.
I think the problem is related to the encoding but the only possible solution I found is to add charset=utf-8 to the header. Unfortunately, it did not solve the problem.
$headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
$headers.Add("Content-Type", "application/json; charset=utf-8")
$headers.Add("Authorization", "Basic $secret")
$body = "{`"templateParameters`": {`"items`": `"$items`"}}"
$response = Invoke-RestMethod 'https://dev.azure.com/<repos>/<project>/_apis/pipelines/1/runs?api-version=7.0' -Method 'POST' -Headers $headers -Body $body
Items is a string of the form:
[['filename','userx','Workspace','givendateTime']]
when the Workspace has a Ö instead of 'o' an exception is raised:
Invoke-RestMethod : {"$id":"1","innerException":null,"message":"Value cannot be null.\r\nParameter name:
runParameters","typeName":"System.ArgumentNullException, mscorlib","typeKey":"ArgumentNullException","errorCode":0,"eventId":0}
+ ... $response = Invoke-RestMethod 'https://dev.azure.com/...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Using Postman with the same parameters and values works fine.
Write-Host $body just before the Invoke-RestMethod is fine and the characters are correct.
Any help will be much appreciated :)

Azure DevOps API error: Field state 'Active' not supported when Work Item has been "Removed"

I have a work item of type feature with a state of "Removed." When trying to update the field state back to "Active" on certain conditions, I'm getting the following error:
resp = Invoke-RestMethod -Uri $updateUri -Method Patch -ContentType application/json-patch+json -Headers $header -Body $jsonBody
Invoke-RestMethod : {"$id":"1","customProperties":{"FieldReferenceName":null,"FieldStatusFlags":"none","ErrorMessage":"The field 'State' contains the value 'Active' that is not in
the list of supported values","FieldStatusCode":0,"RuleValidationErrors":[{"fieldReferenceName":"System.State","fieldStatusFlags":"required, hasValues, limitedToValues,
invalidListValue","errorMessage":"The field 'State' contains the value 'Active' that is not in the list of supported
values","fieldStatusCode":4194317,"ruleValidationErrors":null}]},"innerException":null,"message":"The field 'State' contains the value 'Active' that is not in the list of supported
values","typeName":"Microsoft.TeamFoundation.WorkItemTracking.Server.RuleValidationException,
Microsoft.TeamFoundation.WorkItemTracking.Server","typeKey":"RuleValidationException","errorCode":0,"eventId":3200}
At line:1 char:9
+ $resp = Invoke-RestMethod -Uri $updateUri -Method Patch -ContentType ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
Note that my code works updating field.'System.State' in all cases except when it's trying to change a "removed" work item. Is there a special way to do this?
Is there a special way to do this?
I am afraid there is no such way to do this.
That is because the workitem that is already in the Removed state cannot be converted to other states except New.
You could check the document Agile workflow states for some more details.
In addition, when we have chosen to Remove a certain workitem, it means that we have abandoned the workitem. If the workitem is re-enabled, it should return to the state of New.

Current Iteration Query Script Devops Looping Through Projects

I'm trying to put a script together that will let me loop through projects and create queries relevant to the current iteration.
I can loop through and create queries until it comes to looking at the iteration path and then it doesn't seem to like it.
When I run my script all of the other queries are created, and this one is as well, but it brings up an error when I go to look at it because it's posting that it's not in the right form. How can I get this query to loop through like the others and bring back the appropriate team in the process?
I'm assuming that somehow I've got to put project and team as variables? I tried $project as well but that just presented a similar error. Although I'm also confused about about the "" since the code doesn't like that either, and it won't even create the query when I've tried that.
$JSON27 = #'
{
"name": "Current Sprint Query", "wiql": "SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State],[System.Tags],[Microsoft.VSTS.Scheduling.OriginalEstimate],[Microsoft.VSTS.Scheduling.CompletedWork],[System.IterationPath] FROM workitems WHERE [System.TeamProject] = #project AND [System.WorkItemType] <> '' AND [System.State] = 'Closed' AND [System.IterationPath] = #CurrentIteration('[Project]/Team') ORDER BY [System.WorkItemType]"
}
'#
$response27 = Invoke-RestMethod -Uri $url1 -Headers #{Authorization = "Basic $token"} -Method Post -Body $JSON27 -ContentType application/json
Please refer to the following script to get the project name first, and then create a query by looping through the obtained project name.
Please use #CurrentIteration('[Project]\\Team') instead of #CurrentIteration('[Project]/Team') or #CurrentIteration('[Project]\Team')
$token = "your PAT"
$url="https://dev.azure.com/org name/_apis/projects?api-version=6.0"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Get -ContentType application/json
echo $url
foreach ($project in $response.value.name){
$url3="https://dev.azure.com/org name/$($project)/_apis/wit/queries/My Queries?api-version=6.1-preview.2"
echo $url1
$body = "{
`"name`": `"query name`",
`"wiql`": `"Select [System.Id], [System.State] From WorkItems where [System.IterationPath] = #CurrentIteration('[$project]\\$project Team') `"
}"
echo $body
$response3 = Invoke-RestMethod -Uri $url3 -Headers #{Authorization = "Basic $token"} -Method Post -Body $body -ContentType application/json
echo $response3
}
Summarizing the discussion in comments as an answer below:
So, as the error message calls out, I think the issue is that you have #CurrentIteration('[Project]/Team') in your query instead of #CurrentIteration('[Project]\Team').
Your query should be this instead:
$JSON27 = #'
{
"name": "Current Sprint Query", "wiql": "SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State],[System.Tags],[Microsoft.VSTS.Scheduling.OriginalEstimate],[Microsoft.VSTS.Scheduling.CompletedWork],[System.IterationPath] FROM workitems WHERE [System.TeamProject] = #project AND [System.WorkItemType] <> '' AND [System.State] = 'Closed' AND [System.IterationPath] = #CurrentIteration('[Project]\Team') ORDER BY [System.WorkItemType]"
}
'#
EDIT:
By default, shared queries and new queries are scoped to the current project. For querying across multiple projects, you can add the TeamProject field to filter to a select number of projects as:
..
AND [System.TeamProject] IN ('Project 1', 'Project 2', 'Project 3')
However, using the #CurrentIteration macro would need you to select a Team and may not work across Projects and Teams. Check this idea on Developer Community for more details.

Create several queries across multiple projects in Azure DevOps

I'm trying to create the same queries across several projects. I feel like I'm almost there, but can't come up with the right 'thing' to create more than one query. I've tried "," ";" "AND" - and have managed to get it to create one but never both.
Could someone assist?? I've put down a quick example snippet of the type of thing I'm trying (which is why the fields are the same). If I can get that right, I'll use it for real with all the different queries I have (there's quite a few!)
{ $url1="https://dev.azure.com/[ORGANISATION]/$($project)/_apis/wit/queries/Shared Queries?api-version=6.0"
$JSON = #'
{
"name": "All User Stories JW6", "wiql": "SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State],[System.Tags],[Microsoft.VSTS.Scheduling.OriginalEstimate],[Microsoft.VSTS.Scheduling.CompletedWork] FROM workitems WHERE [System.WorkItemType] = 'User Story' AND [System.State] = 'Active' ORDER BY [System.WorkItemType]",
"name": "All User Stories JW7", "wiql": "SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State],[System.Tags],[Microsoft.VSTS.Scheduling.OriginalEstimate],[Microsoft.VSTS.Scheduling.CompletedWork] FROM workitems WHERE [System.WorkItemType] = 'User Story' AND [System.State] = 'Active' ORDER BY [System.WorkItemType]"
}
'#
Create several queries across multiple projects in Azure DevOps
I am afraid we could not set duplicate Keys in JSON body.
That because the names in json object SHOULD be unique, check the state in RFC-7159:
The current standard for JSON published by the Internet Engineering
Task Force (IETF), states "The names within an object SHOULD be
unique".
I use the body in the postman, I got the same warning:
If we use the duplicate Keys in JSON body, most popular parsers may take only the last value present in the object for a particular key and ignore the previous ones.
In your sample, only query All User Stories JW7 will be created.
To resolve this issue, we have to create a another body $JSON2, and send another request:
$JSON2 = #'
{
"name": "All User Stories JW7", "wiql": "SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State],[System.Tags],[Microsoft.VSTS.Scheduling.OriginalEstimate],[Microsoft.VSTS.Scheduling.CompletedWork] FROM workitems WHERE [System.WorkItemType] = 'User Story' AND [System.State] = 'Active' ORDER BY [System.WorkItemType]"
}
'#
$CreateQuery1= Invoke-RestMethod -Uri $url -headers $headers -Method Post -ContentType "application/json" -Body $Json1
$CreateQuery2= Invoke-RestMethod -Uri $url -headers $headers -Method Post -ContentType "application/json" -Body $Json2

VSTS Rest API queries for linked work items in Powershell

Using REST APIs of VSTS in PowerShell, I wanted to create a report on my VSTS Team Project which consists of the details of the Work Item Linking relationships of all workitems under particular "Iteration Path" and "Area Path".
Ex: Epics→Features→UserStories. Since there are parent/child relationships between Epics & Features and also between Features & UserStories.
So the input would be Iteration Path and Area Path, and the corresponding output would be a report (.csv or .xls) which has all the details of these workitems and their relationships.
Could someone let me know on how to achieve this using REST APIs in PowerShell?
I wrote the code for getting the hierarchical workitems in a Team Project but need to modify to filter the the list based on a given Iteration and Area paths.
Also when executing the below code, the string comparison in the query(due to presence of '') is returning an error in the power-shell.
Error: Invoke-RestMethod : {"count":1,"value":{"Message":"After parsing a value an unexpected character was encountered: G.
Path 'query', line 1, position 163.\r\n"}}
$vstsAccount = "xxxx"
$projectName = "xxxx"
$user = "xxxx"
$token = "xxxx"
# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
# Construct the REST URL to obtain Build ID
$uri = "https://$($vstsAccount).visualstudio.com/$($projectName)/_apis/wit/wiql?api-version=1.0"
$body = "{'query': 'Select [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State] From WorkItemLinks WHERE
((Source.[System.TeamProject] = '$projectName' and Source.[System.State] <> 'Removed') and (Source.[System.WorkItemType] = 'Epic') and
([System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward') and (Target.[System.WorkItemType] = 'UserStory') mode(Recursive)'}"
# Invoke the REST call and capture the results (notice this uses the POST method)
$result = Invoke-RestMethod -Uri $uri -Method Post -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Body $body
Modify part of your code like this:
$query = "Select
[System.Id],
[System.WorkItemType],
[System.Title],
[System.AssignedTo],
[System.State]
From WorkItemLinks
WHERE (
Source.[System.TeamProject] = '$projectName'
AND Source.[System.State] <> 'Removed'
) AND (
Source.[System.WorkItemType] = 'Epic')
AND ([System.Links.LinkType] = 'System.LinkTypes.Hierarchy-Forward'
) AND (Target.[System.WorkItemType] = 'UserStory') mode (Recursive)
"
$body = #{query=$query} | ConvertTo-Json
$result = Invoke-RestMethod -Uri $uri -Method Post -ContentType "application/json" -Headers #{Authorization=("Basic $base64AuthInfo")} -Body $body

Resources