Commit e5e6ce94b24155d18551e3299e6808eafa50841f

Authored by Nick Recobra
2 parents a724c092 7a0fdea0
Exists in master and in 1 other branch production

Merge branch 'fogbugz_integration' of https://github.com/narshlob/errbit into pull-request-43

Conflicts:
	app/models/app.rb
@@ -2,7 +2,7 @@ source 'http://rubygems.org' @@ -2,7 +2,7 @@ source 'http://rubygems.org'
2 2
3 gem 'rails', '3.0.5' 3 gem 'rails', '3.0.5'
4 gem 'nokogiri' 4 gem 'nokogiri'
5 -gem 'mongoid', '2.0.0.rc.8' 5 +gem 'mongoid', '2.0.2'
6 gem 'haml' 6 gem 'haml'
7 gem 'will_paginate' 7 gem 'will_paginate'
8 gem 'devise', '~> 1.1.8' 8 gem 'devise', '~> 1.1.8'
@@ -11,14 +11,16 @@ gem 'redmine_client', :git => "git://github.com/oruen/redmine_client.git" @@ -11,14 +11,16 @@ gem 'redmine_client', :git => "git://github.com/oruen/redmine_client.git"
11 gem 'mongoid_rails_migrations' 11 gem 'mongoid_rails_migrations'
12 gem 'useragent', '~> 0.3.1' 12 gem 'useragent', '~> 0.3.1'
13 gem 'pivotal-tracker' 13 gem 'pivotal-tracker'
  14 +gem 'ruby-fogbugz', :require => 'fogbugz'
14 15
15 platform :ruby do 16 platform :ruby do
16 - gem 'bson_ext', '~> 1.2' 17 + gem 'bson_ext', '~> 1.3.1'
17 end 18 end
18 19
19 group :development, :test do 20 group :development, :test do
20 gem 'rspec-rails', '~> 2.5' 21 gem 'rspec-rails', '~> 2.5'
21 gem 'webmock', :require => false 22 gem 'webmock', :require => false
  23 + gem 'ruby-debug19', :require => 'ruby-debug'
22 end 24 end
23 25
24 group :test do 26 group :test do
@@ -36,11 +36,13 @@ GEM @@ -36,11 +36,13 @@ GEM
36 activesupport (= 3.0.5) 36 activesupport (= 3.0.5)
37 activesupport (3.0.5) 37 activesupport (3.0.5)
38 addressable (2.2.5) 38 addressable (2.2.5)
  39 + archive-tar-minitar (0.5.2)
39 arel (2.0.9) 40 arel (2.0.9)
40 bcrypt-ruby (2.1.4) 41 bcrypt-ruby (2.1.4)
41 - bson (1.3.0)  
42 - bson_ext (1.3.0) 42 + bson (1.3.1)
  43 + bson_ext (1.3.1)
43 builder (2.1.2) 44 builder (2.1.2)
  45 + columnize (0.3.4)
44 crack (0.1.8) 46 crack (0.1.8)
45 daemons (1.1.4) 47 daemons (1.1.4)
46 database_cleaner (0.6.7) 48 database_cleaner (0.6.7)
@@ -65,19 +67,20 @@ GEM @@ -65,19 +67,20 @@ GEM
65 lighthouse-api (2.0) 67 lighthouse-api (2.0)
66 activeresource (>= 3.0.0) 68 activeresource (>= 3.0.0)
67 activesupport (>= 3.0.0) 69 activesupport (>= 3.0.0)
  70 + linecache19 (0.5.12)
  71 + ruby_core_source (>= 0.1.4)
68 mail (2.2.17) 72 mail (2.2.17)
69 activesupport (>= 2.3.6) 73 activesupport (>= 2.3.6)
70 i18n (>= 0.4.0) 74 i18n (>= 0.4.0)
71 mime-types (~> 1.16) 75 mime-types (~> 1.16)
72 treetop (~> 1.4.8) 76 treetop (~> 1.4.8)
73 mime-types (1.16) 77 mime-types (1.16)
74 - mongo (1.3.0)  
75 - bson (>= 1.3.0)  
76 - mongoid (2.0.0.rc.8) 78 + mongo (1.3.1)
  79 + bson (>= 1.3.1)
  80 + mongoid (2.0.2)
77 activemodel (~> 3.0) 81 activemodel (~> 3.0)
78 - mongo (~> 1.2) 82 + mongo (~> 1.3)
79 tzinfo (~> 0.3.22) 83 tzinfo (~> 0.3.22)
80 - will_paginate (~> 3.0.pre)  
81 mongoid_rails_migrations (0.0.10) 84 mongoid_rails_migrations (0.0.10)
82 activesupport (~> 3.0.0) 85 activesupport (~> 3.0.0)
83 bundler (>= 0.9.19) 86 bundler (>= 0.9.19)
@@ -124,6 +127,19 @@ GEM @@ -124,6 +127,19 @@ GEM
124 activesupport (~> 3.0) 127 activesupport (~> 3.0)
125 railties (~> 3.0) 128 railties (~> 3.0)
126 rspec (~> 2.5.0) 129 rspec (~> 2.5.0)
  130 + ruby-debug-base19 (0.11.25)
  131 + columnize (>= 0.3.1)
  132 + linecache19 (>= 0.5.11)
  133 + ruby_core_source (>= 0.1.4)
  134 + ruby-debug19 (0.11.6)
  135 + columnize (>= 0.3.1)
  136 + linecache19 (>= 0.5.11)
  137 + ruby-debug-base19 (>= 0.11.19)
  138 + ruby-fogbugz (0.0.4)
  139 + crack
  140 + typhoeus
  141 + ruby_core_source (0.1.5)
  142 + archive-tar-minitar (>= 0.5.2)
127 thin (1.2.11) 143 thin (1.2.11)
128 daemons (>= 1.0.9) 144 daemons (>= 1.0.9)
129 eventmachine (>= 0.12.6) 145 eventmachine (>= 0.12.6)
@@ -131,6 +147,9 @@ GEM @@ -131,6 +147,9 @@ GEM
131 thor (0.14.6) 147 thor (0.14.6)
132 treetop (1.4.9) 148 treetop (1.4.9)
133 polyglot (>= 0.3.1) 149 polyglot (>= 0.3.1)
  150 + typhoeus (0.2.4)
  151 + mime-types
  152 + mime-types
