def parse_webhook_data()

in services/lambda-mxnet-ci-bot/CIBot.py [0:0]


    def parse_webhook_data(self, event):
        """
        This method triggers the CI bot when the appropriate
        GitHub event is recognized by use of a webhook
        :param event: The event data that is received whenever a PR comment is made
        :return: Log statements which we can track in lambda
        """
        try:
            github_event = ast.literal_eval(event["Records"][0]['body'])['headers']["X-GitHub-Event"]
            logging.info(f"github event {github_event}")
        except KeyError:
            raise Exception("Not a GitHub Event")

        if not self._secure_webhook(event):
            raise Exception("Failed to validate WebHook security")

        try:
            payload = json.loads(ast.literal_eval(event["Records"][0]['body'])['body'])
        except ValueError:
            raise Exception("Decoding JSON for payload failed")

        # find all jobs currently run in CI
        self._find_all_jobs()
        if not self.all_jobs:
            raise Exception("Unable to gather jobs from the CI")

        if(github_event == "pull_request"):
            pr_num = payload['number']
            if(payload['action'] == 'opened'):
                logging.info('New PR create event detected. Send help guide.')
                pr_author = payload['pull_request']['user']['login']
                if(self.auto_trigger):
                    intro_mesg = "All tests are already queued to run once. If tests fail, you can trigger one or more tests again with the following commands:"
                else:
                    intro_mesg = "Once your PR is ready for CI checks, invoke the following commands:"
                message = "Hey @" + pr_author + " , Thanks for submitting the PR \n" \
                    + intro_mesg + " \n" \
                    "- To trigger all jobs: @" + self.bot_user + " run ci [all] \n" \
                    "- To trigger specific jobs: @" + self.bot_user + " run ci [job1, job2] \n" \
                    "*** \n" \
                    "**CI supported jobs**: " + str(list(self.all_jobs)).translate(self.translation) + "\n" \
                    "*** \n" \
                    "_Note_: \n Only following 3 categories can trigger CI :" \
                    "PR Author, MXNet Committer, Jenkins Admin. \n" \
                    "All CI tests must pass before the PR can be merged. \n"
                self.create_comment(pr_num, message)
            elif(payload['action'] == 'closed' and payload['pull_request']['merged'] is True and self.run_after_merge):
                if(payload['pull_request']['base']['ref'] != 'master'):
                    # PR not merged into master
                    # no need to run CI
                    logging.info('PR merged into' + payload['pull_request']['base']['ref'] + '.Hence ignore.')
                    return
                # PR has been merged into master
                # trigger a final CI run on master
                successful_jobs = self._trigger_ci(self.all_jobs, "master")
                message = "PR #" + str(pr_num) + " merged. Congrats! \n"
                if successful_jobs:
                    message += "Jenkins CI successfully triggered : " + str(successful_jobs).translate(self.translation)
                else:
                    message += "However, the bot is unable to trigger CI."
                self.create_comment(pr_num, message)
            else:
                # other actions : reopened, deleted
                logging.info('Irrelevant PR related event. Ignore.')
            return

        if github_event in ["check_suite", "check_run", "status"]:
            # if payload["check_suite"]["app"]["slug"] == "github-actions":
            logging.info('Irrelevant event. Ignore.')
            return
        if "action" in payload:
            if(payload["action"] == 'deleted'):
                logging.info('comment deleted. Ignore.')
                return
        # fetch comment author
        comment_author = payload["comment"]["user"]["login"]

        # if comment author is label bot itself, ignore
        if comment_author == self.bot_user:
            logging.info('Ignore comments made by bot')
            return

        logging.info(f"payload loaded {payload}")
        issue_num = payload["issue"]["number"]
        # Grab actual payload data of the appropriate GitHub event needed for
        # triggering CI
        if github_event == "issue_comment":
            # Check if the bot is invoked from a PR or an issue
            if "@" + str(self.bot_user) in payload["comment"]["body"].lower() and "pull_request" not in payload["issue"]:
                # This means the bot didn't get invoked from a PR
                message = "Hey @" + comment_author + " \n @" + self.bot_user + " can only be invoked on a PR."
                self.create_comment(issue_num, message)
                logging.error("Bot invoked on an Issue instead of PR")
                return
            # Look for phrase referencing @<bot_user>
            if "@" + str(self.bot_user) in payload["comment"]["body"].lower():
                # if(payload["comment"]["body"].find("]")==-1):
                #     # ] not found in the phrase; capture everything bot's name onwards
                #     phrase = payload["comment"]["body"][payload["comment"]["body"].find("@"+str(self.bot_user)):]
                # else:
                #     # ] found in the phrase; capture everything between bot's name and end of list token ]
                phrase = payload["comment"]["body"][payload["comment"]["body"].find("@" + str(self.bot_user)):payload["comment"]["body"].find("]") + 1]
                # remove @<bot_user from the phrase
                phrase = phrase.replace('@' + self.bot_user, '')
                logging.info(phrase)
                # remove whitespace characters
                phrase = ' '.join(phrase.split())

                # Handles both cases : ( run ci[job1] ) and ( run ci [job1] ) are treated the same way
                action = phrase[0:phrase.find('[')].strip()

                logging.info(f'action {action}')

                # only looking for the word run in PR Comment
                if action not in ['run ci']:
                    message = "Undefined action detected. \n" \
                              "Permissible actions are : run ci [all], run ci [job1, job2] \n" \
                              "Example : @" + self.bot_user + " run ci [all] \n" \
                              "Example : @" + self.bot_user + " run ci [centos-cpu, clang]"
                    self.create_comment(issue_num, message)
                    logging.error(f'Undefined action by user: {action}')
                    raise Exception("Undefined action by user")

                # parse jobs from the comment
                user_jobs = self._parse_jobs_from_comment(phrase)
                if not user_jobs:
                    logging.error(f'Message typed by user: {phrase}')
                    raise Exception("No jobs found from PR comment")

                # check if any of the jobs requested by user are supported by CI
                # intersection of user request jobs and CI supported jobs
                if user_jobs == ['all']:
                    valid_jobs = self.all_jobs
                else:
                    valid_jobs = list(set(user_jobs).intersection(set(self.all_jobs)))

                if not valid_jobs:
                    logging.error(f'Jobs entered by user: {set(user_jobs)}')
                    logging.error(f'CI supported Jobs: {set(self.all_jobs)}')
                    message = "None of the jobs entered are supported. \n" \
                              "Jobs entered by user: " + str(user_jobs).translate(self.translation) + "\n" \
                              "CI supported Jobs: " + str(list(self.all_jobs)).translate(self.translation) + "\n"
                    self.create_comment(issue_num, message)
                    raise Exception("Provided jobs don't match the ones supported by CI")

                # check if the comment author is authorized
                pr_author = payload["issue"]["user"]["login"]

                if self._is_authorized(comment_author, pr_author):
                    logging.info(f'Authorized user: {comment_author}')
                    # since authorized user commented, go ahead trigger CI
                    # branch to be triggered is PR-N where N is the PR number
                    successful_jobs = self._trigger_ci(valid_jobs, "PR-" + str(issue_num))
                    if successful_jobs:
                        message = "Jenkins CI successfully triggered : " + str(successful_jobs).translate(self.translation)
                    else:
                        message = "Authorized user recognized. However, the bot is unable to trigger CI."
                    self.create_comment(issue_num, message)
                else:
                    # since unauthorized user tried to trigger CI
                    logging.info(f'Unauthorized user: {comment_author}')
                    message = "Unauthorized access detected. \n" \
                              "Only following 3 categories can trigger CI : \n" \
                              "PR Author, MXNet Committer, Jenkins Admin."
                    self.create_comment(issue_num, message)
            else:
                logging.error("CI Bot is not called")
                return
        else:
            logging.info(f'GitHub Event unsupported by CI Bot: {github_event}')  # {payload["action"]}