Commit e5e6ce94b24155d18551e3299e6808eafa50841f
Exists in
master
and in
1 other branch
Merge branch 'fogbugz_integration' of https://github.com/narshlob/errbit into pull-request-43
Conflicts: app/models/app.rb
Showing
26 changed files
with
409 additions
and
92 deletions
Show diff stats
Gemfile
| ... | ... | @@ -2,7 +2,7 @@ source 'http://rubygems.org' |
| 2 | 2 | |
| 3 | 3 | gem 'rails', '3.0.5' |
| 4 | 4 | gem 'nokogiri' |
| 5 | -gem 'mongoid', '2.0.0.rc.8' | |
| 5 | +gem 'mongoid', '2.0.2' | |
| 6 | 6 | gem 'haml' |
| 7 | 7 | gem 'will_paginate' |
| 8 | 8 | gem 'devise', '~> 1.1.8' |
| ... | ... | @@ -11,14 +11,16 @@ gem 'redmine_client', :git => "git://github.com/oruen/redmine_client.git" |
| 11 | 11 | gem 'mongoid_rails_migrations' |
| 12 | 12 | gem 'useragent', '~> 0.3.1' |
| 13 | 13 | gem 'pivotal-tracker' |
| 14 | +gem 'ruby-fogbugz', :require => 'fogbugz' | |
| 14 | 15 | |
| 15 | 16 | platform :ruby do |
| 16 | - gem 'bson_ext', '~> 1.2' | |
| 17 | + gem 'bson_ext', '~> 1.3.1' | |
| 17 | 18 | end |
| 18 | 19 | |
| 19 | 20 | group :development, :test do |
| 20 | 21 | gem 'rspec-rails', '~> 2.5' |
| 21 | 22 | gem 'webmock', :require => false |
| 23 | + gem 'ruby-debug19', :require => 'ruby-debug' | |
| 22 | 24 | end |
| 23 | 25 | |
| 24 | 26 | group :test do | ... | ... |
Gemfile.lock
| ... | ... | @@ -36,11 +36,13 @@ GEM |
| 36 | 36 | activesupport (= 3.0.5) |
| 37 | 37 | activesupport (3.0.5) |
| 38 | 38 | addressable (2.2.5) |
| 39 | + archive-tar-minitar (0.5.2) | |
| 39 | 40 | arel (2.0.9) |
| 40 | 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 | 44 | builder (2.1.2) |
| 45 | + columnize (0.3.4) | |
| 44 | 46 | crack (0.1.8) |
| 45 | 47 | daemons (1.1.4) |
| 46 | 48 | database_cleaner (0.6.7) |
| ... | ... | @@ -65,19 +67,20 @@ GEM |
| 65 | 67 | lighthouse-api (2.0) |
| 66 | 68 | activeresource (>= 3.0.0) |
| 67 | 69 | activesupport (>= 3.0.0) |
| 70 | + linecache19 (0.5.12) | |
| 71 | + ruby_core_source (>= 0.1.4) | |
| 68 | 72 | mail (2.2.17) |
| 69 | 73 | activesupport (>= 2.3.6) |
| 70 | 74 | i18n (>= 0.4.0) |
| 71 | 75 | mime-types (~> 1.16) |
| 72 | 76 | treetop (~> 1.4.8) |
| 73 | 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 | 81 | activemodel (~> 3.0) |
| 78 | - mongo (~> 1.2) | |
| 82 | + mongo (~> 1.3) | |
| 79 | 83 | tzinfo (~> 0.3.22) |
| 80 | - will_paginate (~> 3.0.pre) | |
| 81 | 84 | mongoid_rails_migrations (0.0.10) |
| 82 | 85 | activesupport (~> 3.0.0) |
| 83 | 86 | bundler (>= 0.9.19) |
| ... | ... | @@ -124,6 +127,19 @@ GEM |
| 124 | 127 | activesupport (~> 3.0) |
| 125 | 128 | railties (~> 3.0) |
| 126 | 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 | 143 | thin (1.2.11) |
| 128 | 144 | daemons (>= 1.0.9) |
| 129 | 145 | eventmachine (>= 0.12.6) |
| ... | ... | @@ -131,6 +147,9 @@ GEM |
| 131 | 147 | thor (0.14.6) |
| 132 | 148 | treetop (1.4.9) |
| 133 | 149 | polyglot (>= 0.3.1) |
| 150 | + typhoeus (0.2.4) | |
| 151 | + mime-types | |
| 152 | + mime-types | |
| 134 | 153 | tzinfo (0.3.26) |
| 135 | 154 | useragent (0.3.1) |
| 136 | 155 | warden (1.0.3) |
| ... | ... | @@ -144,14 +163,14 @@ PLATFORMS |
| 144 | 163 | ruby |
| 145 | 164 | |
| 146 | 165 | DEPENDENCIES |
| 147 | - bson_ext (~> 1.2) | |
| 166 | + bson_ext (~> 1.3.1) | |
| 148 | 167 | database_cleaner (~> 0.6.0) |
| 149 | 168 | devise (~> 1.1.8) |
| 150 | 169 | email_spec |
| 151 | 170 | factory_girl_rails |
| 152 | 171 | haml |
| 153 | 172 | lighthouse-api |
| 154 | - mongoid (= 2.0.0.rc.8) | |
| 173 | + mongoid (= 2.0.2) | |
| 155 | 174 | mongoid_rails_migrations |
| 156 | 175 | nokogiri |
| 157 | 176 | pivotal-tracker |
| ... | ... | @@ -159,6 +178,8 @@ DEPENDENCIES |
| 159 | 178 | redmine_client! |
| 160 | 179 | rspec (~> 2.5) |
| 161 | 180 | rspec-rails (~> 2.5) |
| 181 | + ruby-debug19 | |
| 182 | + ruby-fogbugz | |
| 162 | 183 | thin |
| 163 | 184 | useragent (~> 0.3.1) |
| 164 | 185 | webmock | ... | ... |
app/controllers/apps_controller.rb
| ... | ... | @@ -8,9 +8,17 @@ class AppsController < ApplicationController |
| 8 | 8 | end |
| 9 | 9 | |
| 10 | 10 | def show |
| 11 | + where_clause = {} | |
| 11 | 12 | respond_to do |format| |
| 12 | 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 | 22 | @deploys = @app.deploys.order_by(:created_at.desc).limit(5) |
| 15 | 23 | end |
| 16 | 24 | format.atom do | ... | ... |
app/controllers/errs_controller.rb
| ... | ... | @@ -5,12 +5,14 @@ class ErrsController < ApplicationController |
| 5 | 5 | |
| 6 | 6 | def index |
| 7 | 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 | 10 | respond_to do |format| |
| 9 | 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 | 13 | end |
| 12 | 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 | 16 | end |
| 15 | 17 | end |
| 16 | 18 | end |
| ... | ... | @@ -42,7 +44,7 @@ class ErrsController < ApplicationController |
| 42 | 44 | redirect_to app_err_path(@app, @err) |
| 43 | 45 | end |
| 44 | 46 | |
| 45 | - def clear_issue | |
| 47 | + def unlink_issue | |
| 46 | 48 | @err.update_attribute :issue_link, nil |
| 47 | 49 | redirect_to app_err_path(@app, @err) |
| 48 | 50 | end | ... | ... |
app/controllers/users_controller.rb
| ... | ... | @@ -6,7 +6,7 @@ class UsersController < ApplicationController |
| 6 | 6 | before_filter :require_user_edit_priviledges, :only => [:edit, :update] |
| 7 | 7 | |
| 8 | 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 | 10 | end |
| 11 | 11 | |
| 12 | 12 | def show | ... | ... |
app/helpers/application_helper.rb
app/models/app.rb
| ... | ... | @@ -21,11 +21,11 @@ class App |
| 21 | 21 | embeds_many :watchers |
| 22 | 22 | embeds_many :deploys |
| 23 | 23 | embeds_one :issue_tracker |
| 24 | - references_many :errs, :dependent => :destroy | |
| 24 | + has_many :errs, :inverse_of => :app, :dependent => :destroy | |
| 25 | 25 | |
| 26 | 26 | before_validation :generate_api_key, :on => :create |
| 27 | 27 | before_save :normalize_github_url |
| 28 | - | |
| 28 | + | |
| 29 | 29 | validates_presence_of :name, :api_key |
| 30 | 30 | validates_uniqueness_of :name, :allow_blank => true |
| 31 | 31 | validates_uniqueness_of :api_key, :allow_blank => true |
| ... | ... | @@ -35,7 +35,7 @@ class App |
| 35 | 35 | accepts_nested_attributes_for :watchers, :allow_destroy => true, |
| 36 | 36 | :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? } |
| 37 | 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 | 40 | # Mongoid Bug: find(id) on association proxies returns an Enumerator |
| 41 | 41 | def self.find_by_id!(app_id) |
| ... | ... | @@ -60,18 +60,19 @@ class App |
| 60 | 60 | !(self[:notify_on_deploys] == false) |
| 61 | 61 | end |
| 62 | 62 | alias :notify_on_deploys? :notify_on_deploys |
| 63 | - | |
| 63 | + | |
| 64 | 64 | def github_url? |
| 65 | 65 | self.github_url.present? |
| 66 | 66 | end |
| 67 | - | |
| 67 | + | |
| 68 | 68 | def github_url_to_file(file) |
| 69 | 69 | "#{self.github_url}/blob/master#{file}" |
| 70 | 70 | end |
| 71 | - | |
| 71 | + | |
| 72 | 72 | def issue_tracker_configured? |
| 73 | 73 | issue_tracker && !issue_tracker.project_id.blank? |
| 74 | 74 | end |
| 75 | + | |
| 75 | 76 | protected |
| 76 | 77 | |
| 77 | 78 | def generate_api_key |
| ... | ... | @@ -86,12 +87,12 @@ class App |
| 86 | 87 | end if issue_tracker.errors |
| 87 | 88 | end |
| 88 | 89 | end |
| 89 | - | |
| 90 | + | |
| 90 | 91 | def normalize_github_url |
| 91 | 92 | return if self.github_url.blank? |
| 92 | 93 | self.github_url.gsub!(%r{^http://|git@}, 'https://') |
| 93 | 94 | self.github_url.gsub!(/github\.com:/, 'github.com/') |
| 94 | 95 | self.github_url.gsub!(/\.git$/, '') |
| 95 | 96 | end |
| 96 | - | |
| 97 | + | |
| 97 | 98 | end | ... | ... |
app/models/err.rb
app/models/issue_tracker.rb
| ... | ... | @@ -12,6 +12,8 @@ class IssueTracker |
| 12 | 12 | field :account, :type => String |
| 13 | 13 | field :api_token, :type => String |
| 14 | 14 | field :project_id, :type => String |
| 15 | + field :username, :type => String | |
| 16 | + field :password, :type => String | |
| 15 | 17 | field :issue_tracker_type, :type => String, :default => 'lighthouseapp' |
| 16 | 18 | |
| 17 | 19 | def create_issue err |
| ... | ... | @@ -22,6 +24,8 @@ class IssueTracker |
| 22 | 24 | create_redmine_issue err |
| 23 | 25 | when 'pivotal' |
| 24 | 26 | create_pivotal_issue err |
| 27 | + when 'fogbugz' | |
| 28 | + create_fogbugz_issue err | |
| 25 | 29 | end |
| 26 | 30 | end |
| 27 | 31 | |
| ... | ... | @@ -65,22 +69,44 @@ class IssueTracker |
| 65 | 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 | 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 | 87 | def issue_title err |
| 69 | 88 | "[#{ err.environment }][#{ err.where }] #{err.message.to_s.truncate(100)}" |
| 70 | 89 | end |
| 71 | 90 | |
| 72 | 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 | 99 | blank_flags = blank_flag_fields.map {|m| self[m].blank? } |
| 76 | 100 | if blank_flags.any? && !blank_flags.all? |
| 77 | 101 | message = case issue_tracker_type |
| 78 | 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 | 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 | 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 | 110 | end |
| 85 | 111 | errors.add(:base, message) |
| 86 | 112 | end |
| ... | ... | @@ -98,5 +124,9 @@ class IssueTracker |
| 98 | 124 | def pivotal_body_template |
| 99 | 125 | @@pivotal_body_template ||= ERB.new(File.read(Rails.root + "app/views/errs/pivotal_body.txt.erb")) |
| 100 | 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 | 131 | end |
| 102 | 132 | end | ... | ... |
app/models/notice.rb
app/views/apps/_fields.html.haml
| ... | ... | @@ -45,13 +45,15 @@ |
| 45 | 45 | = label_tag :issue_tracker_type_redmine, 'Redmine', :for => label_for_attr(w, 'issue_tracker_type_redmine') |
| 46 | 46 | = w.radio_button :issue_tracker_type, :pivotal |
| 47 | 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 | 50 | %div.tracker_params.lighthouseapp{:class => lighthouse_tracker?(w.object) ? 'chosen' : nil} |
| 49 | 51 | = w.label :account, "Account" |
| 50 | 52 | = w.text_field :account, :placeholder => "abc from abc.lighthouseapp.com" |
| 51 | 53 | = w.label :api_token, "API token" |
| 52 | 54 | = w.text_field :api_token, :placeholder => "API Token for your account" |
| 53 | 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 | 57 | %div.tracker_params.redmine{:class => redmine_tracker?(w.object) ? 'chosen' : nil} |
| 56 | 58 | = w.label :account, "Redmine URL" |
| 57 | 59 | = w.text_field :account, :placeholder => "like http://www.redmine.org/" |
| ... | ... | @@ -64,3 +66,12 @@ |
| 64 | 66 | = w.text_field :project_id |
| 65 | 67 | = w.label :api_token, "API token" |
| 66 | 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 | 1 | - content_for :title, @app.name |
| 2 | 2 | - content_for :head do |
| 3 | 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 | 5 | - content_for :meta do |
| 5 | 6 | %strong Errs Caught: |
| 6 | 7 | = @app.errs.count |
| ... | ... | @@ -12,53 +13,66 @@ |
| 12 | 13 | - if current_user.admin? |
| 13 | 14 | = link_to 'edit', edit_app_path(@app), :class => 'button' |
| 14 | 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 | 26 | %thead |
| 34 | 27 | %tr |
| 35 | - %th GitHub | |
| 28 | + %th User or Email | |
| 36 | 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 | 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 | 77 | - if @app.errs.count > 0 |
| 64 | 78 | %h3.clear Errs | ... | ... |
app/views/errs/_table.html.haml
| ... | ... | @@ -12,7 +12,10 @@ |
| 12 | 12 | %tr{:class => err.resolved? ? 'resolved' : 'unresolved'} |
| 13 | 13 | %td.app |
| 14 | 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 | 19 | %td.message |
| 17 | 20 | = link_to err.message, app_err_path(err.app, err) |
| 18 | 21 | %em= err.where | ... | ... |
| ... | ... | @@ -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 | 5 | = @app.name |
| 6 | 6 | %strong Where: |
| 7 | 7 | = @err.where |
| 8 | + %br | |
| 8 | 9 | %strong Environment: |
| 9 | 10 | = @err.environment |
| 10 | 11 | %strong Last Notice: |
| ... | ... | @@ -15,7 +16,7 @@ |
| 15 | 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 | 17 | - else |
| 17 | 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 | 20 | - if @err.unresolved? |
| 20 | 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
app/views/notices/_session.html.haml
| ... | ... | @@ -0,0 +1 @@ |
| 1 | +require Rails.root.join('lib/overrides/mongoid/relations/builder.rb') | ... | ... |
config/routes.rb
| 1 | 1 | Errbit::Application.routes.draw do |
| 2 | - | |
| 2 | + | |
| 3 | 3 | devise_for :users |
| 4 | 4 | |
| 5 | 5 | # Hoptoad Notifier Routes |
| 6 | 6 | match '/notifier_api/v2/notices' => 'notices#create' |
| 7 | 7 | match '/deploys.txt' => 'deploys#create' |
| 8 | - | |
| 8 | + | |
| 9 | 9 | resources :notices, :only => [:show] |
| 10 | 10 | resources :deploys, :only => [:show] |
| 11 | 11 | resources :users |
| ... | ... | @@ -14,22 +14,22 @@ Errbit::Application.routes.draw do |
| 14 | 14 | get :all |
| 15 | 15 | end |
| 16 | 16 | end |
| 17 | - | |
| 17 | + | |
| 18 | 18 | resources :apps do |
| 19 | 19 | resources :errs do |
| 20 | 20 | resources :notices |
| 21 | 21 | member do |
| 22 | 22 | put :resolve |
| 23 | 23 | post :create_issue |
| 24 | - delete :clear_issue | |
| 24 | + delete :unlink_issue | |
| 25 | 25 | end |
| 26 | 26 | end |
| 27 | 27 | |
| 28 | 28 | resources :deploys, :only => [:index] |
| 29 | 29 | end |
| 30 | - | |
| 30 | + | |
| 31 | 31 | devise_for :users |
| 32 | - | |
| 32 | + | |
| 33 | 33 | root :to => 'apps#index' |
| 34 | - | |
| 34 | + | |
| 35 | 35 | end | ... | ... |
| ... | ... | @@ -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 | ... | ... |
4.78 KB
4.78 KB
| ... | ... | @@ -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 | 376 | table.single_user { |
| 377 | 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 | 387 | /* Code */ |
| 380 | 388 | pre { |
| 381 | 389 | padding: 0.8em; |
| 382 | 390 | margin-bottom: 1em; |
| 383 | - color: #f0f0f0; | |
| 384 | - background-color: #222; | |
| 385 | - border: 1px solid #444; | |
| 386 | 391 | font-family: monaco, courier, monospace; |
| 387 | 392 | font-size: 1.1em; |
| 388 | 393 | } |
| ... | ... | @@ -502,7 +507,7 @@ a.button.active { |
| 502 | 507 | } |
| 503 | 508 | |
| 504 | 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 | 511 | display: none; |
| 507 | 512 | } |
| 508 | 513 | div.nested.watcher .choosen, div.nested.issue_tracker .chosen { |
| ... | ... | @@ -612,6 +617,10 @@ table.tally th.value { |
| 612 | 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 | 624 | #action-bar a.lighthouseapp_goto { |
| 616 | 625 | background: transparent url(/images/lighthouseapp_goto.png) 6px 5px no-repeat; |
| 617 | 626 | } |
| ... | ... | @@ -624,6 +633,10 @@ table.tally th.value { |
| 624 | 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 | 640 | /* Notices Pagination */ |
| 628 | 641 | .notice-pagination { |
| 629 | 642 | float: left; |
| ... | ... | @@ -636,9 +649,11 @@ table.tally th.value { |
| 636 | 649 | margin-bottom: 1em; |
| 637 | 650 | overflow: auto; |
| 638 | 651 | } |
| 652 | + | |
| 639 | 653 | .window table { |
| 640 | 654 | margin: 0; |
| 641 | 655 | } |
| 656 | + | |
| 642 | 657 | table.backtrace td { |
| 643 | 658 | width: 100%; |
| 644 | 659 | padding: 0; |
| ... | ... | @@ -660,3 +675,11 @@ table.backtrace li.in-app { |
| 660 | 675 | color: #2adb2e; |
| 661 | 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 | 74 | assigns(:errs).size.should == 10 |
| 75 | 75 | end |
| 76 | 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 | 142 | end |
| 78 | 143 | |
| 79 | 144 | context 'logged in as a user' do |
| ... | ... | @@ -261,6 +326,34 @@ describe AppsController do |
| 261 | 326 | response.body.should match(/You must specify your Pivotal Tracker api token and project id/) |
| 262 | 327 | end |
| 263 | 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 | 357 | end |
| 265 | 358 | end |
| 266 | 359 | ... | ... |
spec/controllers/errs_controller_spec.rb
| ... | ... | @@ -54,6 +54,50 @@ describe ErrsController do |
| 54 | 54 | assigns(:errs).size.should == 10 |
| 55 | 55 | end |
| 56 | 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 | 101 | end |
| 58 | 102 | |
| 59 | 103 | context 'when logged in as a user' do |
| ... | ... | @@ -358,7 +402,7 @@ describe ErrsController do |
| 358 | 402 | end |
| 359 | 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 | 406 | before(:each) do |
| 363 | 407 | sign_in Factory(:admin) |
| 364 | 408 | end |
| ... | ... | @@ -367,7 +411,7 @@ describe ErrsController do |
| 367 | 411 | let(:err) { Factory :err, :issue_link => "http://some.host" } |
| 368 | 412 | |
| 369 | 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 | 415 | err.reload |
| 372 | 416 | end |
| 373 | 417 | |
| ... | ... | @@ -384,7 +428,7 @@ describe ErrsController do |
| 384 | 428 | let(:err) { Factory :err } |
| 385 | 429 | |
| 386 | 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 | 432 | err.reload |
| 389 | 433 | end |
| 390 | 434 | ... | ... |