Linking multiple existing work items to queued build (from Azure Devops SDK) - azure-devops

In Azure Devops REST API, I want to link a task\bug\story (that already exists) when triggering a build. How do I do that?
checkInTicket might be a solution, but it is not documented.
Payload based on Merlin's response worked:
var payload = new object[] {
new {
op = "add",
path = "/relations/-",
value =
new {
rel = "ArtifactLink",
url = $"vstfs:///Build/Build/{buildId}",
attributes = new
{
name = "Build"
}
}
}
};

1. Approach 1
Same with the UI operation, to link the exists work item to build, just need to update one option in build definition:
This is the api:
https://dev.azure.com/{org name}/{project name}/_apis/build/definitions/{definition id}?api-version=5.0-preview.6
In its request body, please focus on below script part:
"options": [
{
"enabled": true,
"inputs": {
"branchFilters": "[\"+refs/heads/*\"]",
"additionalFields": "{}"
},
"definition": {
"id": "5d58cc01-7c75-450c-be18-a388ddb129ec"
}
}
]
The enabled represent whether it will create links to work items which linked to associated changes after the build completed. To achieve what you want, here, please set the enabled value as true. The id value is fixed and represent this option, so here do not worry about this id value.
When you updating the build definition by this method, do not forget increment the revision in request body. revision increment means update the definition as a new revision. or the update action would not actually succeed.
But, what you should concerned is this update should finished before the build triggered. Thus it will create link to the exists WIT automatically after the build finished.
Also, this would only add this build link to work item which associate with the changes.
2. Approach 2
If the above is not what you want, and you just want to link work item to build while the build is triggered. Afraid to say, there's no such direct API could finish that.
You may need to use 2 APIs: one of it is queue build, and another API is add this build link to work item.
The request body sample of add build link to work item:
[
{
"op": "test",
"path": "/rev",
"value": "2"
},
{
"op": "add",
"path": "/relations/-",
"value":
{
"rel": "ArtifactLink",
"url": "vstfs:///Build/Build/{the build id that you just queued}"
}
}
]
This method need you get the generated BuildId after you queue the build, and then pass it to the workitem API. Different with the approach 1, in this method, you can customize the work item id which you want to add the build link to.

Related

Unable to update results of a shared test step via Azure Devops REST-api in Postman

