/// <summary> /// Sets the pull request comment thread status. /// </summary> /// <param name="threadId">The Id of the comment thread.</param> /// <param name="status">The comment thread status.</param> private void SetCommentThreadStatus(int threadId, CommentThreadStatus status) { if (!this.ValidatePullRequest()) { return; } using (var gitClient = this.gitClientFactory.CreateGitClient(this.CollectionUrl, this.credentials)) { var newThread = new GitPullRequestCommentThread { Status = status, }; gitClient .UpdateThreadAsync( newThread, this.RepositoryId, this.PullRequestId, threadId) .ConfigureAwait(false) .GetAwaiter() .GetResult(); } }
public void Should_Set_Properly() { // Given var thread = new GitPullRequestCommentThread { Id = 15, Status = CommentThreadStatus.Active, }; // When var tfsThread = new TfsPullRequestCommentThread(thread) { Comments = new List <TfsComment> { new TfsComment("hi", false, CommentType.Text) }, }; // Then tfsThread.Comments.ShouldNotBeNull(); tfsThread.Comments.ShouldHaveSingleItem(); tfsThread.Comments.First().ShouldNotBeNull(); tfsThread.Comments.First().ShouldBeOfType <TfsComment>(); tfsThread.Comments.First().Content.ShouldBe("hi"); tfsThread.Comments.First().CommentType.ShouldBe(TfsCommentType.Text); tfsThread.Comments.First().IsDeleted.ShouldBeFalse(); }
public void Should_Set_Correct_Id() { // Given var id = 123; var status = CommentThreadStatus.Active; var filePath = "/foo.cs"; var thread = new GitPullRequestCommentThread { Id = id, Status = status, ThreadContext = new CommentThreadContext() { FilePath = filePath }, Comments = new List <Comment>(), Properties = new PropertiesCollection() }; // When var result = thread.ToPullRequestDiscussionThread(); // Then result.Id.ShouldBe(id); }
/// <summary> /// Close all active comments and complete PR /// </summary> /// <param name="TeamProjectName"></param> /// <param name="RepoName"></param> /// <param name="PrId"></param> static void CompletePR(string TeamProjectName, string RepoName, int PrId) { GitPullRequest pr = GitClient.GetPullRequestAsync(TeamProjectName, RepoName, PrId).Result; if (pr.MergeStatus != PullRequestAsyncStatus.Succeeded) { CreateNewCommentThread(TeamProjectName, RepoName, PrId, "You need to resolve conflicts"); return; } List <GitPullRequestCommentThread> threads = GitClient.GetThreadsAsync(TeamProjectName, RepoName, PrId).Result; foreach (var thread in threads) { if (thread.Status == CommentThreadStatus.Active) { GitPullRequestCommentThread updatedThread = new GitPullRequestCommentThread(); updatedThread.Status = CommentThreadStatus.Fixed; Microsoft.TeamFoundation.SourceControl.WebApi.Comment[] comments = { new Microsoft.TeamFoundation.SourceControl.WebApi.Comment { Content = "Task is completed." } }; updatedThread.Comments = comments; updatedThread = GitClient.UpdateThreadAsync(updatedThread, TeamProjectName, RepoName, PrId, thread.Id).Result; } } GitPullRequest prUdated = new GitPullRequest(); prUdated.Status = PullRequestStatus.Completed; prUdated.LastMergeSourceCommit = pr.LastMergeSourceCommit; prUdated = GitClient.UpdatePullRequestAsync(prUdated, TeamProjectName, RepoName, PrId).Result; }
public void Should_Return_Message() { // Given var message = "foo"; var thread = new GitPullRequestCommentThread { Id = 123, Status = CommentThreadStatus.Active, ThreadContext = new CommentThreadContext() { FilePath = "/foo.cs" }, Comments = new List <Comment>(), Properties = new PropertiesCollection() }; thread.SetIssueMessage(message); // When var result = thread.GetIssueMessage(); // Then result.ShouldBe(message); }
/// <summary> /// Converts a <see cref="GitPullRequestCommentThread"/> from TFS to a <see cref="IPullRequestDiscussionThread"/> as used in this addin. /// </summary> /// <param name="thread">TFS thread to convert.</param> /// <returns>Converted thread.</returns> public static IPullRequestDiscussionThread ToPullRequestDiscussionThread(this GitPullRequestCommentThread thread) { thread.NotNull(nameof(thread)); if (thread.Comments == null) { throw new InvalidOperationException("Comments list is not created."); } FilePath filePath = null; if (thread.ThreadContext != null && thread.ThreadContext.FilePath != null) { filePath = thread.ThreadContext.FilePath.TrimStart('/'); } return(new PullRequestDiscussionThread( thread.Id, thread.Status.ToPullRequestDiscussionStatus(), filePath, thread.Comments.Select(x => x.ToPullRequestDiscussionComment())) { CommentSource = thread.GetCommentSource(), Resolution = thread.Status.ToPullRequestDiscussionResolution() }); }
public static async Task <Dictionary <GitPullRequest, bool> > CheckPullRequestFreshness( GitHttpClient gitClient, string project, IList <GitPullRequest> pullRequests, DateTime staleDate, string botId) { Dictionary <GitPullRequest, bool> pullCollection = new Dictionary <GitPullRequest, bool>(); foreach (GitPullRequest pr in pullRequests) { IList <GitPullRequestCommentThread> threads = await gitClient.GetThreadsAsync(project, pr.Repository.Id, pr.PullRequestId); GitPullRequestCommentThread lastUpdated = threads.OrderByDescending(p => p.LastUpdatedDate).FirstOrDefault(); // Stale PRs have never been updated, or haven't been updated since the staleDate if (lastUpdated == null || lastUpdated.LastUpdatedDate < staleDate) { // Knowing whether or not the last comment was from the bot will tell us if we need to check for tags or not bool commentByBot = lastUpdated != null && lastUpdated.Comments.Last().Author.Id == botId; pullCollection.Add(pr, commentByBot); } } return(pullCollection); }
/// <inheritdoc /> protected override void InternalResolveDiscussionThreads(IEnumerable <IPullRequestDiscussionThread> threads) { // ReSharper disable once PossibleMultipleEnumeration threads.NotNull(nameof(threads)); if (!this.PullRequestSystem.ValidatePullRequest()) { return; } using (var gitClient = this.PullRequestSystem.CreateGitClient()) { // ReSharper disable once PossibleMultipleEnumeration foreach (var thread in threads) { var newThread = new GitPullRequestCommentThread { Status = CommentThreadStatus.Fixed }; gitClient.UpdateThreadAsync( newThread, this.PullRequestSystem.TfsPullRequest.RepositoryId, this.PullRequestSystem.TfsPullRequest.PullRequestId, thread.Id, null, CancellationToken.None).Wait(); } } }
public void Should_Set_Properly() { // Given var thread = new GitPullRequestCommentThread { Id = 15, Status = CommentThreadStatus.Active, }; // When var azureDevOpsThread = new AzureDevOpsPullRequestCommentThread(thread) { Comments = new List <AzureDevOpsComment> { new AzureDevOpsComment("hi", false) }, }; // Then azureDevOpsThread.Comments.ShouldNotBeNull(); azureDevOpsThread.Comments.ShouldHaveSingleItem(); azureDevOpsThread.Comments.First().ShouldNotBeNull(); azureDevOpsThread.Comments.First().ShouldBeOfType <AzureDevOpsComment>(); azureDevOpsThread.Comments.First().Content.ShouldBe("hi"); azureDevOpsThread.Comments.First().CommentType.ShouldBe(AzureDevOpsCommentType.Unknown); azureDevOpsThread.Comments.First().IsDeleted.ShouldBeFalse(); }
private IEnumerable <GitPullRequestCommentThread> CreateDiscussionThreads( GitHttpClient gitClient, IEnumerable <IIssue> issues, string commentSource) { // ReSharper disable once PossibleMultipleEnumeration issues.NotNull(nameof(issues)); this.Log.Verbose("Creating new discussion threads"); var result = new List <GitPullRequestCommentThread>(); // Code flow properties var iterationId = 0; GitPullRequestIterationChanges changes = null; if (this.tfsPullRequest.CodeReviewId > 0) { iterationId = this.GetCodeFlowLatestIterationId(gitClient); changes = this.GetCodeFlowChanges(gitClient, iterationId); } // Filter isues not related to a file. if (!this.settings.ReportIssuesNotRelatedToAFile) { issues = issues.Where(x => x.AffectedFileRelativePath != null); } // ReSharper disable once PossibleMultipleEnumeration foreach (var issue in issues) { this.Log.Information( "Creating a discussion comment for the issue at line {0} from {1}", issue.Line, issue.AffectedFileRelativePath); var newThread = new GitPullRequestCommentThread() { Status = CommentThreadStatus.Active }; var discussionComment = new Comment { CommentType = CommentType.System, IsDeleted = false, Content = ContentProvider.GetContent(issue) }; if (!this.AddThreadProperties(newThread, changes, issue, iterationId, commentSource)) { continue; } newThread.Comments = new List <Comment> { discussionComment }; result.Add(newThread); } return(result); }
public void Should_Return_True_For_Existing_Comment_Source() { // Given var commentSource = "foo"; var thread = new GitPullRequestCommentThread { Id = 123, Status = CommentThreadStatus.Active, ThreadContext = new CommentThreadContext() { FilePath = "/foo.cs" }, Comments = new List <Comment>(), Properties = new PropertiesCollection() }; thread.SetCommentSource(commentSource); // When var result = thread.IsCommentSource(commentSource); // Then result.ShouldBeTrue(); }
public void Should_Throw_If_Thread_Is_Null() { // Given GitPullRequestCommentThread thread = null; // When var result = Record.Exception(() => thread.GetIssueMessage()); // Then result.IsArgumentNullException("thread"); }
/// <summary> /// Gets the original message of the issue as provided by Cake.Issues.PullRequests, /// without any formatting done by this addin. /// </summary> /// <param name="thread">Thread to get the value from.</param> /// <returns>Original message of the issue.</returns> public static string GetIssueMessage(this GitPullRequestCommentThread thread) { thread.NotNull(nameof(thread)); if (thread.Properties == null) { throw new InvalidOperationException("Properties collection is not created."); } return(thread.Properties.GetValue(IssueMessagePropertyName, string.Empty)); }
public void Should_Throw_If_Thread_Is_Null() { // Given GitPullRequestCommentThread thread = null; var value = "foo"; // When var result = Record.Exception(() => thread.IsCommentSource(value)); // Then result.IsArgumentNullException("thread"); }
/// <summary> /// Converts a <see cref="GitPullRequestCommentThread"/> from TFS to a <see cref="IPrcaDiscussionThread"/> as used in this addin. /// </summary> /// <param name="thread">TFS thread to convert.</param> /// <returns>Converted thread.</returns> public static IPrcaDiscussionThread ToPrcaDiscussionThread(this GitPullRequestCommentThread thread) { thread.NotNull(nameof(thread)); return(new PrcaDiscussionThread( thread.Id, thread.Status.ToPrcaDiscussionStatus(), new FilePath(thread.ThreadContext.FilePath.TrimStart('/')), thread.Comments.Select(x => x.ToPrcaDiscussionComment())) { CommentSource = thread.GetCommentSource(), }); }
public void Should_Return_Null_If_Not_Set() { // Given var thread = new GitPullRequestCommentThread { Id = 16, Status = CommentThreadStatus.Active, }; // When var azureDevOpsThread = new AzureDevOpsPullRequestCommentThread(thread); // Then azureDevOpsThread.Properties.ShouldBeNull(); }
async Task _createComment(string message, GitPullRequest pr, GitHttpClient gitClient, string repoId) { var comment = new Comment(); comment.Content = message; var cThread = new GitPullRequestCommentThread { Comments = new List <Comment>() }; cThread.Comments.Add(comment); await gitClient.CreateThreadAsync(cThread, repoId, pr.PullRequestId); }
public async Task CreatePRStatus() { var data = File.ReadAllText("SerialisedObjects\\req6.json"); var obj = JsonConvert.DeserializeObject <VstsRequest>(data); // VssConnection connection = new VssConnection(new Uri(collectionUri), new VssBasicCredential(string.Empty, pat)); var collectionId = AppSettings.AzureDevOpsCollectionName; var connection = new VssConnection(new Uri(collectionId), new VssBasicCredential(string.Empty, SecretOptions.Value.PAT)); var gitClient = connection.GetClient <GitHttpClient>(); var repoId = obj.resource.repository.id; var gRepoId = new Guid(repoId); var pr = await gitClient.GetPullRequestAsync(obj.resource.repository.id, obj.resource.pullRequestId); var its = await gitClient.GetPullRequestIterationsAsync(repoId, pr.PullRequestId); var it = its.Last(); var prStatus = new GitPullRequestStatus(); prStatus.Context = new GitStatusContext { Genre = AppSettings.Genre, Name = AppSettings.Name }; prStatus.State = GitStatusState.Succeeded; prStatus.Description = "All seems good dude6."; prStatus.IterationId = it.Id; var comment = new Comment(); comment.Content = "This was done okay cool yeah *bold* **something** \r\n # test \r\n omething"; var cThread = new GitPullRequestCommentThread { Comments = new List <Comment>() }; cThread.Comments.Add(comment); await gitClient.CreateThreadAsync(cThread, repoId, pr.PullRequestId); await gitClient.CreatePullRequestIterationStatusAsync(prStatus, new Guid(repoId), pr.PullRequestId, it.Id.Value); }
public void Should_Return_Null_If_Not_Initialized() { // Given var thread = new GitPullRequestCommentThread { Id = 16, Status = CommentThreadStatus.Active, }; // When var azureDevOpsThread = new AzureDevOpsPullRequestCommentThread(thread); var filePath = azureDevOpsThread.FilePath; // Then filePath.ShouldBeNull(); }
public void Should_Throw_If_Not_Set() { // Given var thread = new GitPullRequestCommentThread { Id = 15, Status = CommentThreadStatus.Active, }; // When var azureDevOpsThread = new AzureDevOpsPullRequestCommentThread(thread); var result = Record.Exception(() => azureDevOpsThread.Comments); // Then result.IsInvalidOperationException(); }
private bool AddThreadProperties( GitPullRequestCommentThread thread, GitPullRequestIterationChanges changes, IIssue issue, int iterationId, string commentSource) { thread.NotNull(nameof(thread)); changes.NotNull(nameof(changes)); issue.NotNull(nameof(issue)); var properties = new PropertiesCollection(); if (issue.AffectedFileRelativePath != null) { if (this.tfsPullRequest.CodeReviewId > 0) { var changeTrackingId = this.TryGetCodeFlowChangeTrackingId(changes, issue.AffectedFileRelativePath); if (changeTrackingId < 0) { // Don't post comment if we couldn't determine the change. return(false); } AddCodeFlowProperties(issue, iterationId, changeTrackingId, properties); } else { throw new NotSupportedException("Legacy code reviews are not supported."); } } // A VSTS UI extension will recognize this and format the comments differently. properties.Add("CodeAnalysisThreadType", "CodeAnalysisIssue"); thread.Properties = properties; // Add a custom property to be able to distinguish all comments created this way. thread.SetCommentSource(commentSource); // Add a custom property to be able to return issue message from existing threads, // without any formatting done by this addin, back to Cake.Issues.PullRequests. thread.SetIssueMessage(issue.Message); return(true); }
/// <summary> /// Start new comment thread /// </summary> /// <param name="TeamProjectName"></param> /// <param name="RepoName"></param> /// <param name="PrId"></param> /// <param name="Title"></param> /// <param name="Status"></param> static void CreateNewCommentThread(string TeamProjectName, string RepoName, int PrId, string Title, CommentThreadStatus Status = CommentThreadStatus.Active) { GitPullRequest pr = GitClient.GetPullRequestAsync(TeamProjectName, RepoName, PrId).Result; GitPullRequestCommentThread gitThread = new GitPullRequestCommentThread(); gitThread.Status = Status; List <Microsoft.TeamFoundation.SourceControl.WebApi.Comment> comments = new List <Microsoft.TeamFoundation.SourceControl.WebApi.Comment>(); comments.Add(new Microsoft.TeamFoundation.SourceControl.WebApi.Comment { Content = Title }); gitThread.Comments = comments; var thread = GitClient.CreateThreadAsync(gitThread, TeamProjectName, RepoName, PrId).Result; }
public void Should_Set_And_Trimmed_Properly() { // Given var thread = new GitPullRequestCommentThread { Id = 100, Status = CommentThreadStatus.Active, }; // When var azureDevOpsThread = new AzureDevOpsPullRequestCommentThread(thread) { FilePath = "/path/to/myclass.cs", }; // Then azureDevOpsThread.FilePath.ShouldNotBeNull(); azureDevOpsThread.FilePath.FullPath.ShouldBe("path/to/myclass.cs"); }
/// <summary> /// Sets a value in the thread properties. /// </summary> /// <typeparam name="T">Type of the value.</typeparam> /// <param name="thread">Thread for which the value should be set.</param> /// <param name="propertyName">Name of the property.</param> /// <param name="value">Value to set.</param> private static void SetValue <T>(this GitPullRequestCommentThread thread, string propertyName, T value) { thread.NotNull(nameof(thread)); propertyName.NotNullOrWhiteSpace(nameof(propertyName)); if (thread.Properties == null) { throw new InvalidOperationException("Properties collection is not created."); } if (thread.Properties.ContainsKey(propertyName)) { thread.Properties[propertyName] = value; } else { thread.Properties.Add(propertyName, value); } }
public void Should_Set_Correct_FilePath_If_ThreadContext_Is_Null() { // Given var thread = new GitPullRequestCommentThread { Id = 123, Status = CommentThreadStatus.Active, ThreadContext = null, Comments = new List <Comment>(), Properties = new PropertiesCollection() }; // When var result = thread.ToPullRequestDiscussionThread(); // Then result.AffectedFileRelativePath.ShouldBeNull(); }
public void Should_Set_Empty_Collection() { // Given var thread = new GitPullRequestCommentThread { Id = 16, Status = CommentThreadStatus.Active, }; // When var azureDevOpsThread = new AzureDevOpsPullRequestCommentThread(thread) { Properties = new Dictionary <string, object>(), }; // Then azureDevOpsThread.Properties.ShouldNotBeNull(); azureDevOpsThread.Properties.ShouldBeOfType <PropertiesCollection>(); azureDevOpsThread.Properties.ShouldBeEmpty(); }
public void Should_Throw_If_Properties_Are_Null() { // Given var thread = new GitPullRequestCommentThread { Id = 123, Status = CommentThreadStatus.Active, ThreadContext = new CommentThreadContext { FilePath = "/foo.cs" }, Comments = new List <Comment>(), Properties = null }; // When var result = Record.Exception(() => thread.GetIssueMessage()); // Then result.IsInvalidOperationException("Properties collection is not created."); }
public void Should_Throw_If_Comments_Are_Null() { // Given var thread = new GitPullRequestCommentThread { Id = 123, Status = CommentThreadStatus.Active, ThreadContext = new CommentThreadContext { FilePath = "/foo.cs" }, Comments = null, Properties = new PropertiesCollection() }; // When var result = Record.Exception(() => thread.ToPullRequestDiscussionThread()); // Then result.IsInvalidOperationException("Comments list is not created."); }
public void Should_Return_Valid_Comment_Thread() { // Given var gitCommentThread = new GitPullRequestCommentThread { Id = 42, Status = CommentThreadStatus.Pending, ThreadContext = new CommentThreadContext { FilePath = "/src/myclass.cs" }, Comments = new List <Comment> { new Comment { Content = "Hello", CommentType = CommentType.Text, IsDeleted = false } }, Properties = new PropertiesCollection(), }; // When var azureDevOpsCommentThread = new AzureDevOpsPullRequestCommentThread(gitCommentThread); // Then azureDevOpsCommentThread.ShouldNotBeNull(); azureDevOpsCommentThread.Id.ShouldBe(42); azureDevOpsCommentThread.Status.ShouldBe(AzureDevOpsCommentThreadStatus.Pending); azureDevOpsCommentThread.FilePath.ShouldNotBeNull(); azureDevOpsCommentThread.FilePath.FullPath.ShouldBe("src/myclass.cs"); azureDevOpsCommentThread.Comments.ShouldNotBeNull(); azureDevOpsCommentThread.Comments.ShouldNotBeEmpty(); azureDevOpsCommentThread.Comments.ShouldHaveSingleItem(); azureDevOpsCommentThread.Comments.First().ShouldNotBeNull(); azureDevOpsCommentThread.Comments.First().Content.ShouldBe("Hello"); azureDevOpsCommentThread.Comments.First().CommentType.ShouldBe(AzureDevOpsCommentType.Text); azureDevOpsCommentThread.Comments.First().IsDeleted.ShouldBeFalse(); azureDevOpsCommentThread.Properties.ShouldNotBeNull(); azureDevOpsCommentThread.Properties.ShouldBeEmpty(); }
/// <summary> /// Sets the pull request comment thread status. /// </summary> /// <param name="threadId">The Id of the comment thread.</param> /// <param name="status">The comment thread status.</param> private void SetCommentThreadStatus(int threadId, CommentThreadStatus status) { if (!this.ValidatePullRequest()) { return; } using (var gitClient = this.gitClientFactory.CreateGitClient(this.CollectionUrl, this.credentials)) { var newThread = new GitPullRequestCommentThread { Status = status, }; gitClient.UpdateThreadAsync( newThread, this.RepositoryId, this.PullRequestId, threadId, null, CancellationToken.None).Wait(); } }