134 tzinfo (0.3.26) 153 tzinfo (0.3.26)
135 useragent (0.3.1) 154 useragent (0.3.1)
136 warden (1.0.3) 155 warden (1.0.3)
@@ -144,14 +163,14 @@ PLATFORMS @@ -144,14 +163,14 @@ PLATFORMS
144 ruby 163 ruby
145 164
146 DEPENDENCIES 165 DEPENDENCIES
147 - bson_ext (~> 1.2) 166 + bson_ext (~> 1.3.1)
148 database_cleaner (~> 0.6.0) 167 database_cleaner (~> 0.6.0)
149 devise (~> 1.1.8) 168 devise (~> 1.1.8)
150 email_spec 169 email_spec
151 factory_girl_rails 170 factory_girl_rails
152 haml 171 haml
153 lighthouse-api 172 lighthouse-api
154 - mongoid (= 2.0.0.rc.8) 173 + mongoid (= 2.0.2)
155 mongoid_rails_migrations 174 mongoid_rails_migrations
156 nokogiri 175 nokogiri
157 pivotal-tracker 176 pivotal-tracker
@@ -159,6 +178,8 @@ DEPENDENCIES @@ -159,6 +178,8 @@ DEPENDENCIES
159 redmine_client! 178 redmine_client!
160 rspec (~> 2.5) 179 rspec (~> 2.5)
161 rspec-rails (~> 2.5) 180 rspec-rails (~> 2.5)
  181 + ruby-debug19
  182 + ruby-fogbugz
162 thin 183 thin
163 useragent (~> 0.3.1) 184 useragent (~> 0.3.1)
164 webmock 185 webmock
app/controllers/apps_controller.rb
@@ -8,9 +8,17 @@ class AppsController < ApplicationController @@ -8,9 +8,17 @@ class AppsController < ApplicationController
8 end 8 end
9 9
10 def show 10 def show
  11 + where_clause = {}
11 respond_to do |format| 12 respond_to do |format|
12 format.html do 13 format.html do
13 - @errs = @app.errs.ordered.paginate(:page => params[:page], :per_page => current_user.per_page) 14 + where_clause[:environment] = params[:environment] if(params[:environment].present?)
  15 + if(params[:all_errs])
  16 + @errs = @app.errs.where(where_clause).ordered.paginate(:page => params[:page], :per_page => current_user.per_page)
  17 + @all_errs = true
  18 + else
  19 + @errs = @app.errs.unresolved.where(where_clause).ordered.paginate(:page => params[:page], :per_page => current_user.per_page)
  20 + @all_errs = false
  21 + end
14 @deploys = @app.deploys.order_by(:created_at.desc).limit(5) 22 @deploys = @app.deploys.order_by(:created_at.desc).limit(5)
15 end 23 end
16 format.atom do 24 format.atom do
app/controllers/errs_controller.rb
@@ -5,12 +5,14 @@ class ErrsController < ApplicationController @@ -5,12 +5,14 @@ class ErrsController < ApplicationController
5 5
6 def index 6 def index
7 app_scope = current_user.admin? ? App.all : current_user.apps 7 app_scope = current_user.admin? ? App.all : current_user.apps
  8 + where_clause = {}
  9 + where_clause[:environment] = params[:environment] if(params[:environment].present?)
8 respond_to do |format| 10 respond_to do |format|
9 format.html do 11 format.html do
10 - @errs = Err.for_apps(app_scope).unresolved.ordered.paginate(:page => params[:page], :per_page => current_user.per_page) 12 + @errs = Err.for_apps(app_scope).where(where_clause).unresolved.ordered.paginate(:page => params[:page], :per_page => current_user.per_page)
11 end 13 end
12 format.atom do 14 format.atom do
13 - @errs = Err.for_apps(app_scope).unresolved.ordered 15 + @errs = Err.for_apps(app_scope).where(where_clause).unresolved.ordered
14 end 16 end
15 end 17 end
16 end 18 end
@@ -42,7 +44,7 @@ class ErrsController < ApplicationController @@ -42,7 +44,7 @@ class ErrsController < ApplicationController
42 redirect_to app_err_path(@app, @err) 44 redirect_to app_err_path(@app, @err)
43 end 45 end
44 46
45 - def clear_issue 47 + def unlink_issue
46 @err.update_attribute :issue_link, nil 48 @err.update_attribute :issue_link, nil
47 redirect_to app_err_path(@app, @err) 49 redirect_to app_err_path(@app, @err)
48 end 50 end
app/controllers/users_controller.rb
@@ -6,7 +6,7 @@ class UsersController < ApplicationController @@ -6,7 +6,7 @@ class UsersController < ApplicationController
6 before_filter :require_user_edit_priviledges, :only => [:edit, :update] 6 before_filter :require_user_edit_priviledges, :only => [:edit, :update]
7 7
8 def index 8 def index
9 - @users = User.paginate(:page => params[:page], :per_page => current_user.per_page) 9 + @users = User.all.paginate(:page => params[:page], :per_page => current_user.per_page)
10 end 10 end
11 11
12 def show 12 def show
app/helpers/application_helper.rb
@@ -42,4 +42,8 @@ module ApplicationHelper @@ -42,4 +42,8 @@ module ApplicationHelper
42 def pivotal_tracker? object 42 def pivotal_tracker? object
43 object.issue_tracker_type == "pivotal" 43 object.issue_tracker_type == "pivotal"
44 end 44 end
  45 +
  46 + def fogbugz_tracker? object
  47 + object.issue_tracker_type == 'fogbugz'
  48 + end