I am trying to update the result of a test case via Azure DevOps REST-Api in Postman.
My test case is very simple and consists only of one shared step that has two simple steps inside.
I want to mark these steps of my shared step as "Passed".
Currently I am doing the following steps:
create a new test Run via POST request to {{baseUrlTestApi}}/runs?api-version=7.0
In request's body I pass the test run name, shallow reference to build and owner. Run is successfully created, I can see it on the portal.
create a result of a test case via POST request to {{baseUrlTestApi}}/Runs/{{runId}}/results?api-version=7.0
In request body I pass shallow references to the project, test plan, test suite, test point and test case, outcome ("Passed"), state ("Completed"), testCaseTitle and revision. On the portal I can see that the result is created with the correct outcome and state and is linked to the right test suite and test case.
update the result with actionResults via PATCH request to {{baseUrlTestApi}}/Runs/{{runId}}/results?api-version=7.0
In request body I pass the test result's ID and iteration details, that contains iteration id and an array of action results, where I specify shared step's ID and revision and outcome for each shared step's step. This is the request's body:
[
{
"id": 100000,
"iterationDetails": [
{
"id": 1,
"outcome": "Passed",
"startedDate": "2022-02-16T21:14:14.337Z",
"completedDate": "2022-02-16T21:14:17.057Z",
"durationInMs": 27180000.0,
"actionResults": [
{
"actionPath": "0000000200000001",
"sharedStepModel": {
"id": 18338,
"revision": 2
},
"iterationId": 1,
"stepIdentifier": "1;1",
"outcome": "Passed",
"startedDate": "2022-02-16T21:14:14Z",
"completedDate": "2022-02-16T21:14:14Z",
"url": "{{baseUrlTestApi}}/Runs/{{runId}}/Results/100000/Iterations/1/ActionResults?actionPath=0000000200000001"
},
{
"actionPath": "0000000200000002",
"sharedStepModel": {
"id": 18338,
"revision": 2
},
"iterationId": 1,
"stepIdentifier": "1;2",
"outcome": "Passed",
"startedDate": "2022-02-16T21:14:14Z",
"completedDate": "2022-02-16T21:14:14Z",
"url": "{{baseUrlTestApi}}/Runs/{{runId}}/Results/100000/Iterations/1/ActionResults?actionPath=0000000200000002"
}
],
"parameters": [],
"attachments": [],
"url": "{{baseUrlTestApi}}/Runs/{{runId}}/Results/100000/Iterations/1"
}
]
}
]
Then I go to the portal to see the results. I am expecting to see all three steps of my shared step marked as Passed in the Details section. However, when I go to Runs --> select my run --> test results --> select my result I see this error:
workItemRevision error
I found that what is causing this error is that in the payload of POST request to https://{{organization}}.visualstudio.com/_apis/Contribution/dataProviders/query sent by the portal shared step's work item has an empty string in workItemRevisions.
empty string in workItemRevisions
However, I have explicitly specified the revision ID in step 3 (in sharedStepModel object in each element of actionResults array). If I send a request to this endpoint myself via Postman with the same payload but with "1" instead of empty string, the request is successful.
Before this request, portal sends another one to the same endpoint for the Test Case's workitem and that request is successful, workItemRevisions field is populated correctly, so it is empty string only for the shared step's workitem.
This is the case only if the shared step is involved. If a test case consists of only regular steps, then I am able to mark them as "Passed" with the same flow I described above.
Any ideas what can be causing this? Is this even the right approach to mark the shared step's outcome? Thanks in advance.
After checking the REST API:
You could get a request body (use the Iterations detailsToInclude parameter) and modify the request body. and then to update the result to check if it works.
12.14 Update:
If you manually run the test with shared steps, we can get the actionresults like:
"actionResults": [
{
"actionPath": "00000002",
"iterationId": 1,
"sharedStepModel": {
"id": 142,
"revision": 1
},
"stepIdentifier": "2",
"outcome": "Failed",
"startedDate": "2022-12-14T07:43:03Z",
"completedDate": "2022-12-14T07:43:03Z"
},
{
"actionPath": "0000000200000001",
"iterationId": 1,
"stepIdentifier": "2;1",
"outcome": "Failed",
"startedDate": "2022-12-14T07:43:03Z",
"completedDate": "2022-12-14T07:43:03Z"
}
],
"parameters": [],
"attachments": [],
"url": "https://dev.azure.com/{org}/{project}/_apis/test/Runs/{runId}/Results/100000/Iterations/1"
}
]
And then I change the outcome from "Failed" to "Passed" and then Patch the result to:
https://dev.azure.com/{org}/{project}/_apis/test/Runs/{runId}/results?api-version=7.0
And then I can view the result in the run -> Test results -> The run.
So, I think your problem maybe related to the format of the actionresults, you can try to run a manually test to get the actionresults and then PATCH it to check if it works.

In TeamCity, trigger build with 2 VCS roots with 2 custom revisions using REST API

looking for collective wisdom on this problem I am tackling.
I need to trigger build using REST API on TeamCity. The build configuration has 2 VCS roots and I need to pass specific revision for both of them. In documentation, I found example for single VCS, but I am stuck on setting revisions for both of them.
I am trying to set up something like this:
{
"buildType": {
"id": "ExampleConfiguration"
},
"defaultBranch": "false",
"lastChanges": {
"change": [
{
"locator": "combination of locators that will set VCS 1 - branch + revision"
},
{
"locator": "combination of locators that will set VCS 2 - branch + revision"
}
]
}
}
In principle, I am simulating execution of the build, where both revisions are manually set - this is possible in the UI.
https://www.jetbrains.com/help/teamcity/rest/start-and-cancel-builds.html#Advanced+Build+Run
TC version: 2022.04.3 (build 108706)
Edit:
I was able to detect the changes and resolve their id on both VCS roots. For the changes field in the body I can now use locator of the form {"id": 123456}. But another obstacle on the road, if I specify pair of these changes:
"lastChanges": { "change": [ {"id": 1}, {"id": 2} ] }
I get error
Responding with error, status code: 400 (Bad Request).
Details: jetbrains.buildServer.server.rest.errors.BadRequestException: Several non-personal changes are submitted, only one can be present
Invalid request. Please check the request URL and data are correct.
Same build with the exact revisions is possible to trigger manually from TC UI.
Finally resolved it. The way to go is to use revisions instead of lastChanges. To pass revisions for 2 VCS roots together with branch names, use this:
"revisions": {
"revision": [
{
"vcs-root-instance": {
"id": "111"
},
"vcsBranchName": "branch-name-111",
"version": "rev_on_vcs_111"
},
{
"vcs-root-instance": {
"id": "222"
},
"vcsBranchName": "branch-name-222",
"version": "rev_on_vcs_222"
}
]
}
To resolve VCS root instance id, use API call /app/rest/vcs-root-instances/vcsRoot:(id:(Project_Vcs_Root))"

