Example #1
0
        protected override async Task <EventHandlerResult> execute(IssueCommentPayload webhookPayload, RepositorySettings repoSettings)
        {
            if (webhookPayload.Issue == null)
            {
                return(EventHandlerResult.PayloadError("no issue information in webhook payload"));
            }

            if (repoSettings.CommentProtection == null || !repoSettings.CommentProtection.Enabled)
            {
                return(EventHandlerResult.Disabled());
            }

            if (webhookPayload.Action.Equals("edited", StringComparison.OrdinalIgnoreCase) || webhookPayload.Action.Equals("deleted", StringComparison.OrdinalIgnoreCase))
            {
                if (webhookPayload.Sender != null && webhookPayload.Comment?.User != null && webhookPayload.Sender.Login == webhookPayload.Comment.User.Login)
                {
                    return(EventHandlerResult.NoActionNeeded($"comment action {webhookPayload.Action} by {webhookPayload.Sender.Login} allowed, referencing own comment"));
                }
                else
                {
                    await GitHubClient.Issue.Comment.Create(webhookPayload.Repository.Id, webhookPayload.Issue.Number, getWarningText(repoSettings.CommentProtection));

                    return(EventHandlerResult.ActionPerformed("comment action resulting in warning"));
                }
            }

            return(EventHandlerResult.EventNotOfInterest(webhookPayload.Action));
        }
Example #2
0
        protected async Task <EventHandlerResult> processComment(ICommentPayload <T> webhookPayload)
        {
            var gradeCommand = new GradeCommentParser(webhookPayload.CommentBody);

            if (gradeCommand.IsMatch)
            {
                if (!await isAllowed(webhookPayload))
                {
                    return(await handleUserNotAllowed(webhookPayload));
                }

                var pr = await getPullRequest(webhookPayload);

                if (pr == null)
                {
                    return(await handleNotPr(webhookPayload));
                }

                await handleApprove(webhookPayload, pr);
                await handleStoreGrade(webhookPayload, gradeCommand, pr);

                await handleReaction(webhookPayload, ReactionType.Plus1);

                return(EventHandlerResult.ActionPerformed($"comment operation to grade done; grades: {string.Join(" ", gradeCommand.Grades)}"));
            }
            else
            {
                return(EventHandlerResult.NoActionNeeded("not recognized as command"));
            }
        }
        protected override async Task <EventHandlerResult> executeCore(WorkflowRunEventPayload webhookPayload)
        {
            if (webhookPayload.Action.Equals("completed", StringComparison.OrdinalIgnoreCase))
            {
                if (string.IsNullOrEmpty(webhookPayload.Sender?.Login))
                {
                    return(EventHandlerResult.PayloadError("missing actor user"));
                }

                if (await isUserOrganizationMember(webhookPayload, webhookPayload.Sender.Login))
                {
                    return(EventHandlerResult.NoActionNeeded("workflow_run ok, not triggered by student"));
                }

                var workflowRuns = await GitHubClient.CountWorkflowRunsInRepository(webhookPayload.Repository.Owner.Login, webhookPayload.Repository.Name, webhookPayload.Sender.Login);

                if (workflowRuns <= WorkflowRunThreshold)
                {
                    return(EventHandlerResult.NoActionNeeded("workflow_run ok, has less then threshold"));
                }

                var prNum = await getMostRecentPullRequest(webhookPayload);

                if (prNum.HasValue)
                {
                    await GitHubClient.Issue.Comment.Create(webhookPayload.Repository.Id, prNum.Value, WarningText);
                }
                return(EventHandlerResult.ActionPerformed("workflow_run warning, threshold exceeded"));
            }

            return(EventHandlerResult.EventNotOfInterest(webhookPayload.Action));
        }
        protected override async Task <EventHandlerResult> executeCore(PullRequestEventPayload webhookPayload)
        {
            if (webhookPayload.PullRequest == null)
            {
                return(EventHandlerResult.PayloadError("no pull request information in webhook payload"));
            }

            if (webhookPayload.Action.Equals("opened", StringComparison.OrdinalIgnoreCase))
            {
                var repositoryPrs = await getAllRepoPullRequests(webhookPayload);

                if (repositoryPrs.Count <= 1)
                {
                    return(EventHandlerResult.NoActionNeeded("pull request open is ok, there are no other PRs"));
                }
                else
                {
                    var(handledOpen, resultOpen) = await handleAnyOpenPrs(webhookPayload, repositoryPrs);

                    var(handledClosed, resultClosed) = await handleAnyClosedPrs(webhookPayload, repositoryPrs);

                    if (!handledOpen && !handledClosed)
                    {
                        return(EventHandlerResult.NoActionNeeded($"{resultOpen}; {resultClosed}"));
                    }
                    else
                    {
                        return(EventHandlerResult.ActionPerformed($"{resultOpen}; {resultClosed}"));
                    }
                }
            }

            return(EventHandlerResult.EventNotOfInterest(webhookPayload.Action));
        }
