aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Tango.TFS
diff options
context:
space:
mode:
authorShlomo Hecht <shlomo@twine-s.com>2018-05-02 17:36:54 +0300
committerShlomo Hecht <shlomo@twine-s.com>2018-05-02 17:36:54 +0300
commitee697f7a3350d0a97bddee4de3a2ae4f9d285052 (patch)
tree2dc2e3bb811b0d89a3c4c51801c1572966fcee7c /Software/Visual_Studio/Tango.TFS
parent73c4b814f1f28170ae72723568189096413c3564 (diff)
downloadTango-ee697f7a3350d0a97bddee4de3a2ae4f9d285052.tar.gz
Tango-ee697f7a3350d0a97bddee4de3a2ae4f9d285052.zip
merge
Diffstat (limited to 'Software/Visual_Studio/Tango.TFS')
-rw-r--r--Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs36
-rw-r--r--Software/Visual_Studio/Tango.TFS/ResolvedReason.cs27
-rw-r--r--Software/Visual_Studio/Tango.TFS/Tango.TFS.csproj5
-rw-r--r--Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs248
-rw-r--r--Software/Visual_Studio/Tango.TFS/WorkItem.cs52
5 files changed, 327 insertions, 41 deletions
diff --git a/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs b/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs
index 51394f663..1fe169218 100644
--- a/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs
+++ b/Software/Visual_Studio/Tango.TFS/ITeamFoundationServiceClient.cs
@@ -35,6 +35,42 @@ namespace Tango.TFS
Task<WorkItem> GetWorkItem(Project project, int id);
/// <summary>
+ /// Gets all the work items created by the specified team member.
+ /// </summary>
+ /// <param name="project">The project.</param>
+ /// <param name="member">The member.</param>
+ /// <returns></returns>
+ Task<List<WorkItem>> GetWorkItemsCreatedBy(Project project, TeamMember member);
+
+ /// <summary>
+ /// Sets the state of the work item.
+ /// </summary>
+ /// <param name="project">The project.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="state">The state.</param>
+ /// <returns></returns>
+ Task<WorkItem> SetWorkItemState(Project project, WorkItem item, State state);
+
+ /// <summary>
+ /// Sets the work item assignment.
+ /// </summary>
+ /// <param name="project">The project.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="member">The member.</param>
+ /// <returns></returns>
+ Task<WorkItem> SetWorkItemAssignment(Project project, WorkItem item, TeamMember member);
+
+ /// <summary>
+ /// Adds a comment to the work item discussion.
+ /// </summary>
+ /// <param name="project">The project.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="teamMember">Team member</param>
+ /// <param name="comment">The comment.</param>
+ /// <returns></returns>
+ Task<WorkItem> AddWorkItemComment(Project project, WorkItem item, TeamMember teamMember, String comment);
+
+ /// <summary>
/// Deletes the specified work item.
/// </summary>
/// <param name="project">The project.</param>
diff --git a/Software/Visual_Studio/Tango.TFS/ResolvedReason.cs b/Software/Visual_Studio/Tango.TFS/ResolvedReason.cs
new file mode 100644
index 000000000..e1af689a2
--- /dev/null
+++ b/Software/Visual_Studio/Tango.TFS/ResolvedReason.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Tango.TFS
+{
+ public enum ResolvedReason
+ {
+ [Description("As Designed")]
+ AsDesigned,
+ [Description("Cannot Reproduce")]
+ CannotReproduce,
+ [Description("Copied To Backlog")]
+ CopiedToBacklog,
+ [Description("Deferred")]
+ Deferred,
+ [Description("Duplicate")]
+ Duplicate,
+ [Description("Fixed")]
+ Fixed,
+ [Description("Obsolete")]
+ Obsolete,
+ }
+}
diff --git a/Software/Visual_Studio/Tango.TFS/Tango.TFS.csproj b/Software/Visual_Studio/Tango.TFS/Tango.TFS.csproj
index 00c9171c3..cf15c2e9e 100644
--- a/Software/Visual_Studio/Tango.TFS/Tango.TFS.csproj
+++ b/Software/Visual_Studio/Tango.TFS/Tango.TFS.csproj
@@ -204,6 +204,7 @@
<Compile Include="Iteration.cs" />
<Compile Include="Priority.cs" />
<Compile Include="Project.cs" />
+ <Compile Include="ResolvedReason.cs" />
<Compile Include="Severity.cs" />
<Compile Include="State.cs" />
<Compile Include="Tag.cs" />
@@ -244,6 +245,10 @@
<Project>{a34ee0f0-649d-41c8-8489-b6f1cc6924ee}</Project>
<Name>Tango.Core</Name>
</ProjectReference>
+ <ProjectReference Include="..\Tango.Logging\Tango.Logging.csproj">
+ <Project>{bc932dbd-7cdb-488c-99e4-f02cf441f55e}</Project>
+ <Name>Tango.Logging</Name>
+ </ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Microsoft.TeamFoundationServer.ExtendedClient.15.112.1\build\Microsoft.TeamFoundationServer.ExtendedClient.targets" Condition="Exists('..\packages\Microsoft.TeamFoundationServer.ExtendedClient.15.112.1\build\Microsoft.TeamFoundationServer.ExtendedClient.targets')" />
diff --git a/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs b/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs
index e6390260e..6eef1ef92 100644
--- a/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs
+++ b/Software/Visual_Studio/Tango.TFS/TeamFoundationServiceClient.cs
@@ -31,6 +31,9 @@ namespace Tango.TFS
public const String PRIORITY = "Microsoft.VSTS.Common.Priority";
public const String STEPS_TO_REP = "Microsoft.VSTS.TCM.ReproSteps";
public const String SYSTEM_INFO = "Microsoft.VSTS.TCM.SystemInfo";
+ public const String RESOLVED_BY = "Microsoft.VSTS.Common.ResolvedBy";
+ public const String RESOLVED_DATE = "Microsoft.VSTS.Common.ResolvedDate";
+ public const String RESOLVED_REASON = "Microsoft.VSTS.Common.ResolvedReason";
}
#endregion
@@ -86,14 +89,17 @@ namespace Tango.TFS
VssConnection connection = CreateConnection();
+ LogManager.Log("Retrieving project " + name + " details...");
ProjectHttpClient projectClient = connection.GetClient<ProjectHttpClient>();
- TeamProjectReference project = projectClient.GetProjects(null).Result.FirstOrDefault(x => x.Name == name);
+ TeamProjectReference project = projectClient.GetProjects().Result.FirstOrDefault(x => x.Name == name);
if (project == null)
{
- throw new ArgumentException(String.Format("Project '{0}' could not be found.", name));
+ throw LogManager.Log(new ArgumentException(String.Format("Project '{0}' could not be found.", name)));
}
+ LogManager.Log("Project details successfully retrieved.");
+
p.Name = project.Name;
p.ID = project.Id;
p.URL = project.Url;
@@ -128,7 +134,7 @@ namespace Tango.TFS
});
}
- var projCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(CollectionURL));
+ var projCollection = new TfsTeamProjectCollection(new Uri(CollectionURL), connection.Credentials);
var store = projCollection.GetService<WorkItemStore>();
WorkItemCollection queryResults = store.Query("Select [State], [Title] " + "From WorkItems " + "Where [Work Item Type] = 'User Story'");
@@ -213,6 +219,17 @@ namespace Tango.TFS
});
}
+ if (!String.IsNullOrWhiteSpace(workItem.Comment))
+ {
+ patchDocument.Add(new JsonPatchOperation
+ {
+ Operation = Operation.Add,
+ Path = GetFieldNameForWrite(CoreField.History),
+ Value = workItem.Comment,
+ From = workItem.CreatedBy.AssignName
+ });
+ }
+
patchDocument.Add(new JsonPatchOperation
{
Operation = Operation.Add,
@@ -367,72 +384,150 @@ namespace Tango.TFS
{
return Task.Factory.StartNew<WorkItem>(() =>
{
- WorkItem workItem = new WorkItem();
-
var connection = CreateConnection();
WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>();
var item = witClient.GetWorkItemAsync(id, expand: Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItemExpand.All).Result;
- workItem.ID = item.Id.Value;
- workItem.Title = item.Fields[GetFieldNameForRead(CoreField.Title)].ToString();
- workItem.Description = TryGetField(item.Fields, GetFieldNameForRead(CoreField.Description));
- workItem.Area = new Area()
- {
- Path = item.Fields[GetFieldNameForRead(CoreField.AreaPath)].ToString(),
- Name = Path.GetFileName(item.Fields[GetFieldNameForRead(CoreField.AreaPath)].ToString()),
- };
- workItem.Iteration = new Iteration()
- {
- Path = item.Fields[GetFieldNameForRead(CoreField.IterationPath)].ToString(),
- Name = Path.GetFileName(item.Fields[GetFieldNameForRead(CoreField.IterationPath)].ToString()),
- };
+ return ConvertToWorkItem(project, item);
+ });
+ }
- workItem.AssignedTo = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.AssignedTo)));
- workItem.CreatedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.CreatedBy)));
- workItem.ChangedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.ChangedBy)));
- workItem.AuthorizedAs = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.AuthorizedAs)));
+ /// <summary>
+ /// Deletes the specified work item.
+ /// </summary>
+ /// <param name="project">The project.</param>
+ /// <param name="id">The identifier.</param>
+ /// <returns></returns>
+ public Task DeleteWorkItem(Project project, int id)
+ {
+ return Task.Factory.StartNew(() =>
+ {
+ var connection = CreateConnection();
- workItem.Type = (WorkItemType)Enum.Parse(typeof(WorkItemType), item.Fields[GetFieldNameForRead(CoreField.WorkItemType)].ToString());
- workItem.URL = item.Url;
+ WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>();
+ var result = witClient.DeleteWorkItemAsync(id, true).Result;
+ });
+ }
- if (item.Fields.ContainsKey(GetFieldNameForRead(CoreField.Tags)))
+ /// <summary>
+ /// Gets all the work items created by the specified team member.
+ /// </summary>
+ /// <param name="project">The project.</param>
+ /// <param name="member">The member.</param>
+ /// <returns></returns>
+ public Task<List<WorkItem>> GetWorkItemsCreatedBy(Project project, TeamMember member)
+ {
+ return Task.Factory.StartNew<List<WorkItem>>(() =>
+ {
+ var connection = CreateConnection();
+
+ var projCollection = new TfsTeamProjectCollection(new Uri(CollectionURL), connection.Credentials);
+ var store = projCollection.GetService<WorkItemStore>();
+
+ WorkItemCollection queryResults = store.Query(String.Format("Select [Id]" + "From WorkItems " + "Where [Created By] = '{0}' And [Work Item Type] = 'Bug'", member.AssignName));
+ var ids = queryResults.OfType<Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem>().Where(x => x.Project.Name == project.Name).ToList().Select(x => x.Id).ToList();
+
+ if (ids.Count == 0) return new List<WorkItem>();
+
+ WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>();
+
+ var items = witClient.GetWorkItemsAsync(ids).Result;
+
+ return items.Select(x => ConvertToWorkItem(project, x)).ToList();
+ });
+ }
+
+ /// <summary>
+ /// Sets the state of the work item.
+ /// </summary>
+ /// <param name="project">The project.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="state">The state.</param>
+ /// <returns></returns>
+ public Task<WorkItem> SetWorkItemState(Project project, WorkItem item, State state)
+ {
+ return Task.Factory.StartNew<WorkItem>(() =>
+ {
+ var connection = CreateConnection();
+
+ WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>();
+
+ var patchDocument = new JsonPatchDocument();
+
+ patchDocument.Add(new JsonPatchOperation
{
- List<String> tags = item.Fields[GetFieldNameForRead(CoreField.Tags)].ToString().Split(';').Select(x => x.Trim()).ToList();
- workItem.Tags = tags.Select(x => new Tag() { Name = x }).ToList();
- }
+ Operation = Operation.Replace,
+ Path = GetFieldNameForWrite(CoreField.State),
+ Value = state.ToString(),
+ });
- workItem.FoundInBuild = item.Fields[ExtensionFields.FOUND_IN_BUILD].ToString();
+ var updatedItem = witClient.UpdateWorkItemAsync(patchDocument, item.ID).Result;
- workItem.State = (State)Enum.Parse(typeof(State), item.Fields[GetFieldNameForRead(CoreField.State)].ToString());
+ return ConvertToWorkItem(project, updatedItem);
+ });
+ }
- workItem.Severity = ParseEnumByDescription<Severity>(item.Fields[ExtensionFields.SEVERITY].ToString());
+ /// <summary>
+ /// Adds a comment to the work item discussion.
+ /// </summary>
+ /// <param name="project">The project.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="teamMember">Team member</param>
+ /// <param name="comment">The comment.</param>
+ /// <returns></returns>
+ public Task<WorkItem> AddWorkItemComment(Project project, WorkItem item, TeamMember teamMember, String comment)
+ {
+ return Task.Factory.StartNew<WorkItem>(() =>
+ {
+ var connection = CreateConnection();
- workItem.Priority = (Priority)int.Parse(item.Fields[ExtensionFields.PRIORITY].ToString());
+ WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>();
- workItem.StepsToReproduce = item.Fields[ExtensionFields.STEPS_TO_REP].ToString();
+ var patchDocument = new JsonPatchDocument();
- workItem.SystemInformation = item.Fields[ExtensionFields.SYSTEM_INFO].ToString();
+ patchDocument.Add(new JsonPatchOperation
+ {
+ Operation = Operation.Add,
+ Path = GetFieldNameForWrite(CoreField.History),
+ Value = comment,
+ From = teamMember.AssignName
+ });
- return workItem;
+ var updatedItem = witClient.UpdateWorkItemAsync(patchDocument, item.ID).Result;
+
+ return ConvertToWorkItem(project, updatedItem);
});
}
/// <summary>
- /// Deletes the specified work item.
+ /// Sets the work item assignment.
/// </summary>
/// <param name="project">The project.</param>
- /// <param name="id">The identifier.</param>
+ /// <param name="item">The item.</param>
+ /// <param name="member">The member.</param>
/// <returns></returns>
- public Task DeleteWorkItem(Project project, int id)
+ public Task<WorkItem> SetWorkItemAssignment(Project project, WorkItem item, TeamMember member)
{
- return Task.Factory.StartNew(() =>
+ return Task.Factory.StartNew<WorkItem>(() =>
{
var connection = CreateConnection();
WorkItemTrackingHttpClient witClient = connection.GetClient<WorkItemTrackingHttpClient>();
- var result = witClient.DeleteWorkItemAsync(id, true).Result;
+
+ var patchDocument = new JsonPatchDocument();
+
+ patchDocument.Add(new JsonPatchOperation
+ {
+ Operation = Operation.Replace,
+ Path = GetFieldNameForWrite(CoreField.AssignedTo),
+ Value = member.AssignName,
+ });
+
+ var updatedItem = witClient.UpdateWorkItemAsync(patchDocument, item.ID).Result;
+
+ return ConvertToWorkItem(project, updatedItem);
});
}
@@ -446,7 +541,19 @@ namespace Tango.TFS
/// <returns></returns>
private VssConnection CreateConnection()
{
- VssConnection connection = new VssConnection(new Uri(CollectionURL), new VssBasicCredential(UserName, PersonalToken));
+ LogManager.Log("Generating VSTS connection using personal token " + PersonalToken);
+ VssConnection connection = new VssConnection(new Uri(CollectionURL), new VssBasicCredential(String.Empty, PersonalToken));
+ connection.Credentials.PromptType = CredentialPromptType.DoNotPrompt;
+ connection.ConnectAsync(VssConnectMode.User).SyncResult();
+ LogManager.Log("VSS Connection established...");
+
+ LogManager.Log("Authenticated: " + connection.HasAuthenticated);
+
+ if (connection.HasAuthenticated)
+ {
+ LogManager.Log("Authenticated Identity: " + connection.AuthorizedIdentity.DisplayName);
+ }
+
return connection;
}
@@ -514,6 +621,65 @@ namespace Tango.TFS
return values[description];
}
+ private WorkItem ConvertToWorkItem(Project project, Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItem item)
+ {
+ WorkItem workItem = new WorkItem();
+
+
+ workItem.ID = item.Id.Value;
+ workItem.Title = item.Fields[GetFieldNameForRead(CoreField.Title)].ToString();
+ workItem.CreatedDate = DateTime.Parse(item.Fields[GetFieldNameForRead(CoreField.CreatedDate)].ToString());
+ workItem.Description = TryGetField(item.Fields, GetFieldNameForRead(CoreField.Description));
+ workItem.Area = new Area()
+ {
+ Path = item.Fields[GetFieldNameForRead(CoreField.AreaPath)].ToString(),
+ Name = Path.GetFileName(item.Fields[GetFieldNameForRead(CoreField.AreaPath)].ToString()),
+ };
+ workItem.Iteration = new Iteration()
+ {
+ Path = item.Fields[GetFieldNameForRead(CoreField.IterationPath)].ToString(),
+ Name = Path.GetFileName(item.Fields[GetFieldNameForRead(CoreField.IterationPath)].ToString()),
+ };
+
+ workItem.AssignedTo = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.AssignedTo)));
+ workItem.CreatedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.CreatedBy)));
+ workItem.ChangedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.ChangedBy)));
+ workItem.AuthorizedAs = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, GetFieldNameForRead(CoreField.AuthorizedAs)));
+
+ workItem.ResolvedBy = project.Members.SingleOrDefault(x => x.AssignName == TryGetField(item.Fields, ExtensionFields.RESOLVED_BY));
+
+ if (item.Fields.ContainsKey(ExtensionFields.RESOLVED_REASON))
+ {
+ workItem.ResolvedReason = ParseEnumByDescription<ResolvedReason>(item.Fields[ExtensionFields.RESOLVED_REASON].ToString());
+ workItem.ResolvedDate = DateTime.Parse(item.Fields[ExtensionFields.RESOLVED_DATE].ToString());
+ }
+
+ workItem.Type = (WorkItemType)Enum.Parse(typeof(WorkItemType), item.Fields[GetFieldNameForRead(CoreField.WorkItemType)].ToString());
+ workItem.URL = item.Url;
+
+ if (item.Fields.ContainsKey(GetFieldNameForRead(CoreField.Tags)))
+ {
+ List<String> tags = item.Fields[GetFieldNameForRead(CoreField.Tags)].ToString().Split(';').Select(x => x.Trim()).ToList();
+ workItem.Tags = tags.Select(x => new Tag() { Name = x }).ToList();
+ }
+
+ workItem.FoundInBuild = TryGetField(item.Fields, ExtensionFields.FOUND_IN_BUILD).ToString();
+
+ workItem.State = (State)Enum.Parse(typeof(State), item.Fields[GetFieldNameForRead(CoreField.State)].ToString());
+
+ workItem.Severity = ParseEnumByDescription<Severity>(item.Fields[ExtensionFields.SEVERITY].ToString());
+
+ workItem.Priority = (Priority)int.Parse(item.Fields[ExtensionFields.PRIORITY].ToString());
+
+ workItem.StepsToReproduce = TryGetField(item.Fields, ExtensionFields.STEPS_TO_REP).ToString();
+
+ workItem.SystemInformation = TryGetField(item.Fields, ExtensionFields.SYSTEM_INFO).ToString();
+
+ workItem.Comment = TryGetField(item.Fields, GetFieldNameForRead(CoreField.History)).ToString();
+
+ return workItem;
+ }
+
#endregion
}
}
diff --git a/Software/Visual_Studio/Tango.TFS/WorkItem.cs b/Software/Visual_Studio/Tango.TFS/WorkItem.cs
index 98ac05191..59f277f2e 100644
--- a/Software/Visual_Studio/Tango.TFS/WorkItem.cs
+++ b/Software/Visual_Studio/Tango.TFS/WorkItem.cs
@@ -101,6 +101,13 @@ namespace Tango.TFS
set { _assignedTo = value; RaisePropertyChangedAuto(); }
}
+ private DateTime _createdDate;
+ public DateTime CreatedDate
+ {
+ get { return _createdDate; }
+ set { _createdDate = value; RaisePropertyChangedAuto(); }
+ }
+
private TeamMember _createdBy;
public TeamMember CreatedBy
{
@@ -115,6 +122,27 @@ namespace Tango.TFS
set { _changedBy = value; RaisePropertyChangedAuto(); }
}
+ private TeamMember _resolvedBy;
+ public TeamMember ResolvedBy
+ {
+ get { return _resolvedBy; }
+ set { _resolvedBy = value; RaisePropertyChangedAuto(); }
+ }
+
+ private DateTime _resolvedDate;
+ public DateTime ResolvedDate
+ {
+ get { return _resolvedDate; }
+ set { _resolvedDate = value; RaisePropertyChangedAuto(); }
+ }
+
+ private ResolvedReason _resolvedReason;
+ public ResolvedReason ResolvedReason
+ {
+ get { return _resolvedReason; }
+ set { _resolvedReason = value; RaisePropertyChangedAuto(); }
+ }
+
private TeamMember _authorizedAs;
public TeamMember AuthorizedAs
{
@@ -140,6 +168,30 @@ namespace Tango.TFS
set { _foundInBuild = value; RaisePropertyChangedAuto(); }
}
+ private String _comment;
+ public String Comment
+ {
+ get { return _comment; }
+ set { _comment = value; RaisePropertyChangedAuto(); }
+ }
+
+ public bool IsBuildVersionValid
+ {
+ get
+ {
+ Version v = null;
+ return Version.TryParse(FoundInBuild, out v);
+ }
+ }
+
+ public Version FoundInBuildVersion
+ {
+ get
+ {
+ return Version.Parse(FoundInBuild);
+ }
+ }
+
public WorkItem()
{
Attachments = new List<Attachment>();