How to make child-card Discussion notes rollup to parent card?

How to make child-card Discussion notes rollup to parent card in Azure DevOps?
The way we use ADO is like this:
User Story -> Task 1
-> Task 2
-> Task 3
Both User Story cards and Task cards contain Discussion fields, and we have hit a quandary as to "which cards' discussion fields should we use to enter ongoing notes and discussions"? The Parent (user story)? Or the child (task)? It makes more sense for devs to enter discussion notes in the tasks, but our Support people and managers like to just look at the parent User Story card where they hope to see all discussion notes.
For now I am double-entering discussion notes in both tasks and their parent user story cards, which is not only an inefficient pain, but also violates DRY.
Then it occurred to me that the ideal solution would be for user story cards to be able to display (within the Discussion section) all discussion notes from all child cards. So if I enter a note in a Task (child) card and save it, automatically it would appear as a discussion note within its parent user story card. Ideally I could still enter user-story-specific discussion notes on occasion, but all child cards' notes would be automatically pulled in and displayed (maybe readonly? editable only in the child?) in the parent card, in all cases.
Is there an easy way to make this happen?
If the answer to #1 above is no, then is there a difficult way to make it happen? Maybe via customizations or custom API calls? What would be the best way to achieve my desired result?
This is not possible out of the box in Azure DevOps (at least not to my knowledge).
However I can think of a few ways to get this done, but it requires some custom scripting, and have some drawbacks that may of may not affect your use case.
Option 1. Create a custom extension that aggregates the comments on the parent
This option gives you complete freedom of how to visually design the feature. It is also completely DRY
You can develop your own custom extension for Azure DevOps and create a dynamic section on the User Story card that pulls in the comments from all tasks.
Option 2. Setup a Web Hook for the event "Workitem commented on"
This option does is not completely DRY, but it will at least automate the copying of comments across work items.
By configuring a web hook in Azure Devops to post a json object containing information about the new comment to a REST endpoint of your choice. The payload posted looks like below.
{
"subscriptionId": "5be97cbc-ee4b-4c21-91ea-866a61d624c4",
"notificationId": 4,
"id": "fb2617ed-60df-4518-81fa-749faa6c5cd6",
"eventType": "workitem.commented",
"publisherId": "tfs",
"message": {
"markdown": "[Bug #5](http://fabrikam-fiber-inc.visualstudio.com/web/wi.aspx?pcguid=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) commented on by Jamal Hartnett."
},
"detailedMessage": {
"markdown": "[Bug #5](http://fabrikam-fiber-inc.visualstudio.com/web/wi.aspx?pcguid=74e918bf-3376-436d-bd20-8e8c1287f465&id=5) (Some great new idea!) commented on by Jamal Hartnett.\r\nThis is a great new idea"
},
"resource": {
"id": 5,
"rev": 4,
"fields": {
"System.AreaPath": "FabrikamCloud",
"System.TeamProject": "FabrikamCloud",
"System.IterationPath": "FabrikamCloud\\Release 1\\Sprint 1",
"System.WorkItemType": "Bug",
"System.State": "New",
"System.Reason": "New defect reported",
"System.CreatedDate": "2014-07-15T17:42:44.663Z",
"System.CreatedBy": {
"displayName": "Jamal Hartnett",
"url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/e5a5f7f8-6507-4c34-b397-6c4818e002f4",
"_links": {
"avatar": {
"href": "https://dev.azure.com/mseng/_apis/GraphProfile/MemberAvatars/aad.YTkzODFkODYtNTYxYS03ZDdiLWJjM2QtZDUzMjllMjM5OTAz"
}
},
"id": "e5a5f7f8-6507-4c34-b397-6c4818e002f4",
"uniqueName": "Jamal Hartnett",
"imageUrl": "https://dev.azure.com/fabrikam/_api/_common/identityImage?id=e5a5f7f8-6507-4c34-b397-6c4818e002f4",
"descriptor": "ukn.VXkweExUVXRNakV0TWpFME5qYzNNekE0TlMwNU1ETXpOak15T0RVdE56RTVNelEwTnpBM0xURXpPRGswTlRN"
},
"System.ChangedDate": "2014-07-15T17:42:44.663Z",
"System.ChangedBy": {
"displayName": "Jamal Hartnett",
"url": "https://vssps.dev.azure.com/fabrikam/_apis/Identities/e5a5f7f8-6507-4c34-b397-6c4818e002f4",
"_links": {
"avatar": {
"href": "https://dev.azure.com/mseng/_apis/GraphProfile/MemberAvatars/aad.YTkzODFkODYtNTYxYS03ZDdiLWJjM2QtZDUzMjllMjM5OTAz"
}
},
"id": "e5a5f7f8-6507-4c34-b397-6c4818e002f4",
"uniqueName": "Jamal Hartnett",
"imageUrl": "https://dev.azure.com/fabrikam/_api/_common/identityImage?id=e5a5f7f8-6507-4c34-b397-6c4818e002f4",
"descriptor": "ukn.VXkweExUVXRNakV0TWpFME5qYzNNekE0TlMwNU1ETXpOak15T0RVdE56RTVNelEwTnpBM0xURXpPRGswTlRN"
},
"System.Title": "Some great new idea!",
"System.Parent": 26
"Microsoft.VSTS.Common.Severity": "3 - Medium",
"WEF_EB329F44FE5F4A94ACB1DA153FDF38BA_Kanban.Column": "New",
"System.History": "This is a great new idea"
},
"_links": {
"self": {
"href": "http://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/workItems/5"
},
"workItemUpdates": {
"href": "http://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/workItems/5/updates"
},
"workItemRevisions": {
"href": "http://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/workItems/5/revisions"
},
"workItemType": {
"href": "http://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/ea830882-2a3c-4095-a53f-972f9a376f6e/workItemTypes/Bug"
},
"fields": {
"href": "http://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/fields"
}
},
"url": "http://fabrikam-fiber-inc.visualstudio.com/DefaultCollection/_apis/wit/workItems/5"
},
"resourceVersion": "1.0",
"resourceContainers": {
"collection": {
"id": "c12d0eb8-e382-443b-9f9c-c52cba5014c2"
},
"account": {
"id": "f844ec47-a9db-4511-8281-8b63f4eaf94e"
},
"project": {
"id": "be9b3917-87e6-42a4-a549-2bc06a7a878f"
}
},
"createdDate": "2022-07-03T12:30:03.0691717Z"
}
In particular you have the comment message under message, the author of the message under fields.System.ChangedBy, the date of the comment in fields.System.ChangedDate and the parent workitem id in System.Parent
Using this information your REST service that receives the json object can create a comment on the parent work item using the Comments Rest API or post an update to the workitem where you set the System.History field to the value of the comment. This also allows you to set the original comment time and author, given that the user account you use in your automation has the permission Bypass rules on work item updates granted.
Drawbacks
If tasks can be moved from one user story to another, then the task comments on the user stories need to be moved. The same idea as above can be applied to solve this. By configuring a webhook for WorkItem updated you can listen for all updates to tasks.
By fetching the previous revision (Revision is found in resource.rev) from the Revision Rest API and comparing the field System.Parent you can determine whether the task has been moved or not and add the comments to the new parent and remove them from the old.
Similarly if you want to propagate comment updates and reactions, you would need to extend the webhook functionality even further.

