Ejemplo n.º 1
0
 public RepositoryHistoryItem(RepositoryBranchUpdateHistoryEntry other, IUrlHelper url, HttpContext context)
 {
     RepositoryName = other.Repository;
     BranchName     = other.Branch;
     Timestamp      = DateTime.SpecifyKind(other.Timestamp, DateTimeKind.Utc);
     ErrorMessage   = other.ErrorMessage;
     Success        = other.Success;
     Action         = other.Action;
     if (!other.Success)
     {
         string pathAndQuery = url.Action(
             nameof(RepositoryController.RetryActionAsync),
             new
         {
             repository = other.Repository,
             branch     = other.Branch,
             timestamp  = Timestamp.ToUnixTimeSeconds()
         });
         (string path, string query) = pathAndQuery.Split2('?');
         RetryUrl = new UriBuilder
         {
             Scheme = "https",
             Host   = context.Request.GetUri().Host,
             Path   = path,
             Query  = query
         }.Uri.AbsoluteUri;
     }
 }
Ejemplo n.º 2
0
        public async Task ShouldNotCreateIssue(string repoUrl, string branch, string method, string arguments, DateTime errorOccurredAt, bool success)
        {
            // Not testing SubscriptionUpdateHistoryEntry since this is always "no" for them.
            RepositoryBranchUpdateHistoryEntry repositoryBranchUpdate =
                new RepositoryBranchUpdateHistoryEntry
            {
                Repository = repoUrl,
                Branch     = branch,
                Method     = method,
                Timestamp  = errorOccurredAt,
                Arguments  = arguments,
                Success    = success
            };
            Repository repository = new Repository();

            GithubClient.Setup(x => x.Repository.Get(It.IsAny <string>(), It.IsAny <string>())).ReturnsAsync(repository);
            Maestro.Data.Models.Subscription subscription = new Maestro.Data.Models.Subscription();
            Context.Subscriptions.Add(subscription);
            Context.SaveChanges();
            Mock <Issue> issue = new Mock <Issue>();

            GithubClient.Setup(x => x.Issue.Create(It.IsAny <long>(), It.IsAny <NewIssue>())).ReturnsAsync(issue.Object);
            Context.RepoBranchUpdateInMemory = new List <RepositoryBranchUpdateHistoryEntry>
            {
                repositoryBranchUpdate
            };
            DependencyUpdateErrorProcessor errorProcessor =
                ActivatorUtilities.CreateInstance <DependencyUpdateErrorProcessor>(Scope.ServiceProvider,
                                                                                   Context);
            await errorProcessor.ProcessDependencyUpdateErrorsAsync();

            GithubClient.Verify(x => x.Issue.Create(It.IsAny <long>(), It.IsAny <NewIssue>()), Times.Never);
        }
        public async Task CreateIssue()
        {
            RepositoryBranchUpdateHistoryEntry firstError =
                new RepositoryBranchUpdateHistoryEntry
            {
                Repository   = RepoUrl,
                Branch       = BranchOne,
                Method       = "ProcessPendingUpdatesAsync",
                Timestamp    = new DateTime(2200, 1, 1),
                Arguments    = "[Error Message]",
                Success      = false,
                ErrorMessage = "Error Message",
                Action       = "Creating new issue"
            };

            RepositoryBranchUpdateHistoryEntry secondError =
                new RepositoryBranchUpdateHistoryEntry
            {
                Repository   = RepoUrl,
                Branch       = BranchTwo,
                Method       = "ProcessPendingUpdatesAsync",
                Timestamp    = new DateTime(2200, 1, 1),
                Arguments    = "[Arguments]",
                Success      = false,
                ErrorMessage = "ProcessPendingUpdatesAsync error",
                Action       = "Create another issue"
            };

            Context.RepoBranchUpdateInMemory = new List <RepositoryBranchUpdateHistoryEntry>
            {
                firstError, secondError
            };

            Repository repository = new Repository();

            GithubClient.Setup(x => x.Repository.Get(It.IsAny <string>(), It.IsAny <string>())).ReturnsAsync(repository);
            Mock <Octokit.Issue> issue    = new Mock <Issue>();
            List <NewIssue>      newIssue = new List <NewIssue>();

            GithubClient.Setup(x => x.Issue.Create(It.IsAny <long>(), Capture.In(newIssue))).ReturnsAsync(issue.Object);
            DependencyUpdateErrorProcessor errorProcessor =
                ActivatorUtilities.CreateInstance <DependencyUpdateErrorProcessor>(Scope.ServiceProvider,
                                                                                   Context);
            await errorProcessor.ProcessDependencyUpdateErrorsAsync();

            Assert.Equal(2, newIssue.Count);
            Assert.Equal("DependencyUpdateError", newIssue[0].Labels[0]);
            Assert.Contains(RepoUrl, newIssue[0].Title);
            Assert.Contains(BranchOne, newIssue[0].Body);
            Assert.Contains("ProcessPendingUpdatesAsync", newIssue[0].Body);
            Assert.Contains("1/1/2200 12:00:00 AM", newIssue[0].Body);
            Assert.Contains(RepoUrl, newIssue[0].Body);
            Assert.Equal("DependencyUpdateError", newIssue[1].Labels[0]);
            Assert.Contains(RepoUrl, newIssue[1].Title);
            Assert.Contains(BranchTwo, newIssue[1].Body);
            Assert.Contains("ProcessPendingUpdatesAsync error", newIssue[1].Body);
            Assert.Contains("1/1/2200 12:00:00 AM", newIssue[1].Body);
            Assert.Contains(RepoUrl, newIssue[1].Body);
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Creates/updates the github issue.
        /// </summary>
        /// <param name="updateHistoryError">Error info for which github issue has to be created</param>
        /// <param name="issueRepo">Repository where the github issue is created</param>
        /// <param name="shouldReplaceDescription">Func that carries info the description has to be replaced </param>
        /// <param name="description">Description for the issue body / comment body</param>
        /// <returns></returns>
        private async Task CreateOrUpdateGithubIssueAsync(
            RepositoryBranchUpdateHistoryEntry updateHistoryError,
            string issueRepo,
            Func <string, string, bool> shouldReplaceDescription,
            string description)
        {
            _logger.LogInformation($"Error Message : '{updateHistoryError.ErrorMessage}' in repository :  '{updateHistoryError.Repository}'");
            IReliableDictionary <(string repository, string branch), int> gitHubIssueEvaluator =
                await _stateManager.GetOrAddAsync <IReliableDictionary <(string repository, string branch), int> >("gitHubIssueEvaluator");

            GitHubClient client = await AuthenticateGitHubClient(issueRepo);

            var parseRepoUri = ParseRepoUri(issueRepo);

            Octokit.Repository repo = await client.Repository.Get(
                parseRepoUri.owner,
                parseRepoUri.repo);

            var issueNumber = new ConditionalValue <int>();

            using (ITransaction tx = _stateManager.CreateTransaction())
            {
                issueNumber = await gitHubIssueEvaluator.TryGetValueAsync(
                    tx,
                    (updateHistoryError.Repository,
                     updateHistoryError.Branch));

                await tx.CommitAsync();
            }
            if (issueNumber.HasValue)
            {
                Issue issue = await client.Issue.Get(repo.Id, issueNumber.Value);

                // check if the issue is open only then update it else create a new issue and update the dictionary.
                if (issue.State.Equals("Open"))
                {
                    _logger.LogInformation($@"Updating a gitHub issue number : '{issueNumber}' for the error : '{updateHistoryError.ErrorMessage}' for the repository : '{updateHistoryError.Repository}'");
                    await UpdateIssueAsync(
                        client,
                        updateHistoryError,
                        shouldReplaceDescription,
                        description,
                        issue,
                        repo.Id);

                    return;
                }
            }
            // Create a new issue for the error if the issue is already closed or the issue does not exists.
            _logger.LogInformation($@"Creating a new gitHub issue for dependency Update Error, for the error message : '{updateHistoryError.ErrorMessage} for the repository : '{updateHistoryError.Repository}'");
            await CreateIssueAsync(
                client,
                updateHistoryError,
                gitHubIssueEvaluator,
                description,
                repo.Id,
                issueRepo);
        }
        public async Task <IActionResult> RetryActionAsync([Required] string repository, [Required] string branch, long timestamp)
        {
            if (string.IsNullOrEmpty(repository))
            {
                ModelState.TryAddModelError(nameof(repository), "The repository parameter is required");
            }

            if (string.IsNullOrEmpty(branch))
            {
                ModelState.TryAddModelError(nameof(branch), "The branch parameter is required");
            }

            if (!ModelState.IsValid)
            {
                return(BadRequest(ModelState));
            }

            DateTime ts = DateTimeOffset.FromUnixTimeSeconds(timestamp).UtcDateTime;

            Data.Models.RepositoryBranch repoBranch = await Context.RepositoryBranches.FindAsync(repository, branch);

            if (repoBranch == null)
            {
                return(NotFound());
            }

            RepositoryBranchUpdateHistoryEntry update = await Context.RepositoryBranchUpdateHistory
                                                        .Where(u => u.Repository == repository && u.Branch == branch)
                                                        .FirstOrDefaultAsync(u => Math.Abs(EF.Functions.DateDiffSecond(u.Timestamp, ts)) < 1);

            if (update == null)
            {
                return(NotFound());
            }

            if (update.Success)
            {
                return(StatusCode(
                           (int)HttpStatusCode.NotAcceptable,
                           new ApiError("That action was successful, it cannot be retried.")));
            }

            Queue.Post(
                async() =>
            {
                IPullRequestActor actor =
                    PullRequestActorFactory(PullRequestActorId.Create(update.Repository, update.Branch));
                await actor.RunActionAsync(update.Method, update.Arguments);
            });

            return(Accepted());
        }
Ejemplo n.º 6
0
        public async Task ShouldCreateIssue(string repoUrl, string branch, string method, string arguments, DateTime errorOccurredAt, bool success)
        {
            // Not testing SubscriptionUpdateHistoryEntry since this is always "yes" for them.
            RepositoryBranchUpdateHistoryEntry repositoryBranchUpdate =
                new RepositoryBranchUpdateHistoryEntry
            {
                Repository = repoUrl,
                Branch     = branch,
                Method     = method,
                Timestamp  = errorOccurredAt,
                Arguments  = arguments,
                Success    = success
            };
            Repository repository = new Repository();

            GithubClient.Setup(x => x.Repository.Get(It.IsAny <string>(), It.IsAny <string>())).ReturnsAsync(repository);
            Mock <Maestro.Data.Models.Subscription> subscription = new Mock <Maestro.Data.Models.Subscription>();

            subscription.Object.Id = Guid.Parse(SubscriptionId);
            Context.Subscriptions.Add(subscription.Object);
            Context.SaveChanges();
            Mock <Issue> issue = new Mock <Issue>();

            //Shared mocks works for Xunit as it creates a separate file for each test, but for Nunit there will be a conflict.
            //We need to take care of this if we port to Nunit in future.
            GithubClient.Setup(x => x.Issue.Create(It.IsAny <long>(), It.IsAny <NewIssue>())).ReturnsAsync(issue.Object);
            Context.RepoBranchUpdateInMemory = new List <RepositoryBranchUpdateHistoryEntry> {
                repositoryBranchUpdate
            };
            DependencyUpdateErrorProcessor errorProcessor =
                ActivatorUtilities.CreateInstance <DependencyUpdateErrorProcessor>(Scope.ServiceProvider,
                                                                                   Context);
            await errorProcessor.ProcessDependencyUpdateErrorsAsync();

            GithubClient.Verify(x => x.Issue.Create(It.IsAny <long>(), It.IsAny <NewIssue>()), Times.Once);
        }
Ejemplo n.º 7
0
        public async Task CreateIssueAndUpdateIssueBody()
        {
            RepositoryBranchUpdateHistoryEntry firstError =
                new RepositoryBranchUpdateHistoryEntry
            {
                Repository   = RepoUrl,
                Branch       = Branch,
                Method       = MethodName,
                Timestamp    = new DateTime(2200, 1, 1),
                Arguments    = $"[\"{SubscriptionId}\",\"{MethodName}\",\"{ErrorMessage}\"]",
                Success      = false,
                ErrorMessage = ErrorMessage,
                Action       = "Creating new issue"
            };

            RepositoryBranchUpdateHistoryEntry secondError =
                new RepositoryBranchUpdateHistoryEntry
            {
                Repository   = RepoUrl,
                Branch       = Branch,
                Method       = MethodName,
                Timestamp    = new DateTime(2200, 2, 1),
                Arguments    = $"[\"{SubscriptionId}\",\"{MethodName}\",\"{ErrorMessage}\"]",
                Success      = false,
                ErrorMessage = ErrorMessage,
                Action       = "Updating existing issue",
            };

            Context.RepoBranchUpdateInMemory = new List <RepositoryBranchUpdateHistoryEntry>
            {
                firstError, secondError
            };
            Mock <Maestro.Data.Models.Subscription> subscription = new Mock <Maestro.Data.Models.Subscription>();

            subscription.Object.Id = Guid.Parse(SubscriptionId);
            subscription.Object.SourceRepository = "Source Repo";
            subscription.Object.TargetRepository = "Target Repo";
            Context.Subscriptions.Add(subscription.Object);
            Context.SaveChanges();

            var repository = new Repository();

            //Shared mocks works for Xunit as it creates a separate file for each test, but for Nunit there will be a conflict.
            //We need to take care of this if we port to Nunit in future.
            GithubClient.Setup(x => x.Repository.Get(It.IsAny <string>(), It.IsAny <string>())).ReturnsAsync(repository);
            Issue           issueCreated = GetIssue();
            List <NewIssue> newIssues    = new List <NewIssue>();

            GithubClient.Setup(x => x.Issue.Create(It.IsAny <long>(), Capture.In(newIssues))).ReturnsAsync(issueCreated);
            GithubClient.Setup(x => x.Issue.Get(RepoId, IssueNumber)).ReturnsAsync(issueCreated);
            List <IssueUpdate> issueUpdate = new List <IssueUpdate>();

            GithubClient.Setup(x => x.Issue.Update(RepoId, IssueNumber, Capture.In(issueUpdate))).ReturnsAsync(issueCreated);
            DependencyUpdateErrorProcessor errorProcessor =
                ActivatorUtilities.CreateInstance <DependencyUpdateErrorProcessor>(Scope.ServiceProvider,
                                                                                   Context);
            await errorProcessor.ProcessDependencyUpdateErrorsAsync();

            newIssues.Should().ContainSingle();
            newIssues[0].Title.Should().Contain(RepoUrl);
            newIssues[0].Labels[0].Should().Be("DependencyUpdateError");
            newIssues[0].Body.Should().Contain(SubscriptionId);
            newIssues[0].Body.Should().Contain(MethodName);
            newIssues[0].Body.Should().Contain(RepoUrl);
            newIssues[0].Body.Should().Contain("1/1/2200 12:00:00 AM");
            newIssues[0].Body.Should().Contain(Branch);
            newIssues[0].Body.Should().Contain($"[marker]: <> (subscriptionId: '{SubscriptionId}', method: '{MethodName}', errorMessage: '{ErrorMessage}')");
        }
Ejemplo n.º 8
0
        public async Task CreateIssueAndUpdateComment()
        {
            const string AnotherMethod = "ProcessPendingUpdatesAsync";
            RepositoryBranchUpdateHistoryEntry firstError =
                new RepositoryBranchUpdateHistoryEntry
            {
                Repository   = RepoUrl,
                Branch       = Branch,
                Method       = MethodName,
                Timestamp    = new DateTime(2200, 1, 1),
                Arguments    = $"[\"{SubscriptionId}\",\"{MethodName}\",\"{ErrorMessage}\"]",
                Success      = false,
                ErrorMessage = ErrorMessage,
                Action       = "Creating new issue"
            };
            RepositoryBranchUpdateHistoryEntry secondError =
                new RepositoryBranchUpdateHistoryEntry
            {
                Repository   = RepoUrl,
                Branch       = Branch,
                Method       = AnotherMethod,
                Timestamp    = new DateTime(2200, 2, 1),
                Arguments    = "ProcessPendingUpdatesAsync error",
                Success      = false,
                ErrorMessage = ErrorMessage,
                Action       = "Create a new issue comment",
            };

            RepositoryBranchUpdateHistoryEntry thirdError =
                new RepositoryBranchUpdateHistoryEntry
            {
                Repository   = RepoUrl,
                Branch       = Branch,
                Method       = AnotherMethod,
                Timestamp    = new DateTime(2200, 3, 1),
                Arguments    = "ProcessPendingUpdatesAsync arguments",
                Success      = false,
                ErrorMessage = ErrorMessage,
                Action       = "Update the comment",
            };

            Context.RepoBranchUpdateInMemory = new List <RepositoryBranchUpdateHistoryEntry>
            {
                firstError, secondError, thirdError
            };
            Maestro.Data.Models.Subscription subscription = new Maestro.Data.Models.Subscription
            {
                Id = Guid.Parse(SubscriptionId),
                SourceRepository = "Source Repo",
                TargetRepository = "Target Repo",
            };
            Context.Subscriptions.Add(subscription);
            Context.SaveChanges();
            Repository repository = new Repository();

            GithubClient.Setup(x => x.Repository.Get(It.IsAny <string>(), It.IsAny <string>())).ReturnsAsync(repository);
            Issue updateIssue = GetIssue();

            Octokit.AuthorAssociation author = new Octokit.AuthorAssociation();
            string       nodeId  = "1";
            IssueComment comment = new IssueComment
                                   (
                1,
                nodeId,
                "Url",
                "htmlUrl",
                $"[marker]: <> (subscriptionId: '', method: '{AnotherMethod}', errorMessage: '{ErrorMessage}')",
                new DateTime(2200, 02, 02),
                new DateTime(2200, 03, 01),
                new User(),
                new ReactionSummary(),
                author);
            List <IssueComment> issueComment = new List <IssueComment> {
                comment
            };
            List <NewIssue> newIssues      = new List <NewIssue>();
            List <string>   newCommentInfo = new List <string>();

            GithubClient.Setup(x => x.Issue.Create(It.IsAny <long>(), Capture.In(newIssues))).ReturnsAsync(updateIssue);
            GithubClient.Setup(x => x.Issue.Get(RepoId, IssueNumber)).ReturnsAsync(updateIssue);
            GithubClient.Setup(x => x.Issue.Comment.GetAllForIssue(RepoId, IssueNumber)).ReturnsAsync(issueComment);
            GithubClient.Setup(x => x.Issue.Comment.Create(RepoId, IssueNumber, Capture.In(newCommentInfo))).ReturnsAsync(comment);
            GithubClient.Setup(x => x.Issue.Comment.Update(RepoId, CommentId, Capture.In(newCommentInfo)))
            .ReturnsAsync(comment);
            DependencyUpdateErrorProcessor errorProcessor =
                ActivatorUtilities.CreateInstance <DependencyUpdateErrorProcessor>(Scope.ServiceProvider,
                                                                                   Context);
            await errorProcessor.ProcessDependencyUpdateErrorsAsync();

            newIssues.Should().ContainSingle();
            newIssues[0].Labels[0].Should().Be("DependencyUpdateError");
            newIssues[0].Body.Should().Contain(RepoUrl);
            newIssues[0].Body.Should().Contain(SubscriptionId);
            newCommentInfo[0].Should().Contain(AnotherMethod);
            newCommentInfo[0].Should().NotContain(SubscriptionId);
            newCommentInfo[0].Should().Contain("2/1/2200 12:00:00 AM");
            newCommentInfo[1].Should().Contain(AnotherMethod);
            newCommentInfo[1].Should().Contain("3/1/2200 12:00:00 AM");
            newCommentInfo[1].Should().NotContain(SubscriptionId);
        }
Ejemplo n.º 9
0
 public static JToken GetArguments(RepositoryBranchUpdateHistoryEntry update)
 {
     return(JToken.FromObject(new Arguments {
         Repository = update.Repository, Branch = update.Branch, Method = update.Method, MethodArguments = update.Arguments
     }));
 }