Getting Project List via Rally C# API - c#

I need a way to retrieve a list of projects using C#.
Tried doing something like this:
DynamicJsonObject sub = restApi.GetSubscription("Projects");
//query the project collection
Request wRequest = new Request(sub["Projects"]);
QueryResult queryResult = restApi.Query(wRequest);
return queryResult.Results.Select(result => new Project()
{
Id = result["ObjectID"],
Name = result["Name"]
}).ToList();
unfortunately with no success.
Can anyone help please?

The code below should print workspaces and projects to which the user whose account is used to authenticate the code has access to.
DynamicJsonObject sub = restApi.GetSubscription("Workspaces");
Request wRequest = new Request(sub["Workspaces"]);
wRequest.Limit = 1000;
QueryResult queryResult = restApi.Query(wRequest);
int allProjects = 0;
foreach (var result in queryResult.Results)
{
var workspaceReference = result["_ref"];
var workspaceName = result["Name"];
Console.WriteLine("Workspace: " + workspaceName);
Request projectsRequest = new Request(result["Projects"]);
projectsRequest.Fetch = new List<string>()
{
"Name"
};
projectsRequest.Limit = 10000; //project requests are made per workspace
QueryResult queryProjectResult = restApi.Query(projectsRequest);
int projectsPerWorkspace = 0;
foreach (var p in queryProjectResult.Results)
{
allProjects++;
projectsPerWorkspace++;
Console.WriteLine(projectsPerWorkspace + " Project: " + p["Name"] + " State: " + p["State"]);
}
}
Console.WriteLine("Returned " + allProjects + " projects in the subscription");

Related

WIQL Query not including "System.AssignedTo" Field

I have this WIQL, who's purpose is to find the user assigned to the work item.
var wiql = string.Format("SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State]" +
" FROM WorkItems" +
" WHERE ([System.TeamProject] = '{0}')" +
" AND ([System.WorkItemType] = 'Task' OR [System.WorkItemType] = 'Bug')" +
" ORDER BY [System.Id]", projectName);
I'm executing it as so...
Wiql wiql = new Wiql() { Query = wiqlQueryString };
using (var workItemTrackingHttpClient = new WorkItemTrackingHttpClient(VstsAccess.AccessUri, VstsAccess.AccessCredentials))
{
var workItemQueryResult = workItemTrackingHttpClient.QueryByWiqlAsync(wiql).Result;
if (workItemQueryResult != null && workItemQueryResult.WorkItemRelations.Any())
{
List<int> sourceIdList = new List<int>();
foreach (var item in workItemQueryResult.WorkItemRelations)
sourceIdList.Add(item.Target.Id);
int[] arr = sourceIdList.ToArray();
string[] fields = { "System.Id", "System.WorkItemType", "System.AssignedTo", "System.Title", "System.Description", "System.State", "System.IterationPath", "System.TeamProject", "System.ChangedDate", "System.ChangedBy", "System.CreatedDate" };
return workItemTrackingHttpClient.GetWorkItemsAsync(arr, fields, workItemQueryResult.AsOf).Result;
}
else
return new List<WorkItem>();
}
But the "AssignedTo" and "Description" fields are not showing up in the work items' dictionary field-set. Why is this so and how can I fix this?
The query results will only contain fields that are non-null, i.e. nobody is assigned to the work item, the field won't be in the Fields dictionary at all.
You need to implement a custom check for those fields and assign them to something according to your logic:
int[] arr = ids.ToArray();
string[] fields = new string[] {
"System.Id",
"System.Title",
"System.State",
"System.AssignedTo"
};
var workItems = await workItemTrackingHttpClient.GetWorkItemsAsync(arr, fields, workItemQueryResult.AsOf);
List<WorkItemData> list = new List<WorkItemData>();
foreach (var workItem in workItems)
{
var wi = new WorkItemData(workItem.Id.Value);
wi.Title = workItem.Fields["System.Title"].ToString();
wi.State = workItem.Fields["System.State"].ToString();
wi.AssignedTo = workItem.Fields.ContainsKey("System.AssignedTo") ? workItem.Fields["System.AssignedTo"].ToString() : "";
list.Add(wi);
}
You could use the code below to query out workitems and it has "AssignedTo" and "Description" field values.
WorkItemStore workItemStore = teamProjectCollection.GetService<WorkItemStore>();
string queryString = string.Format("SELECT [System.Id], [System.WorkItemType], [System.Title], [System.AssignedTo], [System.State]" +
" FROM WorkItems" +
" WHERE ([System.TeamProject] = '{0}')" +
" AND ([System.WorkItemType] = 'Task' OR [System.WorkItemType] = 'Bug')" +
" ORDER BY [System.Id]", "Mtt-Scrum"); ;
// Create and run the query.
Query query = new Query(workItemStore, queryString);
WorkItemCollection witCollection = query.RunQuery();
foreach (Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem in witCollection)
{
......
}

