private static TriageItem GetTriageItemProperties(string body) { if (string.IsNullOrEmpty(body)) { return(null); } TriageItem triageItem = new TriageItem(); Match propertyMatch = _darcBotPropertyRegex.Match(body); if (propertyMatch.Success) { triageItem.UpdatedCategory = GetDarcBotProperty("category", body); } Match triageIdentifierMatch = _darcBotIssueIdentifierRegex.Match(body); if (!triageIdentifierMatch.Success) { return(null); } int.TryParse(triageIdentifierMatch.Groups["buildid"].Value, out int buildId); int.TryParse(triageIdentifierMatch.Groups["index"].Value, out int index); Guid.TryParse(triageIdentifierMatch.Groups["recordid"].Value, out Guid recordId); triageItem.BuildId = buildId; triageItem.RecordId = recordId; triageItem.Index = index; triageItem.ModifiedDateTime = DateTime.Now; return(triageItem); }
public override bool Equals(object obj) { TriageItem compareItem = obj as TriageItem; return((!Object.ReferenceEquals(null, compareItem)) && (BuildId == compareItem.BuildId) && (RecordId == compareItem.RecordId) && (Index == compareItem.Index)); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "POST", Route = null)] HttpRequestMessage req, ILogger log) { var payloadJson = await req.Content.ReadAsStringAsync(); SimpleJsonSerializer serializer = new SimpleJsonSerializer(); IssueEventPayload issuePayload = serializer.Deserialize <IssueEventPayload>(payloadJson); if (issuePayload.Issue.User.Type.HasValue && issuePayload.Issue.User.Type.Value == AccountType.Bot) { log.LogInformation("Comment is from DarcBot, ignoring."); return(new OkObjectResult($"Ignoring DarcBot comment")); } if (issuePayload.Action != "opened" && issuePayload.Action != "reopened" && issuePayload.Action != "closed") { log.LogInformation($"Received github action '{issuePayload.Action}', nothing to do"); return(new OkObjectResult($"DarcBot has nothing to do with github issue action '{issuePayload.Action}'")); } // Determine identifiable information for triage item TriageItem triageItem = GetTriageItemProperties(issuePayload.Issue.Body); triageItem.Url = issuePayload.Issue.HtmlUrl; if (triageItem == null) { /* Item is not a triage item (does not contain identifiable information), do nothing */ log.LogInformation($"{issuePayload.Issue.Url} is not a triage type issue."); return(new OkObjectResult("No identifiable information detected")); } int.TryParse(System.Environment.GetEnvironmentVariable("AppId"), out int appId); // Create jwt token // Private key is stored in Azure Key vault by downloading the private key (pem file) from GitHub, then // using the Azure CLI to store the value in key vault. // ie: az keyvault secret set --vault-name [vault name] --name GitHubApp-DarcBot-PrivateKey --encoding base64 --file [pem key file path] GitHubAppTokenProvider gitHubTokenProvider = new GitHubAppTokenProvider(); var installationToken = gitHubTokenProvider.GetAppTokenFromEnvironmentVariableBase64(appId, "PrivateKey"); // create client using jwt as a bearer token var userAgent = new Octokit.ProductHeaderValue("DarcBot"); GitHubClient appClient = new GitHubClient(userAgent) { Credentials = new Credentials(installationToken, AuthenticationType.Bearer), }; // using the client, create an installation token AccessToken token = await appClient.GitHubApps.CreateInstallationToken(issuePayload.Installation.Id); // with the installation token, create a new GitHubClient that has the apps permissions var gitHubClient = new GitHubClient(new ProductHeaderValue("DarcBot-Installation")) { Credentials = new Credentials(token.Token) }; if (issuePayload.Action == "created" || issuePayload.Action == "opened" || issuePayload.Action == "reopened") { // First, look for duplicate issues that are open var openIssues = new RepositoryIssueRequest { Filter = IssueFilter.All, State = ItemStateFilter.Open, SortProperty = IssueSort.Created, SortDirection = SortDirection.Ascending, }; openIssues.Labels.Add(_darcBotLabelName); log.LogInformation("Getting open issues"); var issues = await gitHubClient.Issue.GetAllForRepository(issuePayload.Repository.Id, openIssues); log.LogInformation($"There are {issues.Count} open issues with the '{_darcBotLabelName}' label"); foreach (var checkissue in issues) { if (checkissue.Number != issuePayload.Issue.Number) { TriageItem issueItem = GetTriageItemProperties(checkissue.Body); if (triageItem.Equals(issueItem)) { await gitHubClient.Issue.Comment.Create(issuePayload.Repository.Id, issuePayload.Issue.Number, $"DarcBot has detected a duplicate issue.\n\nClosing as duplicate of {checkissue.HtmlUrl}\n\nFor more information see {_docLink}"); var issueUpdate = new IssueUpdate { State = ItemState.Closed, }; await gitHubClient.Issue.Update(issuePayload.Repository.Id, issuePayload.Issue.Number, issueUpdate); return(new OkObjectResult($"Resolved as duplicate of {checkissue.Number}")); } } } // No duplicates, add label and move issue to triage var issue = await gitHubClient.Issue.Get(issuePayload.Repository.Id, issuePayload.Issue.Number); var update = issue.ToUpdate(); update.AddLabel(_darcBotLabelName); await gitHubClient.Issue.Update(issuePayload.Repository.Id, issuePayload.Issue.Number, update); triageItem.UpdatedCategory = "InTriage"; } if (issuePayload.Action == "closed") { IReadOnlyList <IssueComment> comments = gitHubClient.Issue.Comment.GetAllForIssue(issuePayload.Repository.Id, issuePayload.Issue.Number).Result; foreach (var comment in comments) { // Look for category information in comment string category = GetDarcBotProperty("category", comment.Body); if (!string.IsNullOrEmpty(category)) { triageItem.UpdatedCategory = category; } } } log.LogInformation($"buildId: {triageItem.BuildId}, recordId: {triageItem.RecordId}, index: {triageItem.Index}, category: {triageItem.UpdatedCategory}, url: {triageItem.Url}"); await IngestTriageItemsIntoKusto(new[] { triageItem }, log); await gitHubClient.Issue.Comment.Create(issuePayload.Repository.Id, issuePayload.Issue.Number, $"DarcBot has updated the 'TimelineIssuesTriage' database.\n**PowerBI reports may take up to 24 hours to refresh**\n\nSee {_docLink} for more information and 'darcbot' usage."); return(new OkObjectResult("Success")); }