45 end 49 end
app/models/app.rb
@@ -21,11 +21,11 @@ class App @@ -21,11 +21,11 @@ class App
21 embeds_many :watchers 21 embeds_many :watchers
22 embeds_many :deploys 22 embeds_many :deploys
23 embeds_one :issue_tracker 23 embeds_one :issue_tracker
24 - references_many :errs, :dependent => :destroy 24 + has_many :errs, :inverse_of => :app, :dependent => :destroy
25 25
26 before_validation :generate_api_key, :on => :create 26 before_validation :generate_api_key, :on => :create
27 before_save :normalize_github_url 27 before_save :normalize_github_url
28 - 28 +
29 validates_presence_of :name, :api_key 29 validates_presence_of :name, :api_key
30 validates_uniqueness_of :name, :allow_blank => true 30 validates_uniqueness_of :name, :allow_blank => true
31 validates_uniqueness_of :api_key, :allow_blank => true 31 validates_uniqueness_of :api_key, :allow_blank => true
@@ -35,7 +35,7 @@ class App @@ -35,7 +35,7 @@ class App
35 accepts_nested_attributes_for :watchers, :allow_destroy => true, 35 accepts_nested_attributes_for :watchers, :allow_destroy => true,
36 :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? } 36 :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? }
37 accepts_nested_attributes_for :issue_tracker, :allow_destroy => true, 37 accepts_nested_attributes_for :issue_tracker, :allow_destroy => true,
38 - :reject_if => proc { |attrs| !%w(lighthouseapp redmine pivotal).include?(attrs[:issue_tracker_type]) } 38 + :reject_if => proc { |attrs| !%w(lighthouseapp redmine pivotal fogbugz).include?(attrs[:issue_tracker_type]) }
39 39
40 # Mongoid Bug: find(id) on association proxies returns an Enumerator 40 # Mongoid Bug: find(id) on association proxies returns an Enumerator
41 def self.find_by_id!(app_id) 41 def self.find_by_id!(app_id)
@@ -60,18 +60,19 @@ class App @@ -60,18 +60,19 @@ class App
60 !(self[:notify_on_deploys] == false) 60 !(self[:notify_on_deploys] == false)
61 end 61 end
62 alias :notify_on_deploys? :notify_on_deploys 62 alias :notify_on_deploys? :notify_on_deploys
63 - 63 +
64 def github_url? 64 def github_url?
65 self.github_url.present? 65 self.github_url.present?
66 end 66 end
67 - 67 +
68 def github_url_to_file(file) 68 def github_url_to_file(file)
69 "#{self.github_url}/blob/master#{file}" 69 "#{self.github_url}/blob/master#{file}"
70 end 70 end
71 - 71 +
72 def issue_tracker_configured? 72 def issue_tracker_configured?
73 issue_tracker && !issue_tracker.project_id.blank? 73 issue_tracker && !issue_tracker.project_id.blank?
74 end 74 end
  75 +
75 protected 76 protected
76 77
77 def generate_api_key 78 def generate_api_key
@@ -86,12 +87,12 @@ class App @@ -86,12 +87,12 @@ class App
86 end if issue_tracker.errors 87 end if issue_tracker.errors
87 end 88 end
88 end 89 end
89 - 90 +
90 def normalize_github_url 91 def normalize_github_url
91 return if self.github_url.blank? 92 return if self.github_url.blank?
92 self.github_url.gsub!(%r{^http://|git@}, 'https://') 93 self.github_url.gsub!(%r{^http://|git@}, 'https://')
93 self.github_url.gsub!(/github\.com:/, 'github.com/') 94 self.github_url.gsub!(/github\.com:/, 'github.com/')
94 self.github_url.gsub!(/\.git$/, '') 95 self.github_url.gsub!(/\.git$/, '')
95 end 96 end
96 - 97 +
97 end 98 end
app/models/err.rb
@@ -16,8 +16,8 @@ class Err @@ -16,8 +16,8 @@ class Err
16 index :last_notice_at 16 index :last_notice_at
17 index :app_id 17 index :app_id
18 18
19 - referenced_in :app  
20 - references_many :notices 19 + belongs_to :app
  20 + has_many :notices
21 21
22 validates_presence_of :klass, :environment 22 validates_presence_of :klass, :environment
23 23
app/models/issue_tracker.rb
@@ -12,6 +12,8 @@ class IssueTracker @@ -12,6 +12,8 @@ class IssueTracker
12 field :account, :type => String 12 field :account, :type => String
13 field :api_token, :type => String 13 field :api_token, :type => String
14 field :project_id, :type => String 14 field :project_id, :type => String
  15 + field :username, :type => String
  16 + field :password, :type => String
15 field :issue_tracker_type, :type => String, :default => 'lighthouseapp' 17 field :issue_tracker_type, :type => String, :default => 'lighthouseapp'
16 18
17 def create_issue err 19 def create_issue err
@@ -22,6 +24,8 @@ class IssueTracker @@ -22,6 +24,8 @@ class IssueTracker
22 create_redmine_issue err 24 create_redmine_issue err
23 when 'pivotal' 25 when 'pivotal'
24 create_pivotal_issue err 26 create_pivotal_issue err
  27 + when 'fogbugz'
  28 + create_fogbugz_issue err
25 end 29 end
26 end 30 end
27 31
@@ -65,22 +69,44 @@ class IssueTracker @@ -65,22 +69,44 @@ class IssueTracker
65 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$/, '') 69 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$/, '')
66 end 70 end
67 71
  72 + def create_fogbugz_issue err
  73 + fogbugz = Fogbugz::Interface.new(:email => username, :password => password, :uri => "https://#{account}.fogbugz.com")
  74 + fogbugz.authenticate
  75 +
  76 + issue = {}
  77 + issue['sTitle'] = issue_title err
  78 + issue['sArea'] = project_id
  79 + issue['sEvent'] = self.class.fogbugz_body_template.result(binding)
  80 + issue['sTags'] = ['errbit'].join(',')
  81 + issue['cols'] = ['ixBug'].join(',')
  82 +
  83 + fb_resp = fogbugz.command(:new, issue)
  84 + err.update_attribute :issue_link, "https://#{account}.fogbugz.com/default.asp?#{fb_resp['case']['ixBug']}"
  85 + end
  86 +
