ManageIQ/miq_bot

View on GitHub
lib/github_notification_monitor.rb

Summary

Maintainability
A
0 mins
Test Coverage
class GithubNotificationMonitor
  GITHUB_NOTIFICATION_MONITOR_YAML_FILE = Rails.root.join("config", "github_notification_monitor.yml")

  COMMANDS = Hash.new do |h, k|
    normalized = k.to_s.gsub("-", "_")            # Support - or _ in command
    normalized.chop! if normalized.end_with?("s") # Support singular or plural
    h[normalized]    if h.key?(normalized)
  end.merge(
    "add_label"       => :add_labels,
    "remove_label"    => :remove_labels,
    "rm_label"        => :remove_labels,
    "assign"          => :assign,
    "unassign"        => :unassign,
    "add_reviewer"    => :add_reviewer,
    "remove_reviewer" => :remove_reviewer,
    "set_milestone"   => :set_milestone
  ).freeze

  def initialize(fq_repo_name)
    @username = Settings.github_credentials.username
    @fq_repo_name = fq_repo_name
  end

  def process_notifications
    GithubService.repository_notifications(@fq_repo_name, "all" => false).each do |notification|
      process_notification(notification)
    end
  end

  private

  # A notification only notifies about a change to an issue thread, but
  # not which specific comments were added.  Thus, we keep track of the
  # last_processed_timestamp, and check every comment in the issue thread
  # skipping them until we are at the last processed comment.
  def process_notification(notification)
    if notification.issue_number.present?
      issue = GithubService.issue(@fq_repo_name, notification.issue_number)
      process_issue_thread(issue)
    else
      logger.warn("Skipping processing of notification due to missing issue number: #{notification}")
    end
    notification.mark_thread_as_read
  end

  def process_issue_thread(issue)
    @dispatcher = GithubService::CommandDispatcher.new(issue)

    process_issue_comment(issue, issue.author, issue.created_at, issue.body)
    GithubService.issue_comments(@fq_repo_name, issue.number).each do |comment|
      process_issue_comment(issue, comment.author, comment.updated_at, comment.body)
    end
  end

  def process_issue_comment(issue, author, timestamp, body)
    if body.blank?
      logger.warn("Skipping comment due to empty body. Issue: #{issue.url} Author: #{author}, Timestamp: #{timestamp}")
      return
    end

    last_processed_timestamp = timestamps[issue.number] || Time.at(0)
    return if timestamp <= last_processed_timestamp

    @dispatcher.dispatch!(:issuer => author, :text => body)
    update_timestamp(timestamp, issue.number)
  end

  def timestamps
    timestamps_full_hash["timestamps"][@fq_repo_name]
  end

  def update_timestamp(updated_at, issue_number)
    timestamps[issue_number] = updated_at
    save_timestamps
  end

  def timestamps_full_hash
    @timestamps_full_hash ||=
      (YAML.load_file(GITHUB_NOTIFICATION_MONITOR_YAML_FILE, :permitted_classes => [Date, Time]) || {}).tap do |h|
        h["timestamps"] ||= {}
        h["timestamps"][@fq_repo_name] ||= {}
      end
  rescue Errno::ENOENT
    logger.warn("#{Time.now} #{GITHUB_NOTIFICATION_MONITOR_YAML_FILE} was missing, recreating it...")
    FileUtils.touch(GITHUB_NOTIFICATION_MONITOR_YAML_FILE)
    retry
  end

  def save_timestamps
    File.write(GITHUB_NOTIFICATION_MONITOR_YAML_FILE, timestamps_full_hash.to_yaml)
  end

  def logger
    Rails.logger
  end
end