diff options
| author | Shlomo Hecht <shlomo@twine-s.com> | 2018-05-02 17:36:54 +0300 |
|---|---|---|
| committer | Shlomo Hecht <shlomo@twine-s.com> | 2018-05-02 17:36:54 +0300 |
| commit | ee697f7a3350d0a97bddee4de3a2ae4f9d285052 (patch) | |
| tree | 2dc2e3bb811b0d89a3c4c51801c1572966fcee7c /Software/Visual_Studio/Tango.TFS | |
| parent | 73c4b814f1f28170ae72723568189096413c3564 (diff) | |
| download | Tango-ee697f7a3350d0a97bddee4de3a2ae4f9d285052.tar.gz Tango-ee697f7a3350d0a97bddee4de3a2ae4f9d285052.zip | |
merge
Diffstat (limited to 'Software/Visual_Studio/Tango.TFS')
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>(); |