68 def issue_title err 87 def issue_title err
69 "[#{ err.environment }][#{ err.where }] #{err.message.to_s.truncate(100)}" 88 "[#{ err.environment }][#{ err.where }] #{err.message.to_s.truncate(100)}"
70 end 89 end
71 90
72 def check_params 91 def check_params
73 - blank_flag_fields = %w(api_token project_id)  
74 - blank_flag_fields << 'account' if %w(lighthouseapp redmine).include? issue_tracker_type 92 + blank_flag_fields = %w(project_id)
  93 + if(%w(fogbugz).include?(issue_tracker_type))
  94 + blank_flag_fields += %w(username password)
  95 + else
  96 + blank_flag_fields << 'api_token'
  97 + end
  98 + blank_flag_fields << 'account' if(%w(fogbugz lighthouseapp redmine).include?(issue_tracker_type))
75 blank_flags = blank_flag_fields.map {|m| self[m].blank? } 99 blank_flags = blank_flag_fields.map {|m| self[m].blank? }
76 if blank_flags.any? && !blank_flags.all? 100 if blank_flags.any? && !blank_flags.all?
77 message = case issue_tracker_type 101 message = case issue_tracker_type
78 when 'lighthouseapp' 102 when 'lighthouseapp'
79 - "You must specify your Lighthouseapp account, api token and project id" 103 + 'You must specify your Lighthouseapp account, api token and project id'
80 when 'redmine' 104 when 'redmine'
81 - "You must specify your Redmine url, api token and project id" 105 + 'You must specify your Redmine url, api token and project id'
82 when 'pivotal' 106 when 'pivotal'
83 - "You must specify your Pivotal Tracker api token and project id" 107 + 'You must specify your Pivotal Tracker api token and project id'
  108 + when 'fogbugz'
  109 + 'You must specify your FogBugz Area Name, Username, and Password'
84 end 110 end
85 errors.add(:base, message) 111 errors.add(:base, message)
86 end 112 end
@@ -98,5 +124,9 @@ class IssueTracker @@ -98,5 +124,9 @@ class IssueTracker
98 def pivotal_body_template 124 def pivotal_body_template
99 @@pivotal_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/pivotal_body.txt.erb")) 125 @@pivotal_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/pivotal_body.txt.erb"))
100 end 126 end
  127 +
  128 + def fogbugz_body_template
  129 + @@fogbugz_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/fogbugz_body.txt.erb"))
  130 + end
101 end 131 end
102 end 132 end
app/models/notice.rb
@@ -11,7 +11,7 @@ class Notice @@ -11,7 +11,7 @@ class Notice
11 field :request, :type => Hash 11 field :request, :type => Hash
12 field :notifier, :type => Hash 12 field :notifier, :type => Hash
13 13
14 - referenced_in :err 14 + belongs_to :err
15 index :err_id 15 index :err_id
16 16
17 after_create :cache_last_notice_at 17 after_create :cache_last_notice_at
app/views/apps/_fields.html.haml
@@ -45,13 +45,15 @@ @@ -45,13 +45,15 @@
45 = label_tag :issue_tracker_type_redmine, 'Redmine', :for => label_for_attr(w, 'issue_tracker_type_redmine') 45 = label_tag :issue_tracker_type_redmine, 'Redmine', :for => label_for_attr(w, 'issue_tracker_type_redmine')
46 = w.radio_button :issue_tracker_type, :pivotal 46 = w.radio_button :issue_tracker_type, :pivotal
47 = label_tag :issue_tracker_type_pivotal, 'Pivotal Tracker', :for => label_for_attr(w, 'issue_tracker_type_pivotal') 47 = label_tag :issue_tracker_type_pivotal, 'Pivotal Tracker', :for => label_for_attr(w, 'issue_tracker_type_pivotal')
  48 + = w.radio_button :issue_tracker_type, :fogbugz
  49 + = label_tag :issue_tracker_type_fogbugz, 'FogBugz', :for => label_for_attr(w, 'issue_tracker_type_fogbugz')
48 %div.tracker_params.lighthouseapp{:class => lighthouse_tracker?(w.object) ? 'chosen' : nil} 50 %div.tracker_params.lighthouseapp{:class => lighthouse_tracker?(w.object) ? 'chosen' : nil}
49 = w.label :account, "Account" 51 = w.label :account, "Account"
50 = w.text_field :account, :placeholder => "abc from abc.lighthouseapp.com" 52 = w.text_field :account, :placeholder => "abc from abc.lighthouseapp.com"
51 = w.label :api_token, "API token" 53 = w.label :api_token, "API token"
52 = w.text_field :api_token, :placeholder => "API Token for your account" 54 = w.text_field :api_token, :placeholder => "API Token for your account"
53 = w.label :project_id, "Project ID" 55 = w.label :project_id, "Project ID"
54 - = w.text_field :project_id, :placeholder => "123 from abc from abc.lighthouseapp.com/projects/123" 56 + = w.text_field :project_id, :placeholder => "123 from abc.lighthouseapp.com/projects/123"
55 %div.tracker_params.redmine{:class => redmine_tracker?(w.object) ? 'chosen' : nil} 57 %div.tracker_params.redmine{:class => redmine_tracker?(w.object) ? 'chosen' : nil}
56 = w.label :account, "Redmine URL" 58 = w.label :account, "Redmine URL"
57 = w.text_field :account, :placeholder => "like http://www.redmine.org/" 59 = w.text_field :account, :placeholder => "like http://www.redmine.org/"
@@ -64,3 +66,12 @@ @@ -64,3 +66,12 @@
64 = w.text_field :project_id 66 = w.text_field :project_id
65 = w.label :api_token, "API token" 67 = w.label :api_token, "API token"
66 = w.text_field :api_token, :placeholder => "API Token for your account" 68 = w.text_field :api_token, :placeholder => "API Token for your account"
  69 + %div.tracker_params.fogbugz{:class => fogbugz_tracker?(w.object) ? 'chosen' : nil}
  70 + = w.label :project_id, "Area Name"
  71 + = w.text_field :project_id
  72 + = w.label :account, "FogBugz URL"
  73 + = w.text_field :account, :placeholder => "abc from http://abc.fogbugz.com/"
  74 + = w.label :username, 'account username'
  75 + = w.text_field :username, :placeholder => 'Username/Email for your account'
  76 + = w.label :password, 'account password'
  77 + = w.password_field :password, :placeholder => 'Password for your account'