How to add/update milestones in a feature using rally rest API and C#?

I am not able to add or update milestones field for the Features in the Rally. If anyone having the code available using C# to update the same, please share with me. I am searching and doing from last one week with no luck.
When I am trying to add/Update milestones in the Features. I am getting the error as "Could not read: Could not read referenced object null". My code is as follows:-
public DynamicJsonObject UpdateFeaturesbyName(string fea, string bFun)
{
//getting list of Feature.
Request feat = new Request("PortfolioItem/Feature");
feat.Query = new Query("Name", Query.Operator.Equals, fea);
QueryResult TCSResults = restApi.Query(feat);
foreach (var res in TCSResults.Results)
{
var steps = res["Milestones"];
Request tsteps = new Request(steps);
QueryResult tstepsResults = restApi.Query(tsteps);
foreach (var item in tstepsResults.Results)
{
}
if (res.Name == fea)
{
var targetFeature = TCSResults.Results.FirstOrDefault();
DynamicJsonObject toUpdate = new DynamicJsonObject();
//toUpdate["Milestones"] = "";
// CreateResult createResult = restApi.Create(steps._ref, toUpdate);
// String contentRef = steps._ref;
//String contentRef = createResult._ref;
string[] value = null;
string AccCri = string.Empty;
if (!string.IsNullOrWhiteSpace(bFun))
{
value = bFun.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
foreach (string item in value)
{
//if (string.IsNullOrWhiteSpace(AccCri))
// AccCri = item;
//else
// AccCri = AccCri + "<br/>" + item;
if (!string.IsNullOrWhiteSpace(item))
{
//Query for Milestone.
Request ms = new Request("Milestone");
ms.Fetch = new List<string>() { "Name", "ObjectID" };
ms.Query = new Query("Name", Query.Operator.Equals, item);
QueryResult msResults = restApi.Query(ms);
var targetMLResult = msResults.Results.FirstOrDefault();
long MLOID = targetMLResult["ObjectID"];
DynamicJsonObject tarML = restApi.GetByReference("Milestone", MLOID, "Name", "_ref", "DisplayColor");
DynamicJsonObject targetML = new DynamicJsonObject();
targetML["Name"] = tarML["Name"];
//targetML["_ref"] = tarML["_ref"];
targetML["_ref"] = "/milestone/" + Convert.ToString(MLOID);
targetML["DisplayColor"] = tarML["DisplayColor"];
// Grab collection of existing Milestones.
var existingMilestones = targetFeature["Milestones"];
long targetOID = targetFeature["ObjectID"];
// Milestones collection on object is expected to be a System.Collections.ArrayList.
var targetMLArray = existingMilestones;
var tagList2 = targetMLArray["_tagsNameArray"];
tagList2.Add(targetML);//
//targetMLArray.Add(targetML);
targetMLArray["_tagsNameArray"] = tagList2;
toUpdate["Milestones"] = targetMLArray;
OperationResult updateResult = restApi.Update(res._ref, toUpdate);
bool resp = updateResult.Success;
}
}
}
//toUpdate["c_AcceptanceCriteria"] = AccCri;
//OperationResult updateResult = restApi.Update(res._ref, toUpdate);
}
}
var features = TCSResults.Results.Where(p => p.Name == fea).FirstOrDefault();
var featuresref = features._ref;
return features;
}
Now that v3.1.1 of the toolkit has been released you can use the AddToCollection method to do this.
Otherwise, you can still always just update the full collection. The value should be an arraylist of objects with _ref properties.
Check out this example (which adds tasks to defects, but should be very similar to what you're doing): https://github.com/RallyCommunity/rally-dot-net-rest-apps/blob/master/UpdateTaskCollectionOnDefect/addTaskOnDefect.cs

EnumerateFiles in Linq

I have LINQ to Entities does not recognize the method 'System.Collections.Generic.IEnumerable1[System.String] EnumerateFiles(System.String)' method, and this method cannot be translated into a store expression.` error. I need to get links to images from folders, I get ID from the database.
// Collect flat items and add in List<>
var nearestItems = from item in _db.Flats
select new listItem()
{
Price = item.Price,
Address = item.Address,
Bathroom = item.Bathroom,
BesprovodnoiInternet = item.BesprovodnoiInternet,
City = item.City,
FloorAll = item.FloorAll,
FloorCurrent = item.FloorCurrent,
Funiture = item.Funiture,
Kondicioner = item.Kondicioner,
PartyFree = item.PartyFree,
RoomQuantity = item.RoomQuantity,
TipArendy = item.TipArendy,
TV = item.TV,
ImagesString = Directory.EnumerateFiles(Server.MapPath("~/Content/Prop/" + item.FlatID + "/"))
.Select(fn => "~/Content/Prop/" + item.FlatID + "/" + Path.GetFileName(fn)).ToList()
};
Are there fix for this or alternate code?
Your LINQ query should be translated to SQL query to run on SQL Server. It is obvious that the engine cant translate Directory.EnumerateFiles to SQL query.
You can add new property FlatId to your listItem and try this:
// Collect flat items and add in List<>
var nearestItems = (from item in _db.Flats
select new listItem()
{
Price = item.Price,
Address = item.Address,
Bathroom = item.Bathroom,
BesprovodnoiInternet = item.BesprovodnoiInternet,
City = item.City,
FloorAll = item.FloorAll,
FloorCurrent = item.FloorCurrent,
Funiture = item.Funiture,
Kondicioner = item.Kondicioner,
PartyFree = item.PartyFree,
RoomQuantity = item.RoomQuantity,
TipArendy = item.TipArendy,
TV = item.TV,
FlatId = item.FlatID,
}).ToList();
foreach(var item in nearestItems)
{
item.ImagesString = Directory.EnumerateFiles(Server.MapPath("~/Content/Prop/" + item.FlatId + "/"))
.Select(fn => "~/Content/Prop/" + item.FlatId + "/" + Path.GetFileName(fn)).ToList();
}
EntityFramework will build query based on your LINQ which will execute in the database. So there are some constarints while using LINQ to Entity. How must EF convert this code to the database query? Directory.EnumerateFiles
There is no way.
So, you must select only required properties and then change them as you wish in .net:
var nearestItems = (from item in _db.Flats
select new listItem()
{
Price = item.Price,
Address = item.Address,
Bathroom = item.Bathroom,
BesprovodnoiInternet = item.BesprovodnoiInternet,
City = item.City,
FloorAll = item.FloorAll,
FloorCurrent = item.FloorCurrent,
Funiture = item.Funiture,
Kondicioner = item.Kondicioner,
PartyFree = item.PartyFree,
RoomQuantity = item.RoomQuantity,
TipArendy = item.TipArendy,
TV = item.TV,
FlatId = item.FlatID,
}).ToList();
And in you class change the get accessor of your property ImagesString:
public List<string> ImagesString
{
get
{
return Directory.EnumerateFiles(Server.MapPath("~/Content/Prop/" + FlatID + "/"))
.Select(fn => "~/Content/Prop/" + FlatID + "/" + Path.GetFileName(fn))
.ToList();
}
}

Rally Api - How do I page GetByReference results

I've got some code that looks something like this.
var userStory = restApi.GetByReference (userStoryRef, "Name", "FormattedID");
for (int i = 0; i < (int) userStory["TotalResultCount"]; i++)
{
var name = userStory["Results"][i]["Name"];
...
Of course this fails if the GetByReference has more than 20 results (i.e. TotalResultCount > 20) since the default page size is 20.
So I need to page this but I can't work out how you set the page size, or request a second page from GetByReference.
I tried adding ?pagesize=100 to the reference but it doesn't appear to affect the result set.
e.g.
var userStory = restApi.GetByReference (userStoryRef + "?pagesize=100", "Name", "FormattedID");
I also tried giving up on this altogether and re-phrasing it as a query (which I do know how to page.)
e.g.
var request = new Request("HierarchicalRequirement")
{
Fetch = new List<string>()
{
"Name", "FormattedID"
}, Query = new Query("Parent.FormattedID", Query.Operator.Equals, featureId)
};
var result = myRestApi.Query(request);
But this query doesn't work for me either (featureId is a valid formatted id with associated User Stories). But that query returns no results.
(I appreciate that's a different question altogether - happy to get either working but understanding both would be fantastic!)
Grateful as ever for any help.
See code below. Notice storiesRequest.Limit = 1000 When this number is above default 20, it defaults to max 200, so there is no need to set storiesRequest.PageSize to 200
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Rally.RestApi;
using Rally.RestApi.Response;
using Rally.RestApi.Json;
namespace GetByRefExample
{
class Program
{
static void Main(string[] args)
{
RallyRestApi restApi = new RallyRestApi(webServiceVersion: "v2.0");
String apiKey = "_abc123";
restApi.Authenticate(apiKey, "https://rally1.rallydev.com", allowSSO: false);
String workspaceRef = "/workspace/123";
String projectRef = "/project/456";
Request request = new Request("PortfolioItem/Feature");
request.Fetch = new List<string>() { "Name", "FormattedID" };
request.Query = new Query("FormattedID", Query.Operator.Equals, "F2356");
QueryResult result = restApi.Query(request);
String featureRef = result.Results.First()._ref;
Console.WriteLine("found" + featureRef);
//create stories
try
{
for (int i = 1; i <= 25; i++)
{
DynamicJsonObject story = new DynamicJsonObject();
story["Name"] = "story " + i;
story["PlanEstimate"] = new Random().Next(2,10);
story["PortfolioItem"] = featureRef;
story["Project"] = projectRef;
CreateResult createResult = restApi.Create(workspaceRef, "HierarchicalRequirement", story);
story = restApi.GetByReference(createResult.Reference, "FormattedID");
Console.WriteLine("creating..." + story["FormattedID"]);
}
//read stories
DynamicJsonObject feature = restApi.GetByReference(featureRef, "UserStories");
Request storiesRequest = new Request(feature["UserStories"]);
storiesRequest.Fetch = new List<string>()
{
"FormattedID",
"PlanEstimate"
};
storiesRequest.Limit = 1000;
QueryResult storiesResult = restApi.Query(storiesRequest);
int storyCount = 0;
foreach (var userStory in storiesResult.Results)
{
Console.WriteLine(userStory["FormattedID"] + " " + userStory["PlanEstimate"]);
storyCount++;
}
Console.WriteLine(storyCount);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}

How can I retrieve a list of workitems from TFS in C#?

I'm trying to write a project reporting tool in WPF / C#. I want to access all the project names on our TFS (Team Foundation Server), and then display statistics for each work item in a given project.
I've got the project names, but getting the actual work items is what's giving me a hard time. Here's what I've got so far:
public const string tfsLocation = "http://whatever";
// get the top list of project names from the team foundation server
public List<string> LoadProjectList()
{
var tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsLocation));
var workItemStore = new WorkItemStore(tpc);
var projects = (from Project project in workItemStore.Projects select project.Name).ToList();
return projects;
}
public string GetProjectInfo(string targetProject)
{
string info = String.Empty;
var tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsLocation));
var workItemStore = new WorkItemStore(tpc);
foreach (Project project in workItemStore.Projects)
{
if (project.Name == targetProject)
{
info += String.Format("Project: {0}\n\n", project.Name);
info += "Work Item Types:\n";
foreach (WorkItemType item in project.WorkItemTypes)
{
info += String.Format("- {0}\n", item.Name);
info += String.Format(" - Description: {0}\n", item.Description);
info += " - Field Definitions:\n";
foreach (FieldDefinition field in item.FieldDefinitions)
{
info += String.Format(" - {0}\n", field.Name);
}
info += "\n";
}
}
}
return info;
}
GetProjectInfo sends back some helpful info about what's in each project, but so far it looks like I'm only seeing the definitions of what the WorkItems consist of, and not the actual WorkItems themselves. I think the programming I've written is looking in the wrong place.
From Microsoft's definition of WorkItem,
(http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.workitemtracking.client.workitem.aspx)
it looks like it's inside WorkItemTracking.Client, but not inside the WorkItemStore, and I'm not sure where to go to access it.
FINAL VERSION:
Here's the updated version of my function, after referencing the below answer. This just returns a long string of the work item names with new lines between, for printing out, which is all I'm trying to get working (for now).
public string GetProjectInfo(string targetProject)
{
string info = String.Empty;
var tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsLocation));
WorkItemStore workItemStore = new WorkItemStore(tpc);
Query query = new Query(workItemStore, "SELECT * FROM WorkItems WHERE [System.TeamProject] = #project", new Dictionary<string, string>() { { "project", targetProject } });
WorkItemCollection wic = query.RunQuery();
foreach (WorkItem item in wic)
{
info += String.Format("{0}\n", item.Title);
}
return info;
}
You need to use WIQL queries to get actual work items you are interested in, e.g. to get all work items for a particular project:
using Microsoft.TeamFoundation.WorkItemTracking.Client;
Query query = new Query(
workItemStore,
"select * from issue where System.TeamProject = #project",
new Dictionary<string, string>() { { "project", project.Name } }
);
var workItemCollection = query.RunQuery();
foreach(Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem in workItemCollection)
{
/*Get work item properties you are interested in*/
foreach(Microsoft.TeamFoundation.WorkItemTracking.Client.Field field in workItem.Fields)
{
/*Get field value*/
info += String.Format("Field name: {0} Value: {1}\n", field.Name, field.Value);
}
}
I do need to extract the linked Work Item (Testcases) as well with the Bug. I have created a query and it extracts both. But my issue is while i print the Work Items Fields, All of the prints separately, with no trace of which Bug is linked to which Testcase. How can I achieve that.
public async Task<IList<WorkItem>> QueryOpenBugs(string project)
{
var credentials = new VssBasicCredential(string.Empty, this.personalAccessToken);
// create a wiql object and build our query
var wiql = new Wiql()
{
// NOTE: Even if other columns are specified, only the ID & URL are available in the WorkItemReference
//Query = "Select [Id] " +
// "From WorkItems " +
// "Where [Work Item Type] = 'Bug' " +
// "And [System.TeamProject] = '" + project + "' " +
// "And [System.State] = 'Resolved' " +
// "Order By [State] Asc, [Changed Date] Desc",
Query = "Select [System.Id],[System.WorkItemType],[System.Title]" +
"From workitemLinks " +
"Where ([Source].[System.WorkItemType] = 'Bug' " +
"And [Source].[System.TeamProject] = '" + project + "' " +
"And [Source].[System.State] = 'Resolved' )" +
"And ([Target].[System.TeamProject] = '" + project + "' " +
"And [Target].[System.WorkItemType] = 'Test Case' )",
};
using (var httpClient = new WorkItemTrackingHttpClient(this.uri, credentials))
{
// execute the query to get the list of work items in the results
var result = await httpClient.QueryByWiqlAsync(wiql).ConfigureAwait(false);
var ids = result.WorkItemRelations.Select(item => item.Target.Id).ToArray();
// some error handling
if (ids.Length == 0)
{
return Array.Empty<WorkItem>();
}
// build a list of the fields we want to see
var fields = new[] { "System.Id", "System.Title", "System.State" , "System.IterationPath", "System.Tags", "Microsoft.VSTS.Common.StateChangeDate", "System.WorkItemType", "Microsoft.VSTS.TCM.AutomationStatus"};
// get work items for the ids found in query
return await httpClient.GetWorkItemsAsync(ids, fields, result.AsOf).ConfigureAwait(false);
}
}
/// <summary>
/// Execute a WIQL (Work Item Query Language) query to print a list of open bugs.
/// </summary>
/// <param name="project">The name of your project within your organization.</param>
/// <returns>An async task.</returns>
public async Task PrintOpenBugsAsync(string project)
{
var workItems = await this.QueryOpenBugs(project).ConfigureAwait(false);
Console.WriteLine("Query Results: {0} items found", workItems.Count);
// loop though work items and write to console
//Select - BugID , TestCaseID , TestSuiteID{} , ResolvedDate , AutomationStatus{}
foreach (var workItem in workItems)
{
string WorkItemType = (string)workItem.Fields["System.WorkItemType"];
if (WorkItemType == "Bug")
{
Console.WriteLine("The Bugs are:\n\n");
Console.WriteLine(
"{0}\t{1}\t{2}\t{3}\t{4}",
workItem.Id,
workItem.Fields["System.Title"],
workItem.Fields["System.State"],
// workItem.Fields["System.RelatedLinks"],
workItem.Fields["Microsoft.VSTS.Common.StateChangeDate"],
workItem.Fields["System.WorkItemType"]);
Console.WriteLine("\n");
}
else
{
Console.WriteLine("The TestCases are:\n\n");
Console.WriteLine(
"{0}\t{1}\t{2}\t{3}\t{4}",
workItem.Id,
workItem.Fields["System.Title"],
workItem.Fields["System.State"],
workItem.Fields["Microsoft.VSTS.TCM.AutomationStatus"],
workItem.Fields["System.WorkItemType"]);
Console.WriteLine("\n");
}

Categories