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; } }
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); }
/// <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()); }
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); }
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}')"); }
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); }
public static JToken GetArguments(RepositoryBranchUpdateHistoryEntry update) { return(JToken.FromObject(new Arguments { Repository = update.Repository, Branch = update.Branch, Method = update.Method, MethodArguments = update.Arguments })); }