app/views/apps/show.html.haml
1 - content_for :title, @app.name 1 - content_for :title, @app.name
2 - content_for :head do 2 - content_for :head do
3 = auto_discovery_link_tag :atom, app_url(@app, User.token_authentication_key => current_user.authentication_token, :format => "atom"), :title => "Errbit notices for #{@app.name} at #{root_url}" 3 = auto_discovery_link_tag :atom, app_url(@app, User.token_authentication_key => current_user.authentication_token, :format => "atom"), :title => "Errbit notices for #{@app.name} at #{root_url}"
  4 + = javascript_include_tag 'apps.show'
4 - content_for :meta do 5 - content_for :meta do
5 %strong Errs Caught: 6 %strong Errs Caught:
6 = @app.errs.count 7 = @app.errs.count
@@ -12,53 +13,66 @@ @@ -12,53 +13,66 @@
12 - if current_user.admin? 13 - if current_user.admin?
13 = link_to 'edit', edit_app_path(@app), :class => 'button' 14 = link_to 'edit', edit_app_path(@app), :class => 'button'
14 = link_to 'destroy', app_path(@app), :method => :delete, :confirm => 'Seriously?', :class => 'button' 15 = link_to 'destroy', app_path(@app), :method => :delete, :confirm => 'Seriously?', :class => 'button'
  16 + - if @all_errs == true
  17 + = link_to 'unresolved errs', app_path(@app), :class => 'button'
  18 + - else
  19 + = link_to 'all errs', app_path(@app, {all_errs: true}), :class => 'button'
15 20
16 -%h3 Watchers  
17 -%table.watchers  
18 - %thead  
19 - %tr  
20 - %th User or Email  
21 - %tbody  
22 - - @app.watchers.each do |watcher|  
23 - %tr  
24 - %td= watcher.label  
25 - - if @app.watchers.none?  
26 - %tr  
27 - %td  
28 - %em Sadly, no one is watching this app  
29 -  
30 -- if @app.github_url?  
31 - %h3 Repository  
32 - %table.repository 21 +%h3{:id => 'watchers_toggle'}
  22 + Watchers
  23 + %span{:class => 'click_span'} (show/hide)
  24 +#watchers_div
  25 + %table.watchers
33 %thead 26 %thead
34 %tr 27 %tr
35 - %th GitHub 28 + %th User or Email
36 %tbody 29 %tbody
37 - %tr  
38 - %td= link_to(@app.github_url, @app.github_url, :target => '_blank') 30 + - @app.watchers.each do |watcher|
  31 + %tr
  32 + %td= watcher.label
  33 + - if @app.watchers.none?
  34 + %tr
  35 + %td
  36 + %em Sadly, no one is watching this app
39 37
40 -%h3 Latest Deploys  
41 -- if @deploys.any?  
42 - %table.deploys  
43 - %thead  
44 - %tr  
45 - %th When  
46 - %th Who  
47 - %th Message  
48 - %th Repository  
49 - %th Revision 38 +- if @app.github_url?
  39 + %h3{:id => 'repository_toggle'}
  40 + Repository
  41 + %span{:class => 'click_span'} (show/hide)
  42 + #repository_div
  43 + %table.repository
  44 + %thead
  45 + %tr
  46 + %th GitHub
  47 + %tbody
  48 + %tr
  49 + %td= link_to(@app.github_url, @app.github_url, :target => '_blank')
50 50
51 - %tbody  
52 - - @deploys.each do |deploy| 51 +%h3{:id => 'deploys_toggle'}
  52 + Latest Deploys
  53 + %span{:class => 'click_span'} (show/hide)
  54 +#deploys_div
  55 + - if @deploys.any?
  56 + %table.deploys
  57 + %thead
53 %tr 58 %tr
54 - %td.when #{deploy.created_at.to_s(:micro)}  
55 - %td.who #{deploy.username}  
56 - %td.message #{deploy.message}  
57 - %td.repository #{deploy.repository}  
58 - %td.revision #{deploy.revision}  
59 - = link_to "All Deploys (#{@app.deploys.count})", app_deploys_path(@app), :class => 'button'  
60 -- else  
61 - %h3 No deploys 59 + %th When
  60 + %th Who
  61 + %th Message
  62 + %th Repository
  63 + %th Revision
  64 +
  65 + %tbody
  66 + - @deploys.each do |deploy|
  67 + %tr
  68 + %td.when #{deploy.created_at.to_s(:micro)}
  69 + %td.who #{deploy.username}
  70 + %td.message #{deploy.message}
  71 + %td.repository #{deploy.repository}
  72 + %td.revision #{deploy.revision}
  73 + = link_to "All Deploys (#{@app.deploys.count})", app_deploys_path(@app), :class => 'button'
  74 + - else
  75 + %h3 No deploys
62 76
63 - if @app.errs.count > 0 77 - if @app.errs.count > 0
64 %h3.clear Errs 78 %h3.clear Errs
app/views/errs/_table.html.haml
@@ -12,7 +12,10 @@ @@ -12,7 +12,10 @@
12 %tr{:class => err.resolved? ? 'resolved' : 'unresolved'} 12 %tr{:class => err.resolved? ? 'resolved' : 'unresolved'}
13 %td.app 13 %td.app
14 = link_to err.app.name, app_path(err.app) 14 = link_to err.app.name, app_path(err.app)
15 - %span.environment= err.environment 15 + - if(current_page?(:controller => 'errs'))
  16 + %span.environment= link_to err.environment, errs_path(environment: err.environment)
  17 + - else
  18 + %span.environment= link_to err.environment, app_path(environment: err.environment)
