Populating the TFS WorkItem revisions collection - tfs

I am writing a tool that needs to access all the revisions of a TFS workitem template.
The Workitem has a Revisons collection, and Rev property that returns the number of revisions.
When I try and do a foreach through the collection, even though it contains 6 "entries" in my test workitem, the collection is empty.
To work around this I am using the GetWorkItem(WorkItemID, RevisionID), incrementing the revision ID in a for loop to get the revisions. It seems crazy taht I have to do this and there collection that doesn't contain what it is supposed to.
Am I missing something here, or is this simply a bug in the TFS client API.

After much digging, it is quite clear to me now that if you want to get all the revisions of a work item, you must explicitly load the revision(2) that you want, and this makes the revisions collection pretty much useless.

Depending on how your retrieving the work item it may only be partially loaded. Try calling the Open method on the work item before accessing the Revisions collection.

Where are you getting the workitem? I know when I was getting version history of files with sourceControl.QueryHistory I had to set one of my parameters (bool include Changes) to true in order to get the changes in the changeset.

I'm using the Microsoft.TeamFoundation.Controls.PickWorkItemsControl to select the work items I need. After that the revsionsCollectoin is compleet. Maybe this helps:
// select the workitems using the picker
ArrayList workItems = _workItemPicker.Control.SelectedWorkItems();
// after that use a foreach and output all history included in each revision
private void PrintHistory(WorkItem workitem)
{
RevisionCollection revisions = workitem.Revisions;
foreach (Revision revision in revisions)
{
String history = (String) revision.Fields["History"].Value;
Console.WriteLine("**** Revision {0}", revision.Fields["Title"], revision.Fields["Changed Date"]);
foreach (Field field in revision.Fields)
{
Console.WriteLine("* field {0}:{1} ", field.Name, field.Value);
}
Console.WriteLine("****");
Console.WriteLine();
}
}

Related

Microsoft Graph API remove Check List Item