Implemented a Resource Type: How does Concourse use the output of the check, in, and out scripts?

Reading the Concourse documentation about Implementing a Resource Type, in regards to what the check, in, and out scripts must emit, it is not clear why this output is needed or how Concourse uses it. My questions are:
1) How does Concourse use the output of the check script, the in script, and the out script?
2) And, why is it required that the in and out script emit the version? What happens if you don't?
For context, here is the relevant parts of the documentation:
1) For the check script:
...[it] must print the array of new versions, in chronological order,
to stdout, including the requested version if it's still valid.
For example:
[
{ "ref": "61cbef" },
{ "ref": "d74e01" },
{ "ref": "7154fe" }
]
2) For the in script:
The script must emit the fetched version, and may emit metadata as a list of key-value pairs. This data is intended for public consumption and will make it upstream, intended to be shown on the build's page.
For example:
{
"version": { "ref": "61cebf" },
"metadata": [
{ "name": "commit", "value": "61cebf" },
{ "name": "author", "value": "Hulk Hogan" }
]
}
3) Similar to the in script, the out script:
The script must emit the resulting version of the resource. For
example, the git resource emits the sha of the commit that it just
pushed.
For example:
{
"version": { "ref": "61cebf" },
"metadata": [
{ "name": "commit", "value": "61cebf" },
{ "name": "author", "value": "Mick Foley" }
]
}
Concourse uses the check result to verify if there is any new resource available. According to your pipeline definition, the presence of a new resource would trigger a job. The in is therefore used to read the specific resource using parameters provided by the pipeline whilst the out would take care of writing them.
As your in is going to use the information provided by the check you may want to use a similar structure, but you're not obliged to. It is useful to echo the same version information in your check/in/out in order to be able to log it and understand each resource in your pipeline is belonging to which version.