16 %td.message 19 %td.message
17 = link_to err.message, app_err_path(err.app, err) 20 = link_to err.message, app_err_path(err.app, err)
18 %em= err.where 21 %em= err.where
app/views/errs/fogbugz_body.txt.erb 0 → 100644
@@ -0,0 +1,31 @@ @@ -0,0 +1,31 @@
  1 +"See this exception on Errbit": <%= app_err_url(err.app, err) %>
  2 +<% if notice = err.notices.first %>
  3 + <%= notice.message %>
  4 +
  5 + Summary
  6 + - Where
  7 + <%= notice.err.where %>
  8 +
  9 + - Occured
  10 + <%= notice.created_at.to_s(:micro) %>
  11 +
  12 + - Similar
  13 + <%= (notice.err.notices_count - 1).to_s %>
  14 +
  15 + Params
  16 + <%= pretty_hash(notice.params) %>
  17 +
  18 + Session
  19 + <%= pretty_hash(notice.session) %>
  20 +
  21 + Backtrace
  22 + <% for line in notice.backtrace %>
  23 + <%= line['number'] %>: <%= line['file'].sub(/^\[PROJECT_ROOT\]/, '') %>
  24 + <% end %>
  25 +
  26 + Environment
  27 + <% for key, val in notice.env_vars %>
  28 + <%= key %>: <%= val %>
  29 + <% end %>
  30 +<% end %>
  31 +
app/views/errs/show.html.haml
@@ -5,6 +5,7 @@ @@ -5,6 +5,7 @@
5 = @app.name 5 = @app.name
6 %strong Where: 6 %strong Where:
7 = @err.where 7 = @err.where
  8 + %br
8 %strong Environment: 9 %strong Environment:
9 = @err.environment 10 = @err.environment
10 %strong Last Notice: 11 %strong Last Notice:
@@ -15,7 +16,7 @@ @@ -15,7 +16,7 @@
15 %span= link_to 'create issue', create_issue_app_err_path(@app, @err), :method => :post, :class => "#{@app.issue_tracker.issue_tracker_type}_create create-issue" 16 %span= link_to 'create issue', create_issue_app_err_path(@app, @err), :method => :post, :class => "#{@app.issue_tracker.issue_tracker_type}_create create-issue"
16 - else 17 - else
17 %span= link_to 'go to issue', @err.issue_link, :class => "#{@app.issue_tracker.issue_tracker_type}_goto goto-issue" 18 %span= link_to 'go to issue', @err.issue_link, :class => "#{@app.issue_tracker.issue_tracker_type}_goto goto-issue"
18 - = link_to 'clear issue', clear_issue_app_err_path(@app, @err), :method => :delete, :confirm => "Clear err issues?", :class => "clear-issue" 19 + = link_to 'unlink issue', unlink_issue_app_err_path(@app, @err), :method => :delete, :confirm => "Unlink err issues?", :class => "unlink-issue"
19 - if @err.unresolved? 20 - if @err.unresolved?
20 %span= link_to 'resolve', resolve_app_err_path(@app, @err), :method => :put, :confirm => err_confirm, :class => 'resolve' 21 %span= link_to 'resolve', resolve_app_err_path(@app, @err), :method => :put, :confirm => err_confirm, :class => 'resolve'
21 22
app/views/notices/_params.html.haml
1 .window 1 .window
2 - %pre.hash= pretty_hash notice.params  
3 -  
4 \ No newline at end of file 2 \ No newline at end of file
  3 + .raw_data
  4 + %pre.hash= pretty_hash notice.params
  5 +
app/views/notices/_session.html.haml
1 .window 1 .window
2 - %pre.hash= pretty_hash notice.session  
3 \ No newline at end of file 2 \ No newline at end of file
  3 + .raw_data
  4 + %pre.hash= pretty_hash notice.session
config/initializers/overrides.rb 0 → 100644
@@ -0,0 +1 @@ @@ -0,0 +1 @@
  1 +require Rails.root.join('lib/overrides/mongoid/relations/builder.rb')
config/routes.rb
1 Errbit::Application.routes.draw do 1 Errbit::Application.routes.draw do
2 - 2 +
3 devise_for :users 3 devise_for :users
4 4
5 # Hoptoad Notifier Routes 5 # Hoptoad Notifier Routes
6 match '/notifier_api/v2/notices' => 'notices#create' 6 match '/notifier_api/v2/notices' => 'notices#create'
7 match '/deploys.txt' => 'deploys#create' 7 match '/deploys.txt' => 'deploys#create'
8 - 8 +
9 resources :notices, :only => [:show] 9 resources :notices, :only => [:show]
10 resources :deploys, :only => [:show] 10 resources :deploys, :only => [:show]
11 resources :users 11 resources :users
@@ -14,22 +14,22 @@ Errbit::Application.routes.draw do @@ -14,22 +14,22 @@ Errbit::Application.routes.draw do
14 get :all 14 get :all
15 end 15 end
16 end 16 end
17 - 17 +
18 resources :apps do 18 resources :apps do
19 resources :errs do 19 resources :errs do
20 resources :notices 20 resources :notices
21 member do 21 member do
22 put :resolve 22 put :resolve
23 post :create_issue 23 post :create_issue
24 - delete :clear_issue 24 + delete :unlink_issue
25 end 25 end
26 end 26 end
27 27
28 resources :deploys, :only => [:index] 28 resources :deploys, :only => [:index]
29 end 29 end
30 - 30 +
31 devise_for :users 31 devise_for :users
32 - 32 +
33 root :to => 'apps#index' 33 root :to => 'apps#index'
34 - 34 +
35 end 35 end
lib/overrides/mongoid/relations/builder.rb 0 → 100644
@@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
  1 +# ruby-fogbugz requires crack. crack adds the attributes method to strings,
  2 +# thus breaking the relations of Mongoid.
  3 +# Tests reside in a separate fork of mongoid:
  4 +# https://github.com/mhs/mongoid/commit/e5b2b1346c73a2935c606317314b6ded07260429#diff-1
  5 +module Mongoid
  6 + module Relations
  7 + class Builder
  8 + def query?
  9 + return true unless object.respond_to?(:to_a)
  10 + obj = object.to_a.first
  11 + !obj.is_a?(Mongoid::Document) && !obj.nil?
  12 + end
  13 + end
  14 + end
  15 +end