I have a plannerTask and in its Details it has a CheckList. I use it to programatically insert CheckListItems in it, and it all works like a charm when inserting or retrieving the tasks.
My problem arrives when I am going to insert a new CheckListItem and the CheckList already has 20 items. It returns a MaximumChecklistItemsOnTask (because it is forbidden to insert more than 20 items in a check list).
Solution could be to remove the oldest item, but I am not able to do it. I have tried this:
var elementToRemove = oldDetails.Checklist.Where(c => c.Value.IsChecked).OrderBy(c => c.Value.LastModifiedDateTime).First();
oldDetails.Checklist = oldDetails.Checklist.Where(c => c.Value.LastModifiedDateTime <> elementToRemove.Value.LastModifiedDateTime);
But it throws a casting error in the second line:
Unable to cast object of type
'WhereEnumerableIterator1[System.Collections.Generic.KeyValuePair2[System.String,Microsoft.Graph.PlannerChecklistItem]]'
to type 'Microsoft.Graph.PlannerChecklistItems'.
Which is the right way to remove the oldest element from the ChecklistItem?
UPDATE:
In first place I retrieve a plannerTask from the server. Then I get the details from this plannerTask. So oldDetails is a plannertaskdetails object (https://learn.microsoft.com/en-us/graph/api/resources/plannertaskdetails?view=graph-rest-1.0). Inside the plannertaskdetails object (oldDetails), I have the plannerchecklistitems object (oldDetails.Checklist): https://learn.microsoft.com/en-us/graph/api/resources/plannerchecklistitems?view=graph-rest-1.0.
If plannerchecklistitems were just a List, it would be as easy as list.Remove(item), but it is not a normal list, and that is why I am not able to remove the item.
UPDATE 2:
I have found this way to remove the item from oldDetails:
oldDetails.Checklist.AdditionalData.Remove(elementToRemove.Key)
But, the the way I send the changes to the server is this:
await graphClient.Planner.Tasks(plannerTask.Id).Details.Request().Header("If-Match", oldDetails.GetEtag).UpdateAsync(newDetails)
As it is a PATCH request (not a PUT one), I only have in newDetails the records that have changed, it is, the new records. How could I specify there that a record has been deleted from the list? Sorry if my English is not good enough to express myself properly, but what I mean is that newDetails is not the full list, it only contains the records that must be added and I do not know how to specify in that request that one record must be deleted.

Looping through list of objects created by createEntry() and editing their properties before doing submitChange()

Good afternoon fellow developers,
I have come across a scenario where I found myself needing to retrieve the list of pending changes from my model and editing a specific property of those entries before sending them to my back-end.
These are new entities I created using the createEntry() method of the OData model v2. But, at the time of creation of said entities, I do not possess the value I need to add to them yet. This is the list of entities I retrieve by using the getPendingChanges() method on my model:
What I need to do is to loop through each of these newly created entities and set a specific property into them before actually sending them to my back-end with the submitChanges() method. Bare in mind that these are entry objects created by the createEntry() method and exist only in my front-end until I am able to submit them with success.
Any ideas that might point me in the right direction? I look forward to reading from you!
I was able to solve this issue in the following way:
var oPendingChanges = this.model.getPendingChanges();
var aPathsPendingChanges = $.map(oPendingChanges, function(value, index) { return [index];});
aPathsPendingChanges.forEach(sPath => oModel.setProperty("/" + sPath + "/PropertyX","valueFGO"));
The first two instructions retrieve the entire list of pendingChanges objects and then builds an array of paths to each individual entry. I then use that array of paths to loop through my list of pending changes and edit into the property I want in each iteration of the loop. Special thanks to the folks at answers.sap for the guidance!

TF26180: An item with this ID already exists in the input Array

I am using below flat query to get bug details for array of work item ids passed as argument.
Below query works perfectly fine, but it throws error in some cases "TF26180: An item with this ID already exists in the input Array"
Reason is, ids array has duplicate ids, which is expected in my case. Is there any way to ignore this check or by pass this ?
var flatQuery = new Query(_store, detailsWiql.ToString(), ids);
WorkItemCollection workitems = flatQuery.RunQuery();
foreach (WorkItem wi in workitems)
{
WorkItemType worktype = wi.Type;
worktypename = worktype.Name;
}
Thanks in advance
I don't think we can ignore or by pass this check, the exception is defined in Microsoft.TeamFoundation.WorkItemTracking.Common assembly. See https://www.powershellgallery.com/packages/PSPlus.Tfs/0.0.1.104/Content/Microsoft.TeamFoundation.WorkItemTracking.Common.xml
There is a doubt here, why you have duplicated work items?
Generally, the work item ID is unique in TFS server.
I suggest you delete the duplicated work items first, backup them if needed, then create new work items to track the related tasks/features.
UPDATE:
As a workaround, you can try using the WIQL query with REST API to get the IDs first, See A flat query. The write code to serach work item details with ID in loop. See Get a workitem with REST API.
Besides, you can have a try with the Wiql Editor, it can query the basic work item information directly.
I had a similar issue, so when I was creating the ids array, I just ensured it was distinct:
// Capture ids of results in an array
var ids = (from WorkItemLinkInfo info in result select info.TargetId).Distinct().ToArray();
// Query a list of WorkItem IDs we want more info about
wiql = #"SELECT * FROM WorkItems";
q = new Query(App.workItemStore, wiql, ids);
var workItems = q.RunQuery();

TFS 2015. Get CollectionName by item

I have next situation and I am stack.
I am using Microsoft.TeamFoundation classes to get workitems from stored query with c#. I know server name, project name and stored query name. So I can execute stored query and receive all WorkItems.
But than I have to create a direct link to this item. According to the documentation, this link should be like
http:// ServerName:8080/tfs/CollectionName/ProjectName/_workitems/edit/Id
So, before executing query I know ServerName, ProjectName and work item's Id. But I cant find ProjectName anywhere.
So my question is. How, knowing ServerName, ProjectName, WorkItemId and Stored Query name get CollectionName?
Or. How to create link to the work item knowing ItemsId?
P.S. GetArtifactUrl is not the right way.
Thanks for help!
Afraid there is no such API to achieve this. However, as a workaround, you can list all collection's name through client API such as below:
Uri configurationServerUri = new Uri(URL);
TfsConfigurationServer configurationServer = TfsConfigurationServerFactory.GetConfigurationServer(configurationServerUri);
ITeamProjectCollectionService tpcService = configurationServer.GetService<ITeamProjectCollectionService>();
foreach (TeamProjectCollection tpc in tpcService.GetCollections())
{
ListCollection.Add(tpc.Name);
}
More ways for your reference:
How to get all collections from TFS
Retrieve the List of Team Project Collections from TFS 2010 Client APIs

TFS Macro for current Area/Team?

We've moved to the "Single Team Project" way of working, and thus our different teams are split up into different areas (because that works for us).
Our URLs are http://tfsserver/ProjectA, http://tfsserver//ProjectB etc.. etc.. which auto populates the Area, and thus makes sure all the work items go into the correct place.
However, all our queries are very simple for each team. This has led us to have query folders which duplicate many of the queries, but scope down to single areas, to make sure that query only targets the right work items.
Is there a #CurrentTeam macro which we can apply to our queries to prevent duplicating all our queries?
We can't be the only people with this problem can we - or we're doing it wrong.
No, there's no such thing right now in TFS. The only predefined macros are: #project, #me and #today. You can create your own macros when using TFS object model for running queries but there's no way to do it in the UI.
Sample code:
var tpc = new TfsTeamProjectCollection(new Uri("http://localhost:8080/tfs/DefaultCollection"));
var store = tpc.GetService<WorkItemStore>();
Query query = new Query(store, "select * from workitems where System.AreaPath = #TeamArea", new Dictionary<string, string>() { { "TeamArea", "<Team area path>" } });
query.RunQuery();

Resources