Example #5
0
        protected override async Task <EventHandlerResult> execute(PullRequestEventPayload webhookPayload, RepositorySettings repoSettings)
        {
            if (webhookPayload.PullRequest == null)
            {
                return(EventHandlerResult.PayloadError("no pull request information in webhook payload"));
            }

            if (repoSettings.ReviewerToAssignee == null || !repoSettings.ReviewerToAssignee.Enabled)
            {
                return(EventHandlerResult.Disabled());
            }

            if (webhookPayload.Action.Equals("review_requested", StringComparison.OrdinalIgnoreCase))
            {
                if (webhookPayload.PullRequest.RequestedReviewers == null || webhookPayload.PullRequest.RequestedReviewers.Count == 0)
                {
                    return(EventHandlerResult.PayloadError("no requested reviewer in webhook payload"));
                }
                else if (!isPrAssignedToReviewer(webhookPayload))
                {
                    await GitHubClient.Issue.Assignee.AddAssignees(webhookPayload.Repository.Owner.Login, webhookPayload.Repository.Name, webhookPayload.PullRequest.Number, getUsersToAssign(webhookPayload));

                    return(EventHandlerResult.ActionPerformed("pull request review_requested handled, assignee set"));
                }
                else
                {
                    return(EventHandlerResult.NoActionNeeded("pull request review_requested is ok, assignee is present"));
                }
            }

            return(EventHandlerResult.EventNotOfInterest(webhookPayload.Action));
        }
        private async Task <(RepositorySettings, EventHandlerResult)> tryGetRepositorySettings(TPayload webhookPayload, string organizationLogin)
        {
            try
            {
                var contents =
                    await GitHubClient.Repository.Content.GetAllContents(organizationLogin, "ahk-monitor-config",
                                                                         "ahk-monitor.yml")
                    ?? await GitHubClient.Repository.Content.GetAllContentsByRef(webhookPayload.Repository.Id,
                                                                                 ".github/ahk-monitor.yml", webhookPayload.Repository.DefaultBranch);

                var settingsString = contents?.FirstOrDefault()?.Content;

                if (settingsString == null)
                {
                    return(null, EventHandlerResult.Disabled("repository has no ahk-monitor.yml"));
                }

                try
                {
                    return(YamlDeserializer.Deserialize <RepositorySettings>(settingsString), null);
                }
                catch (Exception ex)
                {
                    return(null, EventHandlerResult.PayloadError("Config yaml parse error: " + ex.Message));
                }
            }
            catch (NotFoundException)
            {
                return(null, EventHandlerResult.Disabled("repository has no ahk-monitor.yml"));
            }
        }
