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);
        }
Exemple #2
0
        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"));
        }