issue_tracker.rb 6.07 KB
class IssueTracker
  include Mongoid::Document
  include Mongoid::Timestamps
  include HashHelper
  include Rails.application.routes.url_helpers
  default_url_options[:host] = Errbit::Application.config.action_mailer.default_url_options[:host]

  validate :check_params

  embedded_in :app, :inverse_of => :issue_tracker

  field :account, :type => String
  field :api_token, :type => String
  field :project_id, :type => String
  field :ticket_properties, :type => String
  field :username, :type => String
  field :password, :type => String
  field :issue_tracker_type, :type => String, :default => 'none'

  def create_issue err
    case issue_tracker_type
    when 'lighthouseapp'
      create_lighthouseapp_issue err
    when 'redmine'
      create_redmine_issue err
    when 'pivotal'
      create_pivotal_issue err
    when 'fogbugz'
      create_fogbugz_issue err
    when 'mingle'
      create_mingle_issue err
    end
  end

  def ticket_properties_hash
    # Parses 'key=value, key2=value2' from user input into a ruby hash.
    self.ticket_properties.split(",").inject({}) do |hash, pair|
      key, value = pair.split("=").map(&:strip)
      hash[key] = value
      hash
    end
  end

  protected
  def create_redmine_issue err
    token = api_token
    acc = account
    RedmineClient::Base.configure do
      self.token = token
      self.site = acc
    end
    issue = RedmineClient::Issue.new(:project_id => project_id)
    issue.subject = issue_title err
    issue.description = self.class.redmine_body_template.result(binding)
    issue.save!
    err.update_attribute :issue_link, "#{RedmineClient::Issue.site.to_s.sub(/#{RedmineClient::Issue.site.path}$/, '')}#{RedmineClient::Issue.element_path(issue.id, :project_id => project_id)}".sub(/\.xml\?project_id=#{project_id}$/, "\?project_id=#{project_id}")
  end

  def create_pivotal_issue err
    PivotalTracker::Client.token = api_token
    PivotalTracker::Client.use_ssl = true
    project = PivotalTracker::Project.find project_id.to_i
    story = project.stories.create :name => issue_title(err), :story_type => 'bug', :description => self.class.pivotal_body_template.result(binding)
    err.update_attribute :issue_link, "https://www.pivotaltracker.com/story/show/#{story.id}"
  end

  def create_lighthouseapp_issue err
    Lighthouse.account = account
    Lighthouse.token = api_token

    # updating lighthouse account
    Lighthouse::Ticket.site

    ticket = Lighthouse::Ticket.new(:project_id => project_id)
    ticket.title = issue_title err

    ticket.body = self.class.lighthouseapp_body_template.result(binding)

    ticket.tags << "errbit"
    ticket.save!
    err.update_attribute :issue_link, "#{Lighthouse::Ticket.site.to_s.sub(/#{Lighthouse::Ticket.site.path}$/, '')}#{Lighthouse::Ticket.element_path(ticket.id, :project_id => project_id)}".sub(/\.xml$/, '')
  end

  def create_fogbugz_issue err
    fogbugz = Fogbugz::Interface.new(:email => username, :password => password, :uri => "https://#{account}.fogbugz.com")
    fogbugz.authenticate

    issue = {}
    issue['sTitle'] = issue_title err
    issue['sArea'] = project_id
    issue['sEvent'] = self.class.fogbugz_body_template.result(binding)
    issue['sTags'] = ['errbit'].join(',')
    issue['cols'] = ['ixBug'].join(',')

    fb_resp = fogbugz.command(:new, issue)
    err.update_attribute :issue_link, "https://#{account}.fogbugz.com/default.asp?#{fb_resp['case']['ixBug']}"
  end

  def create_mingle_issue err
    properties = ticket_properties_hash
    basic_auth = account.gsub(/https?:\/\//, "https://#{username}:#{password}@")
    Mingle.set_site "#{basic_auth}/api/v1/projects/#{project_id}/"

    card = Mingle::Card.new
    card.card_type_name = properties.delete("card_type")
    card.name = issue_title(err)
    card.description = self.class.mingle_body_template.result(binding)
    properties.each do |property, value|
      card.send("cp_#{property}=", value)
    end

    card.save!
    err.update_attribute :issue_link, URI.parse("#{account}/projects/#{project_id}/cards/#{card.id}").to_s
  end

  def issue_title err
    "[#{ err.environment }][#{ err.where }] #{err.message.to_s.truncate(100)}"
  end

  def check_params
    blank_flag_fields = %w(project_id)
    if %w(fogbugz mingle).include?(issue_tracker_type)
      blank_flag_fields += %w(username password)
    else
      blank_flag_fields << 'api_token'
    end
    blank_flag_fields << 'account' if(%w(fogbugz lighthouseapp redmine mingle).include?(issue_tracker_type))
    blank_flags = blank_flag_fields.map {|m| self[m].blank? }

    if issue_tracker_type == "mingle"
      # Check that mingle was given a 'card_type' in the ticket_properties
      blank_flags << "card_type" unless ticket_properties_hash["card_type"]
    end

    if blank_flags.any? && !blank_flags.all?
      message = case issue_tracker_type
      when 'lighthouseapp'
        'You must specify your Lighthouseapp account, API token and Project ID'
      when 'redmine'
        'You must specify your Redmine URL, API token and Project ID'
      when 'pivotal'
        'You must specify your Pivotal Tracker API token and Project ID'
      when 'fogbugz'
        'You must specify your FogBugz Area Name, Username, and Password'
      when 'mingle'
        'You must specify your Mingle URL, Project ID, Card Type (in default card properties), Sign-in name, and Password'
      end
      errors.add(:base, message)
    end
  end

  class << self
    def lighthouseapp_body_template
      @@lighthouseapp_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/lighthouseapp_body.txt.erb").gsub(/^\s*/, ''))
    end

    def redmine_body_template
      @@redmine_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/redmine_body.txt.erb"))
    end

    def pivotal_body_template
      @@pivotal_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/pivotal_body.txt.erb"))
    end

    def fogbugz_body_template
      @@fogbugz_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/fogbugz_body.txt.erb"))
    end

    def mingle_body_template
      # Mingle also uses textile markup, so the redmine template is perfect.
      redmine_body_template
    end
  end
end