Example #7
0
        private async Task <EventHandlerResult> handleUserNotAllowed(ICommentPayload <T> webhookPayload)
        {
            await handleReaction(webhookPayload, ReactionType.Confused);

            var comment = WarningText.Replace("{}", webhookPayload.CommentingUser, StringComparison.OrdinalIgnoreCase);
            await GitHubClient.Issue.Comment.Create(webhookPayload.Repository.Id, webhookPayload.PullRequestNumber, comment);

            return(EventHandlerResult.ActionPerformed("comment operation to grade not allowed for user"));
        }
        protected override async Task <EventHandlerResult> executeCore(CreateEventPayload webhookPayload)
        {
            if (!webhookPayload.RefType.StringValue.Equals("branch", StringComparison.OrdinalIgnoreCase))
            {
                return(EventHandlerResult.NoActionNeeded($"create event for ref {webhookPayload.RefType} is not of interest"));
            }

            await GitHubClient.Repository.Branch.UpdateBranchProtection(
                webhookPayload.Repository.Id, webhookPayload.Ref, getBranchProtectionSettingsUpdate(webhookPayload.Ref, webhookPayload.Repository.DefaultBranch));

            return(EventHandlerResult.ActionPerformed("branch protection rule applied"));
        }
        private async Task <EventHandlerResult> OkCommandExecute(string commentContents, IssueCommentPayload payload)
        {
            var merge = new MergePullRequest();

            merge.CommitTitle = $"merged PR via +ok: #{payload.Issue.Number} {payload.Issue.Title}";
            var result = await GitHubClient.PullRequest.Merge(payload.Repository.Id, payload.Issue.Number, merge);

            return(result.Merged
                ? EventHandlerResult.ActionPerformed(
                       $"merged pull request #{payload.Issue.Number} {payload.Issue.Title}")
                : EventHandlerResult.PayloadError(
                       $"failed to merge pull request ${payload.Issue.Number} {payload.Issue.Title}"));
        }
        protected override async Task <EventHandlerResult> executeCore(PullRequestReviewEventPayload webhookPayload)
        {
            if (webhookPayload.Review == null)
            {
                return(EventHandlerResult.PayloadError("no review information in webhook payload"));
            }

            if (webhookPayload.Action.Equals("submitted", StringComparison.OrdinalIgnoreCase))
            {
                return(await processComment(new ReviewCommentPayloadFacade(webhookPayload)));
            }

            return(EventHandlerResult.EventNotOfInterest(webhookPayload.Action));
        }
        protected override async Task <EventHandlerResult> executeCore(IssueCommentPayload webhookPayload)
        {
            if (webhookPayload.Issue == null)
            {
                return(EventHandlerResult.PayloadError("no issue information in webhook payload"));
            }

            if (webhookPayload.Action.Equals("created", StringComparison.OrdinalIgnoreCase))
            {
                return(await processComment(new IssueCommentPayloadFacade(webhookPayload)));
            }

            return(EventHandlerResult.EventNotOfInterest(webhookPayload.Action));
        }
        protected bool tryParsePayload(string requestBody, out TPayload payload, out EventHandlerResult errorResult, out string organizationLogin)
        {
            payload           = null;
            organizationLogin = null;
            if (string.IsNullOrEmpty(requestBody))
            {
                errorResult = EventHandlerResult.PayloadError("request body was empty");
                return(false);
            }

            try
            {
                payload = new SimpleJsonSerializer().Deserialize <TPayload>(requestBody);
            }
            catch (Exception ex)
            {
                errorResult = EventHandlerResult.PayloadError($"request body deserialization failed: {ex.Message}");
                return(false);
            }

            if (payload == null)
            {
                errorResult = EventHandlerResult.PayloadError("parsed payload was null or empty");
                return(false);
            }

            if (payload.Repository == null)
            {
                errorResult = EventHandlerResult.PayloadError("no repository information in webhook payload");
                return(false);
            }

            try
            {
                var jsonobj = JsonDocument.Parse(requestBody);
                organizationLogin = jsonobj.RootElement.GetProperty("organization").GetProperty("login").GetString();
            }
            catch (Exception)
            {
                errorResult = EventHandlerResult.PayloadError($"no organization info in webhook payload");
                return(false);
            }

            errorResult = null;
            return(true);
        }