How do we know a pull request is approved or rejected using API in github?

I would like to know if there is an function in the Github API which would return the status of a pull request whether its accepted or rejected. Does such a function exist?
I was stymied by this problem, too. I had a hard time finding relevant info in the docs.
Option A
The tactic I've landed on is to use the issues search endpoint with the review qualifier in the q param.
So, in my case, when I want to see all PRs assigned to me that have not yet been reviewed, I'd hit this endpoint: https://github.com/api/v3/search/issues?q=is:open+is:pr+review-requested:jordan-bonitatis+review:none
Other review qualifiers include approved and changes_requested as explained here: https://docs.github.com/en/github/searching-for-information-on-github/searching-issues-and-pull-requests#search-by-pull-request-review-status-and-reviewer
Option B
If you want to see the review status for a given PR, instead of getting a list filtered by status like in my example above, you can hit the pull requests reviews endpoint: /repos/:owner/:repo/pulls/:number/reviews
This will return a list of reviews for the PR, each of which have a state key.
Note that a given PR may have multiple reviews with conflicting states. Like, if teamMemberA approved it but teamMemberB requested changes. You'll have to traverse the entire list and decide how you want to treat it based on all the states.
Depending on the repo configuration you can get the answer one way or another. The examples below use the GraphQL API.
Case 1: PR reviews are required before merging
You can query reviewDecision on the pullRequest field for a given repository.
reviewDecision is of type PullRequestReviewDecision, an enum with values of APPROVED, CHANGES_REQUESTED, and REVIEW_REQUIRED.
Example query:
{
repository(name: "gatsby", owner: "gatsbyjs") {
pullRequest(number: 30371) {
title
reviewDecision
url
}
}
}
Response:
{
"data": {
"repository": {
"pullRequest": {
"title": "chore(gatsby): don't terminate dev server if graphql wasn't imported from gatsby",
"reviewDecision": "APPROVED",
"url": "https://github.com/gatsbyjs/gatsby/pull/30371"
}
}
}
}
Case 2: PR reviews are not required before merging
If the repository settings don't specify that reviews are required, reviewDecision will be null regardless of approvals.
In this case you could iterate over the reviews and check the states. state is of type PullRequestReviewState, an enum with values of APPROVED, CHANGES_REQUESTED, COMMENTED, DISMISSED, and PENDING.
Example query:
{
repository(name: "create-react-app", owner: "facebook") {
pullRequest(number: 10003) {
title
reviewDecision
url
reviews(first: 100) {
nodes {
state
author {
login
}
}
}
}
}
}
Response:
{
"data": {
"repository": {
"pullRequest": {
"title": "Update postcss packages",
"reviewDecision": null,
"url": "https://github.com/facebook/create-react-app/pull/10003",
"reviews": {
"nodes": [
{
"state": "APPROVED",
"author": {
"login": "jasonwilliams"
}
}
]
}
}
}
}
}
It's possible to filter reviews for approvals: reviews(first: 100, states: APPROVED).
Note that two reviews will be returned if a reviewer gives his approval and subsequently requests changes.
Checking the PR state (of type PullRequestState) could be misleading: an admin user may have bypassed the required review process to merge changes.
You can get a single PR and check its state and merged properties. If it's merged, then it's accepted. If it's closed and not merged it may be rejected.
In fact it may be not rejected but closed by a creator. I'm not sure if it's possible to check if it was closed by another user (rejected) or by it's creator (denied).
I had a similar requirement - to know if a PR has been approved or not before merging it to master. I added a rule on the repo to ensure that every PR must be approved prior to merging. Then on using the API to get details about the branch there was one field which stated if the branch was clean to merge or blocked . I used this as a way to mange things in my app.
mergeable_state
This is now possible using GraphQL.
Specifically if the mergeStateStatus enum is BLOCKED then the pull request hasn't been approved and for any other status it would have been approved.
This enum is present in the PullRequest object. Do note that at the time of posting this is a preview feature and so must have the appropriate header to work. It will also not work using the GraphQL Explorer while it's in preview.
In the official Github API Documentation it shows that there is a GET request you can make that has the field merged_at if it has already been merged. Edit: theres also a merged field as well.
Snippet:
...
"merge_commit_sha": "e5bd3914e2e596debea16f433f57875b5b90bcd6",
"merged": false,
"mergeable": true,
"merged_by": {
"login": "octocat",
"id": 1,
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"site_admin": false
},