public/images/fogbugz_create.png 0 → 100644

4.78 KB

public/images/fogbugz_goto.png 0 → 100644

4.78 KB

public/javascripts/apps.show.js 0 → 100644
@@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
  1 +$(function() {
  2 + $("#watchers_toggle").click(function() {
  3 + $("#watchers_div").slideToggle("slow");
  4 + });
  5 + $("#repository_toggle").click(function() {
  6 + $("#repository_div").slideToggle("slow");
  7 + });
  8 + $("#deploys_toggle").click(function() {
  9 + $("#deploys_div").slideToggle("slow");
  10 + });
  11 +});
public/stylesheets/application.css
@@ -376,13 +376,18 @@ table .main { width: 100%; } @@ -376,13 +376,18 @@ table .main { width: 100%; }
376 table.single_user { 376 table.single_user {
377 border-top: none; 377 border-top: none;
378 } 378 }
  379 +
  380 +.raw_data {
  381 + width: 100%;
  382 + color: #f0f0f0;
  383 + background-color: #222;
  384 + overflow: auto;
  385 +}
  386 +
379 /* Code */ 387 /* Code */
380 pre { 388 pre {
381 padding: 0.8em; 389 padding: 0.8em;
382 margin-bottom: 1em; 390 margin-bottom: 1em;
383 - color: #f0f0f0;  
384 - background-color: #222;  
385 - border: 1px solid #444;  
386 font-family: monaco, courier, monospace; 391 font-family: monaco, courier, monospace;
387 font-size: 1.1em; 392 font-size: 1.1em;
388 } 393 }
@@ -502,7 +507,7 @@ a.button.active { @@ -502,7 +507,7 @@ a.button.active {
502 } 507 }
503 508
504 /* Watchers and Issue Tracker Forms */ 509 /* Watchers and Issue Tracker Forms */
505 -div.nested.watcher .user, div.nested.watcher .email, div.issue_tracker.nested .lighthouseapp, div.issue_tracker.nested .redmine, div.issue_tracker.nested .pivotal { 510 +div.nested.watcher .user, div.nested.watcher .email, div.issue_tracker.nested .lighthouseapp, div.issue_tracker.nested .redmine, div.issue_tracker.nested .pivotal, div.issue_tracker.nested .fogbugz {
506 display: none; 511 display: none;
507 } 512 }
508 div.nested.watcher .choosen, div.nested.issue_tracker .chosen { 513 div.nested.watcher .choosen, div.nested.issue_tracker .chosen {
@@ -612,6 +617,10 @@ table.tally th.value { @@ -612,6 +617,10 @@ table.tally th.value {
612 background: transparent url(/images/pivotal_create.png) 6px 5px no-repeat; 617 background: transparent url(/images/pivotal_create.png) 6px 5px no-repeat;
613 } 618 }
614 619
  620 +#action-bar a.fogbugz_create {
  621 + background: transparent url(/images/fogbugz_create.png) 6px 5px no-repeat;
  622 +}
  623 +
615 #action-bar a.lighthouseapp_goto { 624 #action-bar a.lighthouseapp_goto {
616 background: transparent url(/images/lighthouseapp_goto.png) 6px 5px no-repeat; 625 background: transparent url(/images/lighthouseapp_goto.png) 6px 5px no-repeat;
617 } 626 }
@@ -624,6 +633,10 @@ table.tally th.value { @@ -624,6 +633,10 @@ table.tally th.value {
624 background: transparent url(/images/pivotal_goto.png) 6px 5px no-repeat; 633 background: transparent url(/images/pivotal_goto.png) 6px 5px no-repeat;
625 } 634 }
626 635
  636 +#action-bar a.fogbugz_goto {
  637 + background: transparent url(/images/fogbugz_goto.png) 6px 5px no-repeat;
  638 +}
  639 +
