Exemple #1
0
        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);
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        private int TryGetCodeFlowChangeTrackingId(GitPullRequestIterationChanges changes, FilePath path)
        {
            changes.NotNull(nameof(changes));
            path.NotNull(nameof(path));

            var change = changes.ChangeEntries.Where(x => x.Item.Path == "/" + path.ToString()).ToList();

            if (change.Count == 0)
            {
                this.Log.Error(
                    "Cannot post a comment for the file {0} because no changes on the pull request server could be found.",
                    path);
                return(-1);
            }

            if (change.Count > 1)
            {
                this.Log.Error(
                    "Cannot post a comment for the file {0} because more than one change has been found on the pull request server:" + Environment.NewLine + "{1}",
                    path,
                    string.Join(
                        Environment.NewLine,
                        change.Select(
                            x => string.Format(
                                CultureInfo.InvariantCulture,
                                "  ID: {0}, Path: {1}",
                                x.ChangeId,
                                x.Item.Path))));
                return(-1);
            }

            var changeTrackingId = change.Single().ChangeTrackingId;

            this.Log.Verbose(
                "Determined ChangeTrackingId of {0} for {1}",
                changeTrackingId,
                path);
            return(changeTrackingId);
        }
        private int TryGetCodeFlowChangeTrackingId(GitPullRequestIterationChanges changes, FilePath path)
        {
            changes.NotNull(nameof(changes));
            path.NotNull(nameof(path));

            var change = changes.ChangeEntries.Where(x => x.Item.Path == "/" + path.ToString()).ToList();

            if (change.Count != 1)
            {
                this.Log.Error(
                    "Cannot post a comment for the file {0} because no changes could be found.",
                    path);
                return(-1);
            }

            var changeTrackingId = change.Single().ChangeTrackingId;

            this.Log.Verbose(
                "Determined ChangeTrackingId of {0} for {1}",
                changeTrackingId,
                path);
            return(changeTrackingId);
        }
        protected override Mock <GitHttpClient> Setup(Mock <GitHttpClient> m)
        {
            m.Setup(arg => arg.CreatePullRequestReviewerAsync(
                        It.Is <IdentityRefWithVote>(i => Enum.IsDefined(typeof(AzureDevOpsPullRequestVote), i.Vote)),
                        It.IsAny <Guid>(),
                        It.IsAny <int>(),
                        It.IsAny <string>(),
                        It.IsAny <object>(),
                        default(CancellationToken)))
            .ReturnsAsync((IdentityRefWithVote identity, Guid project, int prId, string reviewerId, object o, CancellationToken c)
                          => new IdentityRefWithVote
            {
                Vote = identity.Vote,
            });

            m.Setup(arg => arg.CreatePullRequestReviewerAsync(
                        It.Is <IdentityRefWithVote>(i => !Enum.IsDefined(typeof(AzureDevOpsPullRequestVote), i.Vote)),
                        It.IsAny <Guid>(),
                        It.IsAny <int>(),
                        It.IsAny <string>(),
                        It.IsAny <object>(),
                        default(CancellationToken)))
            .Throws(new Exception("Something went wrong"));

            m.Setup(arg => arg.CreatePullRequestStatusAsync(
                        It.IsAny <GitPullRequestStatus>(),
                        It.IsAny <Guid>(),
                        It.IsAny <int>(),
                        It.IsAny <object>(),
                        It.IsAny <CancellationToken>()))
            .ReturnsAsync((GitPullRequestStatus status, Guid repoId, int prId, object o, CancellationToken c)
                          => new GitPullRequestStatus
            {
                Context = status.Context,
                State   = status.State,
            });

            // Setup CommitDiffs object
            var gitChanges = new List <GitChange>
            {
                new GitChange
                {
                    ChangeId   = 1,
                    ChangeType = VersionControlChangeType.Edit,
                    Item       = new GitItem("/src/project/myclass.cs", "ID1", GitObjectType.Commit, "6b13ff8", 0),
                },
                null,
                new GitChange
                {
                    ChangeId   = 2,
                    ChangeType = VersionControlChangeType.Edit,
                    Item       = new GitItem("/tools/folder", "ID2", GitObjectType.Tree, "6b13ff8", 0),
                },
            };

            var gitCommitDiffs = new GitCommitDiffs
            {
                ChangeCounts = new Dictionary <VersionControlChangeType, int> {
                    { VersionControlChangeType.Edit, 2 }
                },
                Changes = gitChanges,
            };

            m.Setup(arg => arg.GetCommitDiffsAsync(
                        It.IsAny <string>(),
                        It.IsAny <Guid>(),
                        true,
                        null,
                        null,
                        It.IsAny <GitBaseVersionDescriptor>(),
                        It.IsAny <GitTargetVersionDescriptor>(),
                        null,
                        CancellationToken.None))
            .ReturnsAsync((string prj, Guid rId, bool?b, int?t, int?s, GitBaseVersionDescriptor bvd, GitTargetVersionDescriptor tvd, object o, CancellationToken c)
                          => gitCommitDiffs);

            m.Setup(arg => arg.UpdateThreadAsync(
                        It.IsAny <GitPullRequestCommentThread>(),
                        It.IsAny <Guid>(),
                        It.IsAny <int>(),
                        It.IsAny <int>(),
                        null,
                        CancellationToken.None))
            .ReturnsAsync((GitPullRequestCommentThread prct, Guid g, int prId, int thId, object o, CancellationToken c)
                          => new GitPullRequestCommentThread {
                Id = thId, Status = prct.Status
            });

            // Setup GitPullRequestCommentThread collection
            var commentThreads = new List <GitPullRequestCommentThread>
            {
                new GitPullRequestCommentThread
                {
                    Id            = 11,
                    ThreadContext = new CommentThreadContext()
                    {
                        FilePath = "/some/path/to/file.cs",
                    },
                    Comments = new List <Comment>
                    {
                        new Comment {
                            Content = "Hello", IsDeleted = false, CommentType = CommentType.CodeChange
                        },
                        new Comment {
                            Content = "Goodbye", IsDeleted = true, CommentType = CommentType.Text
                        },
                    },
                    Status = CommentThreadStatus.Active,
                },
                new GitPullRequestCommentThread
                {
                    Id            = 22,
                    ThreadContext = null,
                    Comments      = new List <Comment>(),
                    Status        = CommentThreadStatus.Fixed,
                },
            };

            m.Setup(arg => arg.GetThreadsAsync(
                        It.IsAny <Guid>(),
                        It.IsAny <int>(),
                        null,
                        null,
                        null,
                        CancellationToken.None))
            .ReturnsAsync((Guid rId, int prId, int?it, int?baseIt, object o, CancellationToken c)
                          => commentThreads);

            m.Setup(arg => arg.CreateThreadAsync(
                        It.IsAny <GitPullRequestCommentThread>(),
                        It.IsAny <Guid>(),
                        It.IsAny <int>(),
                        null,
                        CancellationToken.None))
            .ReturnsAsync((GitPullRequestCommentThread prct, Guid g, int i, object o, CancellationToken c)
                          => prct);

            m.Setup(arg => arg.GetPullRequestIterationsAsync(
                        It.IsAny <Guid>(),
                        It.Is <int>(i => i != 13),
                        null,
                        null,
                        CancellationToken.None))
            .ReturnsAsync((Guid repoId, int prId, bool?b, object o, CancellationToken c)
                          => new List <GitPullRequestIteration>
            {
                new GitPullRequestIteration {
                    Id = 42, CreatedDate = DateTime.Today.AddDays(-3)
                },
                new GitPullRequestIteration {
                    Id = 16, CreatedDate = DateTime.Today.AddDays(-1)
                },
            });

            m.Setup(arg => arg.GetPullRequestIterationsAsync(
                        It.IsAny <Guid>(),
                        It.Is <int>(i => i == 13), // Just to emulate the unlucky case
                        null,
                        null,
                        CancellationToken.None))
            .ReturnsAsync((Guid repoId, int prId, bool?b, object o, CancellationToken c)
                          => new List <GitPullRequestIteration>
            {
                new GitPullRequestIteration {
                    Id = null
                },
            });

            // Setup GitPullRequestIterationChanges collection
            var changes = new GitPullRequestIterationChanges
            {
                ChangeEntries = new List <GitPullRequestChange>
                {
                    new GitPullRequestChange {
                        ChangeId = 100, ChangeTrackingId = 1, Item = new GitItem {
                            Path = "/src/my/class1.cs"
                        }
                    },
                    new GitPullRequestChange {
                        ChangeId = 200, ChangeTrackingId = 2, Item = new GitItem {
                            Path = string.Empty
                        }
                    },
                },
            };

            m.Setup(arg => arg.GetPullRequestIterationChangesAsync(
                        It.IsAny <Guid>(),
                        It.IsAny <int>(),
                        It.IsAny <int>(),
                        null,
                        null,
                        null,
                        null,
                        CancellationToken.None))
            .ReturnsAsync((Guid repoId, int prId, int iterId, int?t, int?s, int?ct, object o, CancellationToken c)
                          => changes);

            // Setup pull request creation
            m.Setup(arg => arg.GetRefsAsync(
                        It.IsAny <string>(),
                        "MyRepoName",
                        "NotExistingBranch",
                        null,
                        null,
                        null,
                        null,
                        null,
                        null,
                        null,
                        CancellationToken.None))
            .ReturnsAsync(() => new List <GitRef>());

            m.Setup(args => args.GetRepositoryAsync(It.IsAny <string>(), "MyRepoName", null, CancellationToken.None))
            .ReturnsAsync(() => new GitRepository()
            {
                DefaultBranch = "master"
            });

            m.Setup(arg => arg.GetRefsAsync(
                        It.IsAny <string>(),
                        "MyRepoName",
                        "master",
                        null,
                        null,
                        null,
                        null,
                        null,
                        null,
                        null,
                        CancellationToken.None))
            .ReturnsAsync(() => new List <GitRef>()
            {
                new GitRef("master"),
            });

            m.Setup(
                arg =>
                arg.CreatePullRequestAsync(
                    It.IsAny <GitPullRequest>(),
                    It.IsAny <string>(),
                    It.IsAny <string>(),
                    null,
                    null,
                    CancellationToken.None))
            .ReturnsAsync(
                (
                    GitPullRequest gitPullRequestToCreate,
                    string project,
                    string repositoryId,
                    bool?supportsIterations,
                    object userState,
                    CancellationToken cancellationToken) =>
            {
                gitPullRequestToCreate.PullRequestId = 777;
                gitPullRequestToCreate.Repository    = new GitRepository
                {
                    Id   = Guid.NewGuid(),
                    Name = repositoryId,
                };
                gitPullRequestToCreate.CodeReviewId          = 123;
                gitPullRequestToCreate.LastMergeSourceCommit = new GitCommitRef {
                    CommitId = "4a92b977"
                };
                gitPullRequestToCreate.LastMergeTargetCommit = new GitCommitRef {
                    CommitId = "78a3c113"
                };

                return(gitPullRequestToCreate);
            });

            return(m);
        }