As many developers, I was also very happy to get to know, that Microsoft published a new REST API for Visual Studio Online. A feature of my main interest was the possibility to update a work item. On this site you can find a reference with some examples, how to use the REST API for updating a work item field.
According to the description, you just need to change a work item field, you’d like to update and pass it in JSON string with your HTTP-Request. Unfortunately, the world is not as simple as one would expect, and updating a work item state may get a little bit tougher in some cases. I have investigated the issue and collected all my knowledge in this blog entry. I know, this is a common problem and bunch of people out there are struggling with it at the moment. I hope, this blog entry can be helpful for them in some way.
1. Some important points
1.1 PATCH-Request
In order to update a work item you need to send a PATCH-Request. I do it following way, since the HttpClient doesn’t have PatchAsync() method, similar to GetAsync() or PostAsync().
1: var content = new StringContent(jsonString);
2: content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
3: var request = new HttpRequestMessage(new HttpMethod("PATCH"), requestUri) { Content = content };
4: HttpResponseMessage response = client.SendAsync(request).Result
1.2 Obtaining error information from response
Lots of developers complain about insufficient information, they get back as a response, after having sent a HTTP request (infamous BAD REQUEST 400). I can bet, they use following or some similar code, that you can find in the VSO REST API reference:
1: using (HttpResponseMessage response = client.GetAsync(apiUrl + _apiVersion).Result)
2: {
3: response.EnsureSuccessStatusCode();
4:
5: responseBody = await response.Content.ReadAsStringAsync();
6: }
I found out, when you reorder the instructions inside the curly brackets, you can still read the response content, before an exception in method response.EnsureSuccessStatusCode() will be thrown. As a response you get then a JSON string with some error information, that could help you with your problem.
1.3 Changing work item state
In VSO you can create a project according to three process templates: Scrum, Agile and CMMI. A Process Template customization is still not available for VSO. For each particular process template you can download process template configuration files from your Visual Studio (Team/Team Project Collection Settings/Process Template Manager…). You must be a member of the Project Colelction Administrators group in order to be able to do that. (see this)
When you have downloaded the process template, you can find there configuration files for all work item types provided by given process template. If you would like to update a Requirement (CMMI), you look up for “MSF for CMMI Process Improvement 2013.3\WorkItem Tracking\TypeDefinitions\Requirement.xml” and open it. The configuration file is an XML-File.
When you want to update a work item state, you are about to perform a transition. You can find transition definitions in the work item configuration file under “WITD/WORKITEMTYPE/WORKFLOW/TRANSITIONS“. A transition is defined by attributes “from” and “to“, which describe the actual state and the new state, you would like to assign to the work item. When you update a work item state, you need to pass in your JSON following fields:
- New state (System.State)
- Reason (System.Reason) You can find allowed reasons in the transition definition under “TRANSITION/REASONS“. I always take the DEFAULTREASON for that.
- For some transition there are some required fields defined, you need to pass with your request. You find them under “TRANSITION/FIELDS“
- Under “WITD/WORKITEMTYPE/WORKFLOW/STATES” you can find state definitions. Check the definition for your new state. In this definition, under “FIELDS” you can find a list of required and empty fields. You can distinguish them on their child node, which will be <REQUIRED /> or <EMPTY />.
In you work item update JSON, you need to pass updates for all this fields. When a field is supposed to be empty, you just pass null as its value. For required field and fields from 3. you need to assign some proper values. From my experience I can say, that these are always fields, that match following pattern: “xxxDate“, “xxxBy” and “xxxTo“. For the first one you need to assign a string with a date with following formatting:
DateTime.Now.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fff''Z");
For the other kinds (“xxxBy” and “xxxTo“) you need to assign a display name of a user. For my individual case it will be “Jakub Sabacinski“. You must find out yourself, which user display name you will use. It should be a project member of course.You should keep in mind, that the set with required fields and and the set with fields from 3. can contain the same elements. You must be careful not to pass a field twice with your request.
2. Example
I would like to update state of a Requirement from my project to “Active“. I obtained work item id and revision number using a REST API query. I know the actual work item state as well, which is “Resolved” Now I search for the transition from “Resolved” to “Active” in the configuration file for a Requirement. I take the default reason, which is “Validation Test Failed“. I also see, that the fields “Microsoft.VSTS.Common.ActivatedBy” and “Microsoft.VSTS.Common.ActivatedDate” are required for this transition, so I assign them the user name and the actual date.
Transition definition:
1: <TRANSITION from="Resolved" to="Active">
2: <REASONS>
3: <DEFAULTREASON value="Validation Test Failed" />
4: </REASONS>
5: <FIELDS>
6: <FIELD refname="Microsoft.VSTS.Common.ActivatedBy">
7: <COPY from="currentuser" />
8: <VALIDUSER />
9: </FIELD>
10: <FIELD refname="Microsoft.VSTS.Common.ActivatedDate">
11: <SERVERDEFAULT from="clock" />
12: </FIELD>
13: </FIELDS>
14: </TRANSITION>
After that, I read the definition for the state “Active”. I find out, that required field for this state are: “Microsoft.VSTS.Common.ActivatedDate“, “Microsoft.VSTS.Common.ActivatedBy” and “System.AssignedTo“. The two first of them I have already assigned. For the field “System.AssignedTo” I do nothing, because I know from my first REST query, that this field has already a value. For each empty field (“Microsoft.VSTS.Common.ResolvedDate”, “Microsoft.VSTS.Common.ResolvedBy”, “Microsoft.VSTS.Common.ResolvedReason”, “Microsoft.VSTS.Common.ClosedDate” and “Microsoft.VSTS.Common.ClosedBy”) I assigned null as value.
State definition:
1: <STATE value="Active">
2: <FIELDS>
3: <FIELD refname="Microsoft.VSTS.Common.ResolvedDate">
4: <EMPTY />
5: </FIELD>
6: <FIELD refname="Microsoft.VSTS.Common.ResolvedBy">
7: <EMPTY />
8: </FIELD>
9: <FIELD refname="Microsoft.VSTS.Common.ResolvedReason">
10: <EMPTY />
11: </FIELD>
12: <FIELD refname="Microsoft.VSTS.Common.ClosedDate">
13: <EMPTY />
14: </FIELD>
15: <FIELD refname="Microsoft.VSTS.Common.ClosedBy">
16: <EMPTY />
17: </FIELD>
18: <FIELD refname="Microsoft.VSTS.Common.ActivatedDate">
19: <REQUIRED />
20: </FIELD>
21: <FIELD refname="Microsoft.VSTS.Common.ActivatedBy">
22: <REQUIRED />
23: </FIELD>
24: <FIELD refname="System.AssignedTo">
25: <REQUIRED />
26: </FIELD>
27: </FIELDS>
28: </STATE>
29:
So, at the end, my JSON, which I’m going to send as a content for a PATCH-Request, looks like this:
1: {
2: "id": xx,
3: "rev": yy,
4: "fields": [{
5: "field": {
6: "refName": "System.State"
7: },
8:
9: "value": "Active"
10: },
11: {
12: "field": {
13: "refName": "System.Reason"
14: },
15:
16: "value": "Validation Test Failed"
17: },
18: {
19: "field": {
20: "refName": "Microsoft.VSTS.Common.ActivatedBy"
21: },
22:
23: "value": "Jakub Sabacinski"
24: },
25:
26: {
27: "field": {
28: "refName": "Microsoft.VSTS.Common.ActivatedDate"
29: },
30:
31: "value": "2014-08-25T19:14:04.594Z"
32: },
33: {
34: "field": {
35: "refName": "Microsoft.VSTS.Common.ResolvedDate"
36: },
37:
38: "value": null
39: },
40: {
41: "field": {
42: "refName": "Microsoft.VSTS.Common.ResolvedBy"
43: },
44:
45: "value": null
46: },
47: {
48: "field": {
49: "refName": "Microsoft.VSTS.Common.ResolvedReason"
50: },
51:
52: "value": null
53: },
54: {
55: "field": {
56: "refName": "Microsoft.VSTS.Common.ClosedDate"
57: },
58:
59: "value": null
60: },
61: {
62: "field": {
63: "refName": "Microsoft.VSTS.Common.ClosedBy"
64: },
65: "value": null
66: }]
67: }
3. Conclusion
As you could see, changing work item state is very cumbersome and requires from a developer a chain of trials and errors, to find a solution for this problem. The VSO REST API unfortunately isn’t very helpful in this case. All fields you need to assign a value to, should be expected to be assigned with proper value by the server. Since the VSO REST API is still in preview, we can hope, that Microsoft will eventually implement this that way. This would save all of us some work. I have submitted a user voice for this issue, so if some one is also missing this functionality, he is kindly asked to vote for it here.
I hope, I could help people struggling with this issue and make the world a little bit better. All kind of feedback would be also appreciated, so don’t hesitate to write a comment below.
TFS ASAP (www.tfsasap.com) automatically changes the state of work items. Server side rules help to automate recurring TFS tasks.
Thanks for the great blog article Jakub! I’m the Program Manager at Microsoft responsible for the work item tracking APIs and I sympathize with the issues you and many of our customers have faced with the v1.1 of the APIs. Rest assured we hear the feedback loud and clear and are working diligently to improve the situation. Next week, we’ll be releasing v1.2 of the API which fixes many of the issues you’ve outlined. We have implemented support for the JSON-PATCH standard as well as implemented a server side rules engine so creating a work item is as simple as saying the title and you’re done! Making additional changes to a work item is just as simple and we’re excited to get feedback on the updates. One additional item on our backlog that we’re hoping to deliver in the following deployment is a server side enumeration of the states and transitions per work item type so that will address the other portion of your feedback. Please keep the feedback coming!
Thank you so much for the post. It looks like MSFT have fixed the issue of updating multiple fields. However , a code sample would be great if you have one for reference . I am able to do all query or get requests. But for some reason my work items fields don’t change values.