627 /* Notices Pagination */ 640 /* Notices Pagination */
628 .notice-pagination { 641 .notice-pagination {
629 float: left; 642 float: left;
@@ -636,9 +649,11 @@ table.tally th.value { @@ -636,9 +649,11 @@ table.tally th.value {
636 margin-bottom: 1em; 649 margin-bottom: 1em;
637 overflow: auto; 650 overflow: auto;
638 } 651 }
  652 +
639 .window table { 653 .window table {
640 margin: 0; 654 margin: 0;
641 } 655 }
  656 +
642 table.backtrace td { 657 table.backtrace td {
643 width: 100%; 658 width: 100%;
644 padding: 0; 659 padding: 0;
@@ -660,3 +675,11 @@ table.backtrace li.in-app { @@ -660,3 +675,11 @@ table.backtrace li.in-app {
660 color: #2adb2e; 675 color: #2adb2e;
661 background-color: #2f2f2f; 676 background-color: #2f2f2f;
662 } 677 }
  678 +
  679 +span.click_span {
  680 + font-size: 0.7em;
  681 +}
  682 +
  683 +#deploys_div, #repository_div, #watchers_div {
  684 + display: none;
  685 +}
spec/controllers/apps_controller_spec.rb
@@ -74,6 +74,71 @@ describe AppsController do @@ -74,6 +74,71 @@ describe AppsController do
74 assigns(:errs).size.should == 10 74 assigns(:errs).size.should == 10
75 end 75 end
76 end 76 end
  77 +
  78 + context 'with resolved errors' do
  79 + before(:each) do
  80 + resolved_err = Factory.create(:err, app: @app, resolved: true)
  81 + Factory.create(:notice, err: resolved_err)
  82 + end
  83 +
  84 + context 'and no params' do
  85 + it 'shows only unresolved errs' do
  86 + get :show, id: @app.id
  87 + assigns(:errs).size.should == 1
  88 + end
  89 + end
  90 +
  91 + context 'and all_errs=true params' do
  92 + it 'shows all errors' do
  93 + get :show, id: @app.id, all_errs: true
  94 + assigns(:errs).size.should == 2
  95 + end
  96 + end
  97 + end
  98 +
  99 + context 'with environment filters' do
  100 + before(:each) do
  101 + environments = ['production', 'test', 'development', 'staging']
  102 + 20.times do |i|
  103 + Factory.create(:err, app: @app, environment: environments[i % environments.length])
  104 + end
  105 + end
  106 +
  107 + context 'no params' do
  108 + it 'shows errs for all environments' do
  109 + get :show, id: @app.id
  110 + assigns(:errs).size.should == 21
  111 + end
  112 + end
  113 +
  114 + context 'environment production' do
  115 + it 'shows errs for just production' do
  116 + get :show, id: @app.id, environment: :production
  117 + assigns(:errs).size.should == 6
  118 + end
  119 + end
  120 +
  121 + context 'environment staging' do
  122 + it 'shows errs for just staging' do
  123 + get :show, id: @app.id, environment: :staging
  124 + assigns(:errs).size.should == 5
  125 + end
  126 + end
  127 +
  128 + context 'environment development' do
  129 + it 'shows errs for just development' do
  130 + get :show, id: @app.id, environment: :development
  131 + assigns(:errs).size.should == 5
  132 + end
  133 + end
  134 +
  135 + context 'environment test' do
  136 + it 'shows errs for just test' do
  137 + get :show, id: @app.id, environment: :test
  138 + assigns(:errs).size.should == 5
  139 + end
  140 + end
  141 + end
77 end 142 end
78 143
79 context 'logged in as a user' do 144 context 'logged in as a user' do
@@ -261,6 +326,34 @@ describe AppsController do @@ -261,6 +326,34 @@ describe AppsController do
261 response.body.should match(/You must specify your Pivotal Tracker api token and project id/) 326 response.body.should match(/You must specify your Pivotal Tracker api token and project id/)
262 end 327 end
263 end 328 end
  329 +
  330 + context "fogbugz" do
  331 + context 'with correct params' do
  332 + before do
  333 + put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
  334 + :issue_tracker_type => 'fogbugz', :account => 'abc', :project_id => 'Service - Peon', :username => '1234', :password => '123123' } }
  335 + @app.reload
  336 + end
  337 +
  338 + subject {@app.issue_tracker}
  339 + its(:issue_tracker_type) {should == 'fogbugz'}
  340 + its(:account) {should == 'abc'}
  341 + its(:project_id) {should == 'Service - Peon'}
  342 + its(:username) {should == '1234'}
  343 + its(:password) {should == '123123'}
  344 + end
  345 +
  346 + context 'insufficient params' do
  347 + it 'shows validation notice' do
  348 + put :update, :id => @app.id, :app => { :issue_tracker_attributes => {
  349 + :issue_tracker_type => 'fogbugz', :project_id => '1234' } }
  350 + @app.reload
  351 +
  352 + @app.issue_tracker.should be_nil
  353 + response.body.should match(/You must specify your FogBugz Area Name, Username, and Password/)
  354 + end
  355 + end
  356 + end
264 end 357 end
265 end 358 end
266 359
spec/controllers/errs_controller_spec.rb
@@ -54,6 +54,50 @@ describe ErrsController do @@ -54,6 +54,50 @@ describe ErrsController do
54 assigns(:errs).size.should == 10 54 assigns(:errs).size.should == 10
55 end 55 end
56 end 56 end
  57 +
  58 + context 'with environment filters' do
  59 + before(:each) do
  60 + environments = ['production', 'test', 'development', 'staging']
  61 + 20.times do |i|
  62 + Factory.create(:err, environment: environments[i % environments.length])
  63 + end
  64 + end
  65 +
  66 + context 'no params' do
  67 + it 'shows errs for all environments' do
  68 + get :index
  69 + assigns(:errs).size.should == 21
  70 + end
  71 + end
  72 +
  73 + context 'environment production' do
  74 + it 'shows errs for just production' do
  75 + get :index, environment: :production
  76 + assigns(:errs).size.should == 6
  77 + end
  78 + end
  79 +
  80 + context 'environment staging' do
  81 + it 'shows errs for just staging' do
  82 + get :index, environment: :staging
  83 + assigns(:errs).size.should == 5
  84 + end
  85 + end
  86 +
  87 + context 'environment development' do
  88 + it 'shows errs for just development' do
  89 + get :index, environment: :development
  90 + assigns(:errs).size.should == 5
  91 + end
  92 + end
  93 +
  94 + context 'environment test' do
  95 + it 'shows errs for just test' do
  96 + get :index, environment: :test
  97 + assigns(:errs).size.should == 5
  98 + end
  99 + end
  100 + end
57 end 101 end
58 102
59 context 'when logged in as a user' do 103 context 'when logged in as a user' do
@@ -358,7 +402,7 @@ describe ErrsController do @@ -358,7 +402,7 @@ describe ErrsController do
358 end 402 end
359 end 403 end
360 404
361 - describe "DELETE /apps/:app_id/errs/:id/clear_issue" do 405 + describe "DELETE /apps/:app_id/errs/:id/unlink_issue" do
362 before(:each) do 406 before(:each) do
363 sign_in Factory(:admin) 407 sign_in Factory(:admin)
364 end 408 end
@@ -367,7 +411,7 @@ describe ErrsController do @@ -367,7 +411,7 @@ describe ErrsController do
367 let(:err) { Factory :err, :issue_link => "http://some.host" } 411 let(:err) { Factory :err, :issue_link => "http://some.host" }
368 412
369 before(:each) do 413 before(:each) do
370 - delete :clear_issue, :app_id => err.app.id, :id => err.id 414 + delete :unlink_issue, :app_id => err.app.id, :id => err.id
371 err.reload 415 err.reload
372 end 416 end
373 417
@@ -384,7 +428,7 @@ describe ErrsController do @@ -384,7 +428,7 @@ describe ErrsController do
384 let(:err) { Factory :err } 428 let(:err) { Factory :err }
385 429
386 before(:each) do 430 before(:each) do
387 - delete :clear_issue, :app_id => err.app.id, :id => err.id 431 + delete :unlink_issue, :app_id => err.app.id, :id => err.id
388 err.reload 432 err.reload
389 end 433 end
390 434