Example #13
0
        public async Task <EventHandlerResult> Execute(string requestBody)
        {
            if (!tryParsePayload(requestBody, out var webhookPayload, out var errorResult))
            {
                return(errorResult);
            }

            GitHubClient = await gitHubClientFactory.CreateGitHubClient(webhookPayload.Installation.Id, Logger);

            if (!await isEnabledForRepository(webhookPayload))
            {
                Logger.LogInformation("no ahk-monitor.yml or disabled");
                return(EventHandlerResult.Disabled("no ahk-monitor.yml or disabled"));
            }

            return(await executeCore(webhookPayload));
        }
        protected override async Task <EventHandlerResult> execute(IssueCommentPayload webhookPayload, RepositorySettings repoSettings)
        {
            if (webhookPayload.Issue == null)
            {
                return(EventHandlerResult.PayloadError("no issue information in webhook payload"));
            }

            if (repoSettings.PullRequestCommentCommand == null || !repoSettings.PullRequestCommentCommand.Enabled)
            {
                return(EventHandlerResult.Disabled());
            }

            if (!webhookPayload.Action.Equals("created", StringComparison.OrdinalIgnoreCase))
            {
                return(EventHandlerResult.EventNotOfInterest(webhookPayload.Action));
            }

            var contents = webhookPayload.Comment.Body;

            if (!contents.StartsWith('+'))
            {
                return(EventHandlerResult.NoActionNeeded("comment is not a command"));
            }

            var endOfWord        = contents.IndexOf(' ');
            var potentialCommand = endOfWord >= 0
                ? contents.Substring(1, contents.IndexOf(' ') - 1)
                : contents.Substring(1);

            if (!commands.ContainsKey(potentialCommand))
            {
                return(EventHandlerResult.NoActionNeeded($"invalid command: +{potentialCommand}"));
            }

            if (!await commands[potentialCommand].checkPermission(webhookPayload))
            {
                return(EventHandlerResult.PayloadError($"{webhookPayload.Comment.User.Login} is not allowed to execute the command: {contents}"));
            }

            return(await commands[potentialCommand].execute(contents, webhookPayload));
        }
Example #15
0
        protected bool tryParsePayload(string requestBody, out TPayload payload, out EventHandlerResult errorResult)
        {
            payload = null;
            if (string.IsNullOrEmpty(requestBody))
            {
                errorResult = EventHandlerResult.PayloadError("request body was empty");
                Logger.LogError("request body was empty");
                return(false);
            }

            try
            {
                payload = new SimpleJsonSerializer().Deserialize <TPayload>(requestBody);
            }
            catch (Exception ex)
            {
                errorResult = EventHandlerResult.PayloadError($"request body deserialization failed: {ex.Message}");
                Logger.LogError(ex, "request body deserialization failed");
                return(false);
            }

            if (payload == null)
            {
                errorResult = EventHandlerResult.PayloadError("parsed payload was null or empty");
                Logger.LogError("parsed payload was null or empty");
                return(false);
            }

            if (payload.Repository == null)
            {
                errorResult = EventHandlerResult.PayloadError("no repository information in webhook payload");
                Logger.LogError("no repository information in webhook payload");
                return(false);
            }

            errorResult = null;
            return(true);
        }
        public async Task <EventHandlerResult> Execute(string requestBody)
        {
            if (!tryParsePayload(requestBody, out var webhookPayload, out var errorResult, out var organizationLogin))
            {
                return(errorResult);
            }

            GitHubClient = await gitHubClientFactory.CreateGitHubClient(webhookPayload.Installation.Id);

            var(repoSettings, repoSettingsErrorResult) = await tryGetRepositorySettings(webhookPayload, organizationLogin);

            if (repoSettings == null)
            {
                return(repoSettingsErrorResult);
            }

            if (!repoSettings.Enabled)
            {
                return(EventHandlerResult.Disabled($"ahk-monitor.yml disabled app for repository {webhookPayload.Repository.FullName}"));
            }

            return(await execute(webhookPayload, repoSettings));
        }
Example #17
0
        private async Task <EventHandlerResult> handleNotPr(ICommentPayload <T> webhookPayload)
        {
            await handleReaction(webhookPayload, ReactionType.Confused);

            return(EventHandlerResult.ActionPerformed("comment operation to grade not called for PR"));
        }