public async Task <IActionResult> OnPost(string gitHubIssueUri, string formAction) { if (!GitHubIssueKey.TryCreateFromUri(gitHubIssueUri, out var issueKey)) { return(await OnError("Not a valid GitHub Issue Url")); } var buildKey = GetBuildKey(Number !.Value); if (formAction == "addIssue") { try { var modelBuild = await TriageContextUtil.GetModelBuildAsync(buildKey); await TriageContextUtil.EnsureGitHubIssueAsync(modelBuild, issueKey, saveChanges : true); } catch (DbUpdateException ex) { if (!ex.IsUniqueKeyViolation()) { throw; } } } else { var query = TriageContextUtil .GetModelBuildQuery(buildKey) .SelectMany(x => x.ModelGitHubIssues) .Where(x => x.Organization == issueKey.Organization && x.Repository == issueKey.Repository && x.Number == issueKey.Number); var modelGitHubIssue = await query.FirstOrDefaultAsync(); if (modelGitHubIssue is object) { TriageContextUtil.Context.ModelGitHubIssues.Remove(modelGitHubIssue); await TriageContextUtil.Context.SaveChangesAsync(); } } var util = new TrackingGitHubUtil(GitHubClientFactory, TriageContextUtil.Context, SiteLinkUtil.Published, Logger); await util.UpdateAssociatedGitHubIssueAsync(issueKey); return(await OnGet()); async Task <IActionResult> OnError(string message) { GitHubIssueAddErrorMessage = message; return(await OnGet()); } }
public async Task <IActionResult> OnPost() { if (TrackingKind == TrackingKind.Unknown) { ErrorMessage = "Invalid Tracking Kind"; return(Page()); } if (string.IsNullOrEmpty(IssueTitle)) { ErrorMessage = "Need an issue title"; return(Page()); } if (IssueTitle.Length >= ModelTrackingIssue.IssueTitleLengthLimit) { ErrorMessage = $"Please limit issue title to {ModelTrackingIssue.IssueTitleLengthLimit} characters"; return(Page()); } if (string.IsNullOrEmpty(SearchText)) { ErrorMessage = "Must provide search text"; return(Page()); } ModelBuildDefinition?modelBuildDefinition = null; if (!string.IsNullOrEmpty(Definition)) { modelBuildDefinition = await TriageContextUtil.FindModelBuildDefinitionAsync(Definition); if (modelBuildDefinition is null) { ErrorMessage = $"Cannot find build definition with name or ID: {Definition}"; return(Page()); } } switch (TrackingKind) { #pragma warning disable 618 case TrackingKind.HelixConsole: case TrackingKind.HelixRunClient: ErrorMessage = $"'{TrackingKind}' is deprecated. Please use {TrackingKind.HelixLogs}"; return(Page()); case TrackingKind.HelixLogs: { if (TryParseQueryString <SearchHelixLogsRequest>(out var request)) { if (request.HelixLogKinds.Count == 0) { ErrorMessage = "Need to specify at least one log kind to search"; return(Page()); } } else { return(Page()); } } break; case TrackingKind.Test: if (!TryParseQueryString <SearchTestsRequest>(out _)) { return(Page()); } break; case TrackingKind.Timeline: if (!TryParseQueryString <SearchTimelinesRequest>(out _)) { return(Page()); } break; } GitHubIssueKey?issueKey = null; if (!string.IsNullOrEmpty(GitHubIssueUri)) { if (GitHubIssueKey.TryCreateFromUri(GitHubIssueUri, out var key)) { issueKey = key; GitHubOrganization = key.Organization; GitHubRepository = key.Repository; } else { ErrorMessage = $"Invalid GitHub issue link: {GitHubIssueUri}"; return(Page()); } } else if (string.IsNullOrEmpty(GitHubRepository) || string.IsNullOrEmpty(GitHubOrganization)) { ErrorMessage = "Must provide GitHub organization and repository"; return(Page()); } IGitHubClient?gitHubClient = null; try { gitHubClient = await GitHubClientFactory.CreateForAppAsync(GitHubOrganization, GitHubRepository); } catch (Exception ex) { ErrorMessage = $"Cannot create GitHub client for that repository: {ex.Message}"; return(Page()); } var modelTrackingIssue = await CreateTrackingIssue(gitHubClient); return(RedirectToPage( "./Issue", new { id = modelTrackingIssue.Id })); async Task <ModelTrackingIssue> CreateTrackingIssue(IGitHubClient gitHubClient) { var issueKey = await GetOrCreateGitHubIssueAsync(gitHubClient); var modelTrackingIssue = new ModelTrackingIssue() { IsActive = true, IssueTitle = IssueTitle, TrackingKind = TrackingKind, SearchQuery = SearchText, ModelBuildDefinition = modelBuildDefinition, GitHubOrganization = issueKey.Organization, GitHubRepository = issueKey.Repository, GitHubIssueNumber = issueKey.Number, }; TriageContext.ModelTrackingIssues.Add(modelTrackingIssue); await TriageContext.SaveChangesAsync(); await InitialTriageAsync(modelTrackingIssue); return(modelTrackingIssue); } async Task InitialTriageAsync(ModelTrackingIssue modelTrackingIssue) { if (modelBuildDefinition is object) { // The initial triage is only done for tracking issues that have definitions // associated with. Lacking a definition we end up querying all builds and that // can produce a *lot* of data. Hard to find a good formula that is performant // there hence we limit to only builds with definitions. var request = new SearchBuildsRequest() { Definition = modelBuildDefinition?.DefinitionId.ToString() ?? null, Queued = new DateRequestValue(7, RelationalKind.GreaterThan), Result = new BuildResultRequestValue(BuildResult.Succeeded, EqualsKind.NotEquals), }; await FunctionQueueUtil.QueueTriageBuildAttempts(TriageContextUtil, modelTrackingIssue, request); // Issues are bulk updated on a 15 minute cycle. This is a new issue though so want to make sure that // the user sees progress soon. Schedule two manual updates in the near future on this so the issue // gets rolling then it will fall into the 15 minute bulk cycle. await FunctionQueueUtil.QueueUpdateIssueAsync(modelTrackingIssue, TimeSpan.FromSeconds(30)); await FunctionQueueUtil.QueueUpdateIssueAsync(modelTrackingIssue, TimeSpan.FromMinutes(2)); } else { await FunctionQueueUtil.QueueUpdateIssueAsync(modelTrackingIssue, TimeSpan.FromMinutes(0)); } } async Task <GitHubIssueKey> GetOrCreateGitHubIssueAsync(IGitHubClient gitHubClient) { if (issueKey is { } key) { await TrackingGitHubUtil.EnsureGitHubIssueHasMarkers(gitHubClient, key); return(key); } var newIssue = new NewIssue(IssueTitle) { Body = TrackingGitHubUtil.WrapInStartEndMarkers("Runfo Creating Tracking Issue (data being generated)") }; var issue = await gitHubClient.Issue.Create(GitHubOrganization, GitHubRepository, newIssue); return(issue.GetIssueKey()); } bool TryParseQueryString <T>(out T value) where T : ISearchRequest, new() { value = new T(); try { value.ParseQueryString(SearchText ?? ""); return(true); } catch (Exception ex) { ErrorMessage = ex.ToString(); return(false); } } }
public TrackingGitHubUtilTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { TrackingGitHubUtil = new TrackingGitHubUtil(TestableGitHubClientFactory, Context, new SiteLinkUtil("localhost"), TestableLogger); }