Commit ac8d29a562cb5f44832106643398b9b4013f7da9
Exists in
master
and in
1 other branch
Merge pull request #612 from errbit/features/extract_issue_tracker
Extract all Issue Tracker in gems
Showing
68 changed files
with
802 additions
and
1416 deletions
Show diff stats
Gemfile
@@ -25,31 +25,18 @@ gem 'rails_autolink' | @@ -25,31 +25,18 @@ gem 'rails_autolink' | ||
25 | # Please don't update hoptoad_notifier to airbrake. | 25 | # Please don't update hoptoad_notifier to airbrake. |
26 | # It's for internal use only, and we monkeypatch certain methods | 26 | # It's for internal use only, and we monkeypatch certain methods |
27 | gem 'hoptoad_notifier', "~> 2.4" | 27 | gem 'hoptoad_notifier', "~> 2.4" |
28 | - | ||
29 | - | ||
30 | -# Remove / comment out any of the gems below if you want to disable | ||
31 | -# a given issue tracker, notification service, or authentication. | ||
32 | - | ||
33 | -# Issue Trackers | ||
34 | -# --------------------------------------- | ||
35 | -# Lighthouse | ||
36 | -gem 'lighthouse-api' | ||
37 | -# Redmine | ||
38 | -gem 'oruen_redmine_client', :require => 'redmine_client' | ||
39 | -# Pivotal Tracker | ||
40 | -gem 'pivotal-tracker' | ||
41 | -# Fogbugz | ||
42 | -gem 'ruby-fogbugz', :require => 'fogbugz' | ||
43 | -# Github Issues | ||
44 | -gem 'octokit', '~> 2.0' | ||
45 | -# Gitlab | ||
46 | -gem 'gitlab', '~> 3.0.0' | ||
47 | - | ||
48 | -# Bitbucket Issues | ||
49 | -gem 'bitbucket_rest_api', :require => false | ||
50 | - | ||
51 | -# Jira | ||
52 | -gem 'jira-ruby', :require => 'jira' | 28 | +gem 'draper', :require => false |
29 | + | ||
30 | +gem 'errbit_plugin' | ||
31 | +gem 'errbit_bitbucket_plugin' | ||
32 | +gem 'errbit_fogbugz_plugin' | ||
33 | +gem 'errbit_github_plugin' | ||
34 | +gem 'errbit_gitlab_plugin' | ||
35 | +gem 'errbit_jira_plugin' | ||
36 | +gem 'errbit_lighthouse_plugin' | ||
37 | +gem 'errbit_pivotal_plugin' | ||
38 | +gem 'errbit_redmine_plugin' | ||
39 | +gem 'errbit_unfuddle_plugin' | ||
53 | 40 | ||
54 | # Notification services | 41 | # Notification services |
55 | # --------------------------------------- | 42 | # --------------------------------------- |
@@ -101,6 +88,7 @@ end | @@ -101,6 +88,7 @@ end | ||
101 | 88 | ||
102 | group :test do | 89 | group :test do |
103 | gem 'capybara' | 90 | gem 'capybara' |
91 | + gem 'poltergeist' | ||
104 | gem 'launchy' | 92 | gem 'launchy' |
105 | gem 'database_cleaner' | 93 | gem 'database_cleaner' |
106 | gem 'email_spec' | 94 | gem 'email_spec' |
Gemfile.lock
@@ -32,7 +32,7 @@ GEM | @@ -32,7 +32,7 @@ GEM | ||
32 | activesupport (3.2.21) | 32 | activesupport (3.2.21) |
33 | i18n (~> 0.6, >= 0.6.4) | 33 | i18n (~> 0.6, >= 0.6.4) |
34 | multi_json (~> 1.0) | 34 | multi_json (~> 1.0) |
35 | - addressable (2.3.5) | 35 | + addressable (2.3.6) |
36 | airbrake (3.1.14) | 36 | airbrake (3.1.14) |
37 | builder | 37 | builder |
38 | json | 38 | json |
@@ -66,6 +66,7 @@ GEM | @@ -66,6 +66,7 @@ GEM | ||
66 | rack (>= 1.0.0) | 66 | rack (>= 1.0.0) |
67 | rack-test (>= 0.5.4) | 67 | rack-test (>= 0.5.4) |
68 | xpath (~> 2.0) | 68 | xpath (~> 2.0) |
69 | + cliver (0.3.2) | ||
69 | coderay (1.0.9) | 70 | coderay (1.0.9) |
70 | coveralls (0.7.0) | 71 | coveralls (0.7.0) |
71 | multi_json (~> 1.3) | 72 | multi_json (~> 1.3) |
@@ -73,8 +74,8 @@ GEM | @@ -73,8 +74,8 @@ GEM | ||
73 | simplecov (>= 0.7) | 74 | simplecov (>= 0.7) |
74 | term-ansicolor | 75 | term-ansicolor |
75 | thor | 76 | thor |
76 | - crack (0.4.1) | ||
77 | - safe_yaml (~> 0.9.0) | 77 | + crack (0.4.2) |
78 | + safe_yaml (~> 1.0.0) | ||
78 | css_parser (1.3.5) | 79 | css_parser (1.3.5) |
79 | addressable | 80 | addressable |
80 | database_cleaner (1.2.0) | 81 | database_cleaner (1.2.0) |
@@ -88,16 +89,49 @@ GEM | @@ -88,16 +89,49 @@ GEM | ||
88 | warden (~> 1.2.3) | 89 | warden (~> 1.2.3) |
89 | diff-lcs (1.2.4) | 90 | diff-lcs (1.2.4) |
90 | dotenv (0.9.0) | 91 | dotenv (0.9.0) |
92 | + draper (1.3.0) | ||
93 | + actionpack (>= 3.0) | ||
94 | + activemodel (>= 3.0) | ||
95 | + activesupport (>= 3.0) | ||
96 | + request_store (~> 1.0.3) | ||
91 | email_spec (1.5.0) | 97 | email_spec (1.5.0) |
92 | launchy (~> 2.1) | 98 | launchy (~> 2.1) |
93 | mail (~> 2.2) | 99 | mail (~> 2.2) |
100 | + errbit_bitbucket_plugin (0.1.0) | ||
101 | + bitbucket_rest_api | ||
102 | + errbit_plugin | ||
103 | + faraday (~> 0.8.1) | ||
104 | + errbit_fogbugz_plugin (0.1.0) | ||
105 | + errbit_plugin (~> 0.4, >= 0.4.0) | ||
106 | + ruby-fogbugz (~> 0.1, >= 0.1) | ||
107 | + errbit_github_plugin (0.1.0) | ||
108 | + errbit_plugin | ||
109 | + octokit | ||
110 | + errbit_gitlab_plugin (0.1.0) | ||
111 | + errbit_plugin (~> 0.4, >= 0.4.0) | ||
112 | + gitlab (~> 3.0.0, >= 3.0.0) | ||
113 | + errbit_jira_plugin (0.2.0) | ||
114 | + errbit_plugin | ||
115 | + jira-ruby | ||
116 | + errbit_lighthouse_plugin (0.1.0) | ||
117 | + errbit_plugin (~> 0) | ||
118 | + lighthouse-api (~> 2) | ||
119 | + errbit_pivotal_plugin (0.2.0) | ||
120 | + errbit_plugin (~> 0.4, >= 0.4.0) | ||
121 | + pivotal-tracker (~> 0.5, >= 0.5.0) | ||
122 | + errbit_plugin (0.4.0) | ||
123 | + errbit_redmine_plugin (0.2.0) | ||
124 | + errbit_plugin (~> 0) | ||
125 | + oruen_redmine_client (~> 0) | ||
126 | + errbit_unfuddle_plugin (0.1.0) | ||
127 | + errbit_plugin (~> 0.4, >= 0.4.0) | ||
94 | erubis (2.7.0) | 128 | erubis (2.7.0) |
95 | execjs (2.0.2) | 129 | execjs (2.0.2) |
96 | - fabrication (2.8.1) | 130 | + fabrication (2.9.0) |
97 | faraday (0.8.9) | 131 | faraday (0.8.9) |
98 | multipart-post (~> 1.2.0) | 132 | multipart-post (~> 1.2.0) |
99 | - faraday_middleware (0.9.0) | ||
100 | - faraday (>= 0.7.4, < 0.9) | 133 | + faraday_middleware (0.9.1) |
134 | + faraday (>= 0.7.4, < 0.10) | ||
101 | flowdock (0.3.1) | 135 | flowdock (0.3.1) |
102 | httparty (~> 0.7) | 136 | httparty (~> 0.7) |
103 | multi_json | 137 | multi_json |
@@ -111,7 +145,7 @@ GEM | @@ -111,7 +145,7 @@ GEM | ||
111 | happymapper (0.4.1) | 145 | happymapper (0.4.1) |
112 | libxml-ruby (~> 2.0) | 146 | libxml-ruby (~> 2.0) |
113 | hashie (2.0.5) | 147 | hashie (2.0.5) |
114 | - highline (1.6.19) | 148 | + highline (1.6.20) |
115 | hike (1.2.3) | 149 | hike (1.2.3) |
116 | hipchat (0.12.0) | 150 | hipchat (0.12.0) |
117 | httparty | 151 | httparty |
@@ -127,10 +161,9 @@ GEM | @@ -127,10 +161,9 @@ GEM | ||
127 | multi_xml (>= 0.5.2) | 161 | multi_xml (>= 0.5.2) |
128 | httpauth (0.2.0) | 162 | httpauth (0.2.0) |
129 | i18n (0.6.11) | 163 | i18n (0.6.11) |
130 | - jira-ruby (0.1.2) | 164 | + jira-ruby (0.1.10) |
131 | activesupport | 165 | activesupport |
132 | oauth | 166 | oauth |
133 | - railties | ||
134 | journey (1.0.4) | 167 | journey (1.0.4) |
135 | jquery-rails (2.1.4) | 168 | jquery-rails (2.1.4) |
136 | railties (>= 3.0, < 5.0) | 169 | railties (>= 3.0, < 5.0) |
@@ -158,7 +191,7 @@ GEM | @@ -158,7 +191,7 @@ GEM | ||
158 | railties | 191 | railties |
159 | method_source (0.8.2) | 192 | method_source (0.8.2) |
160 | mime-types (1.25.1) | 193 | mime-types (1.25.1) |
161 | - mini_portile (0.5.3) | 194 | + mini_portile (0.6.1) |
162 | mongoid (3.1.5) | 195 | mongoid (3.1.5) |
163 | activemodel (~> 3.2) | 196 | activemodel (~> 3.2) |
164 | moped (~> 1.4) | 197 | moped (~> 1.4) |
@@ -184,9 +217,9 @@ GEM | @@ -184,9 +217,9 @@ GEM | ||
184 | net-ssh (2.7.0) | 217 | net-ssh (2.7.0) |
185 | net-ssh-gateway (1.2.0) | 218 | net-ssh-gateway (1.2.0) |
186 | net-ssh (>= 2.6.5) | 219 | net-ssh (>= 2.6.5) |
187 | - nokogiri (1.6.1) | ||
188 | - mini_portile (~> 0.5.0) | ||
189 | - nokogiri-happymapper (0.5.8) | 220 | + nokogiri (1.6.4.1) |
221 | + mini_portile (~> 0.6.0) | ||
222 | + nokogiri-happymapper (0.5.9) | ||
190 | nokogiri (~> 1.5) | 223 | nokogiri (~> 1.5) |
191 | oauth (0.4.7) | 224 | oauth (0.4.7) |
192 | oauth2 (0.8.1) | 225 | oauth2 (0.8.1) |
@@ -195,8 +228,8 @@ GEM | @@ -195,8 +228,8 @@ GEM | ||
195 | jwt (~> 0.1.4) | 228 | jwt (~> 0.1.4) |
196 | multi_json (~> 1.0) | 229 | multi_json (~> 1.0) |
197 | rack (~> 1.2) | 230 | rack (~> 1.2) |
198 | - octokit (2.7.1) | ||
199 | - sawyer (~> 0.5.2) | 231 | + octokit (3.3.1) |
232 | + sawyer (~> 0.5.3) | ||
200 | omniauth (1.1.4) | 233 | omniauth (1.1.4) |
201 | hashie (>= 1.2, < 3) | 234 | hashie (>= 1.2, < 3) |
202 | rack | 235 | rack |
@@ -222,6 +255,11 @@ GEM | @@ -222,6 +255,11 @@ GEM | ||
222 | rest-client (~> 1.6.0) | 255 | rest-client (~> 1.6.0) |
223 | pjax_rails (0.3.4) | 256 | pjax_rails (0.3.4) |
224 | jquery-rails | 257 | jquery-rails |
258 | + poltergeist (1.5.1) | ||
259 | + capybara (~> 2.1) | ||
260 | + cliver (~> 0.3.1) | ||
261 | + multi_json (~> 1.0) | ||
262 | + websocket-driver (>= 0.2.0) | ||
225 | polyglot (0.3.5) | 263 | polyglot (0.3.5) |
226 | premailer (1.7.3) | 264 | premailer (1.7.3) |
227 | css_parser (>= 1.1.9) | 265 | css_parser (>= 1.1.9) |
@@ -268,8 +306,10 @@ GEM | @@ -268,8 +306,10 @@ GEM | ||
268 | rdoc (3.12.2) | 306 | rdoc (3.12.2) |
269 | json (~> 1.4) | 307 | json (~> 1.4) |
270 | ref (1.0.5) | 308 | ref (1.0.5) |
271 | - rest-client (1.6.7) | ||
272 | - mime-types (>= 1.16) | 309 | + request_store (1.0.6) |
310 | + rest-client (1.6.8) | ||
311 | + mime-types (~> 1.16) | ||
312 | + rdoc (>= 2.4.2) | ||
273 | ri_cal (0.8.8) | 313 | ri_cal (0.8.8) |
274 | rspec (2.14.1) | 314 | rspec (2.14.1) |
275 | rspec-core (~> 2.14.0) | 315 | rspec-core (~> 2.14.0) |
@@ -291,11 +331,11 @@ GEM | @@ -291,11 +331,11 @@ GEM | ||
291 | rushover (0.3.0) | 331 | rushover (0.3.0) |
292 | json | 332 | json |
293 | rest-client | 333 | rest-client |
294 | - safe_yaml (0.9.7) | ||
295 | - sawyer (0.5.3) | 334 | + safe_yaml (1.0.4) |
335 | + sawyer (0.5.5) | ||
296 | addressable (~> 2.3.5) | 336 | addressable (~> 2.3.5) |
297 | faraday (~> 0.8, < 0.10) | 337 | faraday (~> 0.8, < 0.10) |
298 | - simple_oauth (0.2.0) | 338 | + simple_oauth (0.3.0) |
299 | simplecov (0.7.1) | 339 | simplecov (0.7.1) |
300 | multi_json (~> 1.0) | 340 | multi_json (~> 1.0) |
301 | simplecov-html (~> 0.7.1) | 341 | simplecov-html (~> 0.7.1) |
@@ -330,18 +370,19 @@ GEM | @@ -330,18 +370,19 @@ GEM | ||
330 | tzinfo (0.3.41) | 370 | tzinfo (0.3.41) |
331 | uglifier (2.2.1) | 371 | uglifier (2.2.1) |
332 | execjs (>= 0.3.0) | 372 | execjs (>= 0.3.0) |
333 | - multi_json (~> 1.0, >= 1.0.2) | 373 | + json (>= 1.8.0) |
334 | underscore-rails (1.5.2) | 374 | underscore-rails (1.5.2) |
335 | unicorn (4.6.3) | 375 | unicorn (4.6.3) |
336 | kgio (~> 2.6) | 376 | kgio (~> 2.6) |
337 | rack | 377 | rack |
338 | raindrops (~> 0.7) | 378 | raindrops (~> 0.7) |
339 | - useragent (0.8.3) | 379 | + useragent (0.9.0) |
340 | warden (1.2.3) | 380 | warden (1.2.3) |
341 | rack (>= 1.0) | 381 | rack (>= 1.0) |
342 | webmock (1.15.0) | 382 | webmock (1.15.0) |
343 | addressable (>= 2.2.7) | 383 | addressable (>= 2.2.7) |
344 | crack (>= 0.3.2) | 384 | crack (>= 0.3.2) |
385 | + websocket-driver (0.3.3) | ||
345 | xmpp4r (0.5.5) | 386 | xmpp4r (0.5.5) |
346 | xpath (2.0.0) | 387 | xpath (2.0.0) |
347 | nokogiri (~> 1.3) | 388 | nokogiri (~> 1.3) |
@@ -357,7 +398,6 @@ DEPENDENCIES | @@ -357,7 +398,6 @@ DEPENDENCIES | ||
357 | airbrake | 398 | airbrake |
358 | better_errors | 399 | better_errors |
359 | binding_of_caller | 400 | binding_of_caller |
360 | - bitbucket_rest_api | ||
361 | campy | 401 | campy |
362 | capistrano (~> 2.0) | 402 | capistrano (~> 2.0) |
363 | capybara | 403 | capybara |
@@ -365,32 +405,38 @@ DEPENDENCIES | @@ -365,32 +405,38 @@ DEPENDENCIES | ||
365 | database_cleaner | 405 | database_cleaner |
366 | decent_exposure | 406 | decent_exposure |
367 | devise | 407 | devise |
408 | + draper | ||
368 | email_spec | 409 | email_spec |
410 | + errbit_bitbucket_plugin | ||
411 | + errbit_fogbugz_plugin | ||
412 | + errbit_github_plugin | ||
413 | + errbit_gitlab_plugin | ||
414 | + errbit_jira_plugin | ||
415 | + errbit_lighthouse_plugin | ||
416 | + errbit_pivotal_plugin | ||
417 | + errbit_plugin | ||
418 | + errbit_redmine_plugin | ||
419 | + errbit_unfuddle_plugin | ||
369 | execjs | 420 | execjs |
370 | fabrication | 421 | fabrication |
371 | flowdock | 422 | flowdock |
372 | foreman | 423 | foreman |
373 | - gitlab (~> 3.0.0) | ||
374 | haml | 424 | haml |
375 | hipchat | 425 | hipchat |
376 | hoi | 426 | hoi |
377 | hoptoad_notifier (~> 2.4) | 427 | hoptoad_notifier (~> 2.4) |
378 | htmlentities | 428 | htmlentities |
379 | httparty | 429 | httparty |
380 | - jira-ruby | ||
381 | jquery-rails (~> 2.1.4) | 430 | jquery-rails (~> 2.1.4) |
382 | kaminari (>= 0.14.1) | 431 | kaminari (>= 0.14.1) |
383 | launchy | 432 | launchy |
384 | - lighthouse-api | ||
385 | meta_request | 433 | meta_request |
386 | mongoid | 434 | mongoid |
387 | mongoid-rspec | 435 | mongoid-rspec |
388 | mongoid_rails_migrations | 436 | mongoid_rails_migrations |
389 | - octokit (~> 2.0) | ||
390 | omniauth-github | 437 | omniauth-github |
391 | - oruen_redmine_client | ||
392 | - pivotal-tracker | ||
393 | pjax_rails | 438 | pjax_rails |
439 | + poltergeist | ||
394 | pry-rails | 440 | pry-rails |
395 | puma | 441 | puma |
396 | quiet_assets | 442 | quiet_assets |
@@ -400,7 +446,6 @@ DEPENDENCIES | @@ -400,7 +446,6 @@ DEPENDENCIES | ||
400 | railties (~> 3.2.21) | 446 | railties (~> 3.2.21) |
401 | ri_cal | 447 | ri_cal |
402 | rspec-rails | 448 | rspec-rails |
403 | - ruby-fogbugz | ||
404 | rushover | 449 | rushover |
405 | strong_parameters | 450 | strong_parameters |
406 | therubyracer | 451 | therubyracer |
app/assets/images/github_create.png
2.14 KB
app/assets/images/github_inactive.png
1.05 KB
app/assets/stylesheets/issue_tracker_icons.css.erb
1 | /* Issue Tracker inactive, select, create and goto icons */ | 1 | /* Issue Tracker inactive, select, create and goto icons */ |
2 | -<% trackers = IssueTracker.subclasses.map{|t| t.label } << 'none' %> | ||
3 | 2 | ||
4 | -<% trackers.each do |tracker| %> | ||
5 | -div.issue_tracker.nested label.<%= tracker %> { | ||
6 | - background: url(<%= asset_path "#{ tracker }_inactive.png" %>) no-repeat; | ||
7 | -} | ||
8 | -div.issue_tracker.nested label.r_on.<%= tracker %> { | ||
9 | - background: url(<%= asset_path "#{ tracker }_create.png" %>) no-repeat; | ||
10 | -} | ||
11 | -#action-bar a.<%= tracker %>_create { | ||
12 | - background: transparent url(<%= asset_path "#{ tracker }_create.png" %>) 6px 5px no-repeat; | ||
13 | -} | ||
14 | -#action-bar a.<%= tracker %>_goto { | ||
15 | - background: transparent url(<%= asset_path "#{ tracker }_goto.png" %>) 6px 5px no-repeat; | ||
16 | -} | 3 | +<% ErrbitPlugin::Registry.issue_trackers.keys.each do |label| %> |
4 | +div.issue_tracker.nested label.<%= label %> { background: url(<%= asset_path "#{ label }_inactive.png" %>) no-repeat; } | ||
5 | +div.issue_tracker.nested label.r_on.<%= label %> { background: url(<%= asset_path "#{ label }_create.png" %>) no-repeat; } | ||
6 | +#action-bar a.<%= label %>_create { background: transparent url(<%= asset_path "#{ label }_create.png" %>) 6px 5px no-repeat; } | ||
7 | +#action-bar a.<%= label %>_goto { background: transparent url(<%= asset_path "#{ label }_goto.png" %>) 6px 5px no-repeat; } | ||
17 | <% end %> | 8 | <% end %> |
18 | 9 |
app/controllers/apps_controller.rb
@@ -16,6 +16,9 @@ class AppsController < ApplicationController | @@ -16,6 +16,9 @@ class AppsController < ApplicationController | ||
16 | } | 16 | } |
17 | 17 | ||
18 | expose(:app, :ancestor => :app_scope) | 18 | expose(:app, :ancestor => :app_scope) |
19 | + expose(:app_decorate) do | ||
20 | + AppDecorator.new(app) | ||
21 | + end | ||
19 | 22 | ||
20 | expose(:all_errs) { | 23 | expose(:all_errs) { |
21 | !!params[:all_errs] | 24 | !!params[:all_errs] |
@@ -50,7 +53,6 @@ class AppsController < ApplicationController | @@ -50,7 +53,6 @@ class AppsController < ApplicationController | ||
50 | end | 53 | end |
51 | 54 | ||
52 | def create | 55 | def create |
53 | - initialize_subclassed_issue_tracker | ||
54 | initialize_subclassed_notification_service | 56 | initialize_subclassed_notification_service |
55 | if app.save | 57 | if app.save |
56 | redirect_to app_url(app), :flash => { :success => I18n.t('controllers.apps.flash.create.success') } | 58 | redirect_to app_url(app), :flash => { :success => I18n.t('controllers.apps.flash.create.success') } |
@@ -61,7 +63,6 @@ class AppsController < ApplicationController | @@ -61,7 +63,6 @@ class AppsController < ApplicationController | ||
61 | end | 63 | end |
62 | 64 | ||
63 | def update | 65 | def update |
64 | - initialize_subclassed_issue_tracker | ||
65 | initialize_subclassed_notification_service | 66 | initialize_subclassed_notification_service |
66 | if app.save | 67 | if app.save |
67 | redirect_to app_url(app), :flash => { :success => I18n.t('controllers.apps.flash.update.success') } | 68 | redirect_to app_url(app), :flash => { :success => I18n.t('controllers.apps.flash.update.success') } |
@@ -91,17 +92,6 @@ class AppsController < ApplicationController | @@ -91,17 +92,6 @@ class AppsController < ApplicationController | ||
91 | 92 | ||
92 | protected | 93 | protected |
93 | 94 | ||
94 | - def initialize_subclassed_issue_tracker | ||
95 | - # set the app's issue tracker | ||
96 | - if params[:app][:issue_tracker_attributes] && tracker_type = params[:app][:issue_tracker_attributes][:type] | ||
97 | - available_tracker_classes = [IssueTracker] + IssueTracker.subclasses | ||
98 | - tracker_class = available_tracker_classes.detect{|c| c.name == tracker_type} | ||
99 | - if !tracker_class.nil? | ||
100 | - app.issue_tracker = tracker_class.new(params[:app][:issue_tracker_attributes]) | ||
101 | - end | ||
102 | - end | ||
103 | - end | ||
104 | - | ||
105 | def initialize_subclassed_notification_service | 95 | def initialize_subclassed_notification_service |
106 | # set the app's notification service | 96 | # set the app's notification service |
107 | if params[:app][:notification_service_attributes] && notification_type = params[:app][:notification_service_attributes][:type] | 97 | if params[:app][:notification_service_attributes] && notification_type = params[:app][:notification_service_attributes][:type] |
@@ -115,7 +105,7 @@ class AppsController < ApplicationController | @@ -115,7 +105,7 @@ class AppsController < ApplicationController | ||
115 | 105 | ||
116 | def plug_params app | 106 | def plug_params app |
117 | app.watchers.build if app.watchers.none? | 107 | app.watchers.build if app.watchers.none? |
118 | - app.issue_tracker = IssueTracker.new unless app.issue_tracker_configured? | 108 | + app.issue_tracker ||= IssueTracker.new |
119 | app.notification_service = NotificationService.new unless app.notification_service_configured? | 109 | app.notification_service = NotificationService.new unless app.notification_service_configured? |
120 | app.copy_attributes_from(params[:copy_attributes_from]) if params[:copy_attributes_from] | 110 | app.copy_attributes_from(params[:copy_attributes_from]) if params[:copy_attributes_from] |
121 | end | 111 | end |
app/controllers/problems_controller.rb
@@ -62,8 +62,7 @@ class ProblemsController < ApplicationController | @@ -62,8 +62,7 @@ class ProblemsController < ApplicationController | ||
62 | end | 62 | end |
63 | 63 | ||
64 | def create_issue | 64 | def create_issue |
65 | - IssueTracker.update_url_options(request) | ||
66 | - issue_creation = IssueCreation.new(problem, current_user, params[:tracker]) | 65 | + issue_creation = IssueCreation.new(problem, current_user, params[:tracker], request) |
67 | 66 | ||
68 | unless issue_creation.execute | 67 | unless issue_creation.execute |
69 | flash[:error] = issue_creation.errors.full_messages.join(', ') | 68 | flash[:error] = issue_creation.errors.full_messages.join(', ') |
@@ -0,0 +1,19 @@ | @@ -0,0 +1,19 @@ | ||
1 | +class AppDecorator < Draper::Decorator | ||
2 | + | ||
3 | + decorates_association :watchers | ||
4 | + decorates_association :issue_tracker, :with => IssueTrackerDecorator | ||
5 | + delegate_all | ||
6 | + | ||
7 | + def email_at_notices | ||
8 | + object.email_at_notices.join(', ') | ||
9 | + end | ||
10 | + | ||
11 | + def notify_user_display | ||
12 | + object.notify_all_users ? 'display: none;' : '' | ||
13 | + end | ||
14 | + | ||
15 | + def notify_err_display | ||
16 | + object.notify_on_errs ? '' : 'display: none;' | ||
17 | + end | ||
18 | + | ||
19 | +end |
@@ -0,0 +1,37 @@ | @@ -0,0 +1,37 @@ | ||
1 | +class IssueTrackerDecorator < Draper::Decorator | ||
2 | + | ||
3 | + def initialize(object, key) | ||
4 | + @object = object | ||
5 | + @key = key | ||
6 | + end | ||
7 | + attr_reader :key | ||
8 | + | ||
9 | + delegate_all | ||
10 | + | ||
11 | + def issue_trackers | ||
12 | + ErrbitPlugin::Registry.issue_trackers.each do |key, object| | ||
13 | + yield IssueTrackerDecorator.new(object, key) | ||
14 | + end | ||
15 | + end | ||
16 | + | ||
17 | + def note | ||
18 | + object.note.html_safe | ||
19 | + end | ||
20 | + | ||
21 | + def fields | ||
22 | + object.fields.each do |field, field_info| | ||
23 | + yield IssueTrackerFieldDecorator.new(field, field_info) | ||
24 | + end | ||
25 | + end | ||
26 | + | ||
27 | + def params_class(tracker) | ||
28 | + [chosen?(tracker), label].join(" ").strip | ||
29 | + end | ||
30 | + | ||
31 | + private | ||
32 | + | ||
33 | + def chosen?(issue_tracker) | ||
34 | + key == issue_tracker.type_tracker.to_s ? 'chosen' : '' | ||
35 | + end | ||
36 | + | ||
37 | +end |
@@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
1 | +class IssueTrackerFieldDecorator < Draper::Decorator | ||
2 | + | ||
3 | + def initialize(field, field_info) | ||
4 | + @object = field | ||
5 | + @field_info = field_info | ||
6 | + end | ||
7 | + attr_reader :object, :field_info | ||
8 | + | ||
9 | + alias :key :object | ||
10 | + | ||
11 | + def label | ||
12 | + field_info[:label] || object.to_s.titleize | ||
13 | + end | ||
14 | + | ||
15 | + | ||
16 | + def input(form, issue_tracker) | ||
17 | + form.send(input_field, key.to_s, | ||
18 | + :placeholder => field_info[:placeholder], | ||
19 | + :value => issue_tracker.options[key.to_s]) | ||
20 | + end | ||
21 | + | ||
22 | + private | ||
23 | + | ||
24 | + def input_field | ||
25 | + object == :password ? :password_field : :text_field | ||
26 | + end | ||
27 | +end |
app/interactors/issue_creation.rb
@@ -5,12 +5,24 @@ class IssueCreation | @@ -5,12 +5,24 @@ class IssueCreation | ||
5 | 5 | ||
6 | delegate :app, :to => :problem | 6 | delegate :app, :to => :problem |
7 | 7 | ||
8 | - def initialize(problem, user, tracker_name) | 8 | + def initialize(problem, user, tracker_name, request) |
9 | @problem = problem | 9 | @problem = problem |
10 | @user = user | 10 | @user = user |
11 | @tracker_name = tracker_name | 11 | @tracker_name = tracker_name |
12 | + IssueTracker.update_url_options(request) | ||
12 | end | 13 | end |
13 | 14 | ||
15 | + def execute | ||
16 | + tracker.create_issue(problem, user) if tracker | ||
17 | + errors.empty? | ||
18 | + rescue => ex | ||
19 | + Rails.logger.error "Error during issue creation: " << ex.message | ||
20 | + errors.add :base, "There was an error during issue creation: #{ex.message}" | ||
21 | + false | ||
22 | + end | ||
23 | + | ||
24 | + private | ||
25 | + | ||
14 | def tracker | 26 | def tracker |
15 | return @tracker if @tracker | 27 | return @tracker if @tracker |
16 | 28 | ||
@@ -21,10 +33,11 @@ class IssueCreation | @@ -21,10 +33,11 @@ class IssueCreation | ||
21 | elsif !user.github_account? | 33 | elsif !user.github_account? |
22 | errors.add :base, "You haven't linked your Github account." | 34 | errors.add :base, "You haven't linked your Github account." |
23 | else | 35 | else |
24 | - @tracker = GithubIssuesTracker.new( | ||
25 | - :app => app, | ||
26 | - :username => user.github_login, | ||
27 | - :oauth_token => user.github_oauth_token | 36 | + @tracker = ErrbitGithubPlugin::IssueTracker.new( |
37 | + app, { | ||
38 | + :username => user.github_login, | ||
39 | + :oauth_token => user.github_oauth_token | ||
40 | + } | ||
28 | ) | 41 | ) |
29 | end | 42 | end |
30 | 43 | ||
@@ -39,13 +52,4 @@ class IssueCreation | @@ -39,13 +52,4 @@ class IssueCreation | ||
39 | 52 | ||
40 | @tracker | 53 | @tracker |
41 | end | 54 | end |
42 | - | ||
43 | - def execute | ||
44 | - tracker.create_issue problem, user if tracker | ||
45 | - errors.empty? | ||
46 | - rescue => ex | ||
47 | - Rails.logger.error "Error during issue creation: " << ex.message | ||
48 | - errors.add :base, "There was an error during issue creation: #{ex.message}" | ||
49 | - false | ||
50 | - end | ||
51 | end | 55 | end |
app/models/app.rb
@@ -26,7 +26,7 @@ class App | @@ -26,7 +26,7 @@ class App | ||
26 | 26 | ||
27 | embeds_many :watchers | 27 | embeds_many :watchers |
28 | embeds_many :deploys | 28 | embeds_many :deploys |
29 | - embeds_one :issue_tracker | 29 | + embeds_one :issue_tracker, :class_name => 'IssueTracker' |
30 | embeds_one :notification_service | 30 | embeds_one :notification_service |
31 | 31 | ||
32 | has_many :problems, :inverse_of => :app, :dependent => :destroy | 32 | has_many :problems, :inverse_of => :app, :dependent => :destroy |
@@ -44,7 +44,7 @@ class App | @@ -44,7 +44,7 @@ class App | ||
44 | accepts_nested_attributes_for :watchers, :allow_destroy => true, | 44 | accepts_nested_attributes_for :watchers, :allow_destroy => true, |
45 | :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? } | 45 | :reject_if => proc { |attrs| attrs[:user_id].blank? && attrs[:email].blank? } |
46 | accepts_nested_attributes_for :issue_tracker, :allow_destroy => true, | 46 | accepts_nested_attributes_for :issue_tracker, :allow_destroy => true, |
47 | - :reject_if => proc { |attrs| !IssueTracker.subclasses.map(&:to_s).include?(attrs[:type].to_s) } | 47 | + :reject_if => proc { |attrs| !ErrbitPlugin::Registry.issue_trackers.keys.map(&:to_s).include?(attrs[:type_tracker].to_s) } |
48 | accepts_nested_attributes_for :notification_service, :allow_destroy => true, | 48 | accepts_nested_attributes_for :notification_service, :allow_destroy => true, |
49 | :reject_if => proc { |attrs| !NotificationService.subclasses.map(&:to_s).include?(attrs[:type].to_s) } | 49 | :reject_if => proc { |attrs| !NotificationService.subclasses.map(&:to_s).include?(attrs[:type].to_s) } |
50 | 50 | ||
@@ -120,7 +120,7 @@ class App | @@ -120,7 +120,7 @@ class App | ||
120 | 120 | ||
121 | 121 | ||
122 | def issue_tracker_configured? | 122 | def issue_tracker_configured? |
123 | - !!(issue_tracker.class < IssueTracker && issue_tracker.configured?) | 123 | + !!issue_tracker && !!(issue_tracker.configured?) |
124 | end | 124 | end |
125 | 125 | ||
126 | def notification_service_configured? | 126 | def notification_service_configured? |
@@ -175,6 +175,13 @@ class App | @@ -175,6 +175,13 @@ class App | ||
175 | set(:api_key, SecureRandom.hex) | 175 | set(:api_key, SecureRandom.hex) |
176 | end | 176 | end |
177 | 177 | ||
178 | + ## | ||
179 | + # Check if comments can be allowed on this application | ||
180 | + # | ||
181 | + def comments_allowed? | ||
182 | + !issue_tracker || issue_tracker.comments_allowed? | ||
183 | + end | ||
184 | + | ||
178 | protected | 185 | protected |
179 | 186 | ||
180 | def store_cached_attributes_on_problems | 187 | def store_cached_attributes_on_problems |
@@ -202,5 +209,6 @@ class App | @@ -202,5 +209,6 @@ class App | ||
202 | github_repo.sub!(/(git@|https?:\/\/)#{github_host}(\/|:)/, '') | 209 | github_repo.sub!(/(git@|https?:\/\/)#{github_host}(\/|:)/, '') |
203 | github_repo.sub!(/\.git$/, '') | 210 | github_repo.sub!(/\.git$/, '') |
204 | end | 211 | end |
212 | + | ||
205 | end | 213 | end |
206 | 214 |
app/models/issue_tracker.rb
1 | class IssueTracker | 1 | class IssueTracker |
2 | include Mongoid::Document | 2 | include Mongoid::Document |
3 | include Mongoid::Timestamps | 3 | include Mongoid::Timestamps |
4 | - include HashHelper | 4 | + |
5 | include Rails.application.routes.url_helpers | 5 | include Rails.application.routes.url_helpers |
6 | + | ||
6 | default_url_options[:host] = ActionMailer::Base.default_url_options[:host] | 7 | default_url_options[:host] = ActionMailer::Base.default_url_options[:host] |
7 | default_url_options[:port] = ActionMailer::Base.default_url_options[:port] | 8 | default_url_options[:port] = ActionMailer::Base.default_url_options[:port] |
8 | 9 | ||
9 | embedded_in :app, :inverse_of => :issue_tracker | 10 | embedded_in :app, :inverse_of => :issue_tracker |
10 | 11 | ||
11 | - field :project_id, :type => String | ||
12 | - field :alt_project_id, :type => String # Specify an alternative project id. e.g. for viewing files | ||
13 | - field :api_token, :type => String | ||
14 | - field :account, :type => String | ||
15 | - field :username, :type => String | ||
16 | - field :password, :type => String | ||
17 | - field :ticket_properties, :type => String | ||
18 | - field :subdomain, :type => String | ||
19 | - field :milestone_id, :type => String | ||
20 | - | ||
21 | - # Is there any better way to enhance the props? Putting them into the subclass leads to | ||
22 | - # an error while rendering the form fields -.- | ||
23 | - field :base_url, :type => String | ||
24 | - field :context_path, :type => String | ||
25 | - field :issue_type, :type => String | ||
26 | - field :issue_component, :type => String | ||
27 | - field :issue_priority, :type => String | ||
28 | - | ||
29 | - validate :check_params | ||
30 | - | ||
31 | - # Subclasses are responsible for overwriting this method. | ||
32 | - def check_params; true; end | ||
33 | - | ||
34 | - def issue_title(problem) | ||
35 | - "[#{ problem.environment }][#{ problem.where }] #{problem.message.to_s.truncate(100)}" | ||
36 | - end | ||
37 | - | ||
38 | - # Allows us to set the issue tracker class from a single form. | ||
39 | - def type; self._type; end | ||
40 | - def type=(t); self._type=t; end | 12 | + field :type_tracker, :type => String |
13 | + field :options, :type => Hash, :default => {} | ||
41 | 14 | ||
42 | - def url; nil; end | ||
43 | - | ||
44 | - # Retrieve tracker label from either class or instance. | ||
45 | - Label = '' | ||
46 | - def self.label; self::Label; end | ||
47 | - def label; self.class.label; end | ||
48 | - | ||
49 | - def configured? | ||
50 | - project_id.present? | ||
51 | - end | 15 | + validate :validate_tracker |
52 | 16 | ||
53 | ## | 17 | ## |
54 | # Update default_url_option with valid data from the request information | 18 | # Update default_url_option with valid data from the request information |
@@ -60,4 +24,23 @@ class IssueTracker | @@ -60,4 +24,23 @@ class IssueTracker | ||
60 | IssueTracker.default_url_options[:port] = request.port | 24 | IssueTracker.default_url_options[:port] = request.port |
61 | IssueTracker.default_url_options[:protocol] = request.scheme | 25 | IssueTracker.default_url_options[:protocol] = request.scheme |
62 | end | 26 | end |
27 | + | ||
28 | + def tracker | ||
29 | + klass = ErrbitPlugin::Registry.issue_trackers[self.type_tracker] | ||
30 | + klass = ErrbitPlugin::NoneIssueTracker unless klass | ||
31 | + | ||
32 | + @tracker = klass.new(app, self.options) | ||
33 | + end | ||
34 | + | ||
35 | + # Allow the tracker to validate its own params | ||
36 | + def validate_tracker | ||
37 | + (tracker.errors || {}).each do |k,v| | ||
38 | + errors.add k, v | ||
39 | + end | ||
40 | + end | ||
41 | + | ||
42 | + delegate :configured?, :to => :tracker | ||
43 | + delegate :create_issue, :to => :tracker | ||
44 | + delegate :comments_allowed?, :to => :tracker | ||
45 | + delegate :url, :to => :tracker | ||
63 | end | 46 | end |
app/models/issue_trackers/bitbucket_issues_tracker.rb
@@ -1,55 +0,0 @@ | @@ -1,55 +0,0 @@ | ||
1 | -begin | ||
2 | - require 'bitbucket_rest_api' | ||
3 | -rescue LoadError | ||
4 | -end | ||
5 | - | ||
6 | -if defined? BitBucket | ||
7 | - class IssueTrackers::BitbucketIssuesTracker < IssueTracker | ||
8 | - Label = "bitbucket" | ||
9 | - Note = 'Please configure your Bitbucket repository in the <strong>BITBUCKET REPO</strong> field above.' | ||
10 | - Fields = [ | ||
11 | - [:api_token, { | ||
12 | - :placeholder => "Your username on Bitbucket account", | ||
13 | - :label => "Username" | ||
14 | - }], | ||
15 | - [:project_id, { | ||
16 | - :placeholder => "Password for your Bitbucket account", | ||
17 | - :label => "Password" | ||
18 | - }] | ||
19 | - ] | ||
20 | - | ||
21 | - def check_params | ||
22 | - if Fields.detect {|f| self[f[0]].blank? } | ||
23 | - errors.add :base, 'You must specify your Bitbucket username and password' | ||
24 | - end | ||
25 | - end | ||
26 | - | ||
27 | - def repo_name | ||
28 | - app.bitbucket_repo | ||
29 | - end | ||
30 | - | ||
31 | - def create_issue(problem, reported_by = nil) | ||
32 | - bitbucket = BitBucket.new :basic_auth => "#{api_token}:#{project_id}" | ||
33 | - | ||
34 | - begin | ||
35 | - r_user = repo_name.split('/')[0] | ||
36 | - r_name = repo_name.split('/')[1] | ||
37 | - issue = bitbucket.issues.create r_user, r_name, :title => issue_title(problem), :content => body_template.result(binding), :priority => 'critical' | ||
38 | - problem.update_attributes( | ||
39 | - :issue_link => "https://bitbucket.org/#{repo_name}/issue/#{issue.local_id}/", | ||
40 | - :issue_type => Label | ||
41 | - ) | ||
42 | - rescue BitBucket::Error::Unauthorized | ||
43 | - raise IssueTrackers::AuthenticationError, "Could not authenticate with BitBucket. Please check your username and password." | ||
44 | - end | ||
45 | - end | ||
46 | - | ||
47 | - def body_template | ||
48 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/bitbucket_issues_body.txt.erb")) | ||
49 | - end | ||
50 | - | ||
51 | - def url | ||
52 | - "https://www.bitbucket.org/#{repo_name}/issues" | ||
53 | - end | ||
54 | - end | ||
55 | -end |
app/models/issue_trackers/fogbugz_tracker.rb
@@ -1,53 +0,0 @@ | @@ -1,53 +0,0 @@ | ||
1 | -if defined? Fogbugz | ||
2 | - class IssueTrackers::FogbugzTracker < IssueTracker | ||
3 | - Label = "fogbugz" | ||
4 | - Fields = [ | ||
5 | - [:project_id, { | ||
6 | - :label => "Area Name" | ||
7 | - }], | ||
8 | - [:account, { | ||
9 | - :label => "FogBugz URL", | ||
10 | - :placeholder => "abc from http://abc.fogbugz.com/" | ||
11 | - }], | ||
12 | - [:username, { | ||
13 | - :placeholder => "Username/Email for your account" | ||
14 | - }], | ||
15 | - [:password, { | ||
16 | - :placeholder => "Password for your account" | ||
17 | - }] | ||
18 | - ] | ||
19 | - | ||
20 | - def check_params | ||
21 | - if Fields.detect {|f| self[f[0]].blank? } | ||
22 | - errors.add :base, 'You must specify your FogBugz Area Name, FogBugz URL, Username, and Password' | ||
23 | - end | ||
24 | - end | ||
25 | - | ||
26 | - def create_issue(problem, reported_by = nil) | ||
27 | - fogbugz = Fogbugz::Interface.new(:email => username, :password => password, :uri => "https://#{account}.fogbugz.com") | ||
28 | - fogbugz.authenticate | ||
29 | - | ||
30 | - issue = {} | ||
31 | - issue['sTitle'] = issue_title problem | ||
32 | - issue['sArea'] = project_id | ||
33 | - issue['sEvent'] = body_template.result(binding) | ||
34 | - issue['sTags'] = ['errbit'].join(',') | ||
35 | - issue['cols'] = ['ixBug'].join(',') | ||
36 | - | ||
37 | - fb_resp = fogbugz.command(:new, issue) | ||
38 | - problem.update_attributes( | ||
39 | - :issue_link => "https://#{account}.fogbugz.com/default.asp?#{fb_resp['case']['ixBug']}", | ||
40 | - :issue_type => Label | ||
41 | - ) | ||
42 | - | ||
43 | - end | ||
44 | - | ||
45 | - def body_template | ||
46 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/fogbugz_body.txt.erb")) | ||
47 | - end | ||
48 | - | ||
49 | - def url | ||
50 | - "http://#{account}.fogbugz.com/" | ||
51 | - end | ||
52 | - end | ||
53 | -end |
app/models/issue_trackers/github_issues_tracker.rb
@@ -1,61 +0,0 @@ | @@ -1,61 +0,0 @@ | ||
1 | -if defined? Octokit | ||
2 | - class IssueTrackers::GithubIssuesTracker < IssueTracker | ||
3 | - Label = "github" | ||
4 | - Note = 'Please configure your github repository in the <strong>GITHUB REPO</strong> field above.<br/>' << | ||
5 | - 'Instead of providing your username & password, you can link your Github account ' << | ||
6 | - 'to your user profile, and allow Errbit to create issues using your OAuth token.' | ||
7 | - | ||
8 | - Fields = [ | ||
9 | - [:username, { | ||
10 | - :placeholder => "Your username on GitHub" | ||
11 | - }], | ||
12 | - [:password, { | ||
13 | - :placeholder => "Password for your account" | ||
14 | - }] | ||
15 | - ] | ||
16 | - | ||
17 | - attr_accessor :oauth_token | ||
18 | - | ||
19 | - def project_id | ||
20 | - app.github_repo | ||
21 | - end | ||
22 | - | ||
23 | - def check_params | ||
24 | - if Fields.detect {|f| self[f[0]].blank? } | ||
25 | - errors.add :base, 'You must specify your GitHub username and password' | ||
26 | - end | ||
27 | - end | ||
28 | - | ||
29 | - def create_issue(problem, reported_by = nil) | ||
30 | - # Login using OAuth token, if given. | ||
31 | - if oauth_token | ||
32 | - client = Octokit::Client.new(:login => username, :access_token => oauth_token) | ||
33 | - else | ||
34 | - client = Octokit::Client.new(:login => username, :password => password) | ||
35 | - end | ||
36 | - | ||
37 | - begin | ||
38 | - issue = client.create_issue( | ||
39 | - project_id, | ||
40 | - issue_title(problem), | ||
41 | - body_template.result(binding).unpack('C*').pack('U*') | ||
42 | - ) | ||
43 | - problem.update_attributes( | ||
44 | - :issue_link => issue.rels[:html].href, | ||
45 | - :issue_type => Label | ||
46 | - ) | ||
47 | - | ||
48 | - rescue Octokit::Unauthorized | ||
49 | - raise IssueTrackers::AuthenticationError, "Could not authenticate with GitHub. Please check your username and password." | ||
50 | - end | ||
51 | - end | ||
52 | - | ||
53 | - def body_template | ||
54 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/github_issues_body.txt.erb").gsub(/^\s*/, '')) | ||
55 | - end | ||
56 | - | ||
57 | - def url | ||
58 | - "https://github.com/#{project_id}/issues" | ||
59 | - end | ||
60 | - end | ||
61 | -end |
app/models/issue_trackers/gitlab_tracker.rb
@@ -1,53 +0,0 @@ | @@ -1,53 +0,0 @@ | ||
1 | -if defined? Gitlab | ||
2 | - class IssueTrackers::GitlabTracker < IssueTracker | ||
3 | - Label = "gitlab" | ||
4 | - Fields = [ | ||
5 | - [:account, { | ||
6 | - :label => "Gitlab URL", | ||
7 | - :placeholder => "e.g. https://example.net" | ||
8 | - }], | ||
9 | - [:api_token, { | ||
10 | - :placeholder => "API Token for your account" | ||
11 | - }], | ||
12 | - [:project_id, { | ||
13 | - :label => "Ticket Project ID (use Number)", | ||
14 | - :placeholder => "Gitlab Project where issues will be created" | ||
15 | - }], | ||
16 | - [:alt_project_id, { | ||
17 | - :label => "Project Name (namespace/project)", | ||
18 | - :placeholder => "Gitlab Project where issues will be created" | ||
19 | - }] | ||
20 | - ] | ||
21 | - | ||
22 | - def check_params | ||
23 | - if Fields.detect {|f| self[f[0]].blank?} | ||
24 | - errors.add :base, 'You must specify your Gitlab URL, API token, Project ID and Project Name' | ||
25 | - end | ||
26 | - end | ||
27 | - | ||
28 | - def create_issue(problem, reported_by = nil) | ||
29 | - Gitlab.configure do |config| | ||
30 | - config.endpoint = "#{account}/api/v3" | ||
31 | - config.private_token = api_token | ||
32 | - config.user_agent = 'Errbit User Agent' | ||
33 | - end | ||
34 | - title = issue_title problem | ||
35 | - description_summary = summary_template.result(binding) | ||
36 | - description_body = body_template.result(binding) | ||
37 | - ticket = Gitlab.create_issue(project_id, title, { :description => description_summary, :labels => "errbit" } ) | ||
38 | - Gitlab.create_issue_note(project_id, ticket.id, description_body) | ||
39 | - end | ||
40 | - | ||
41 | - def summary_template | ||
42 | - @@summary_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/gitlab_summary.txt.erb").gsub(/^\s*/, '')) | ||
43 | - end | ||
44 | - | ||
45 | - def body_template | ||
46 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/gitlab_body.txt.erb").gsub(/^\s*/, '')) | ||
47 | - end | ||
48 | - | ||
49 | - def url | ||
50 | - "#{account}/#{alt_project_id}/issues" | ||
51 | - end | ||
52 | - end | ||
53 | -end |
app/models/issue_trackers/jira_tracker.rb
@@ -1,110 +0,0 @@ | @@ -1,110 +0,0 @@ | ||
1 | -if defined? JIRA | ||
2 | - class IssueTrackers::JiraTracker < IssueTracker | ||
3 | - Label = 'jira' | ||
4 | - | ||
5 | - Fields = [ | ||
6 | - [:base_url, { | ||
7 | - :label => 'Jira URL without trailing slash', | ||
8 | - :placeholder => 'https://jira.example.org/' | ||
9 | - }], | ||
10 | - [:context_path, { | ||
11 | - :optional => true, | ||
12 | - :label => 'Context Path (Just "/" if empty otherwise with leading slash)', | ||
13 | - :placeholder => "/jira" | ||
14 | - }], | ||
15 | - [:username, { | ||
16 | - :optional => true, | ||
17 | - :label => 'HTTP Basic Auth User', | ||
18 | - :placeholder => 'johndoe' | ||
19 | - }], | ||
20 | - [:password, { | ||
21 | - :optional => true, | ||
22 | - :label => 'HTTP Basic Auth Password', | ||
23 | - :placeholder => 'p@assW0rd' | ||
24 | - }], | ||
25 | - [:project_id, { | ||
26 | - :label => 'Project Key', | ||
27 | - :placeholder => 'The project Key where the issue will be created' | ||
28 | - }], | ||
29 | - [:account, { | ||
30 | - :optional => true, | ||
31 | - :label => 'Assign to this user. If empty, Jira takes the project default.', | ||
32 | - :placeholder => "username" | ||
33 | - }], | ||
34 | - [:issue_component, { | ||
35 | - :label => 'Issue category', | ||
36 | - :placeholder => 'Website - Other' | ||
37 | - }], | ||
38 | - [:issue_type, { | ||
39 | - :label => 'Issue type', | ||
40 | - :placeholder => 'Bug' | ||
41 | - }], | ||
42 | - [:issue_priority, { | ||
43 | - :label => 'Priority', | ||
44 | - :placeholder => 'Normal' | ||
45 | - }] | ||
46 | - ] | ||
47 | - | ||
48 | - def check_params | ||
49 | - if Fields.detect { |f| self[f[0]].blank? && !f[1][:optional] } | ||
50 | - errors.add :base, 'You must specify all non optional values!' | ||
51 | - end | ||
52 | - end | ||
53 | - | ||
54 | - | ||
55 | - # | ||
56 | - # @param problem Problem | ||
57 | - def create_issue(problem, reported_by = nil) | ||
58 | - options = { | ||
59 | - :username => username, | ||
60 | - :password => password, | ||
61 | - :site => base_url, | ||
62 | - :context_path => context_path, | ||
63 | - :auth_type => :basic, | ||
64 | - :use_ssl => base_url.match(/^https/) ? true : false | ||
65 | - } | ||
66 | - client = JIRA::Client.new(options) | ||
67 | - | ||
68 | - issue = { | ||
69 | - :fields => { | ||
70 | - :project => { | ||
71 | - :key => project_id | ||
72 | - }, | ||
73 | - :summary => issue_title(problem), | ||
74 | - :description => body_template.result(binding), | ||
75 | - :issuetype => { | ||
76 | - :name => issue_type | ||
77 | - }, | ||
78 | - :priority => { | ||
79 | - :name => issue_priority, | ||
80 | - }, | ||
81 | - | ||
82 | - :components => [{:name => issue_component}] | ||
83 | - } | ||
84 | - } | ||
85 | - | ||
86 | - issue[:fields][:assignee] = {:name => account} if account | ||
87 | - | ||
88 | - issue_build = client.Issue.build | ||
89 | - issue_build.save(issue) | ||
90 | - issue_build.fetch | ||
91 | - | ||
92 | - problem.update_attributes( | ||
93 | - :issue_link => "#{base_url}#{context_path}browse/#{issue_build.key}", | ||
94 | - :issue_type => Label | ||
95 | - ) | ||
96 | - | ||
97 | - # Maybe in a later version? | ||
98 | - #remote_link = { | ||
99 | - # :url => app_problem_url(problem.app, problem), | ||
100 | - # :name => "Link to Errbit Issue" | ||
101 | - #} | ||
102 | - #remote_link_build = issue_build.remotelink.build | ||
103 | - #remote_link_build.save(remote_link) | ||
104 | - end | ||
105 | - | ||
106 | - def body_template | ||
107 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/jira_body.txt.erb")) | ||
108 | - end | ||
109 | - end | ||
110 | -end | ||
111 | \ No newline at end of file | 0 | \ No newline at end of file |
app/models/issue_trackers/lighthouse_tracker.rb
@@ -1,53 +0,0 @@ | @@ -1,53 +0,0 @@ | ||
1 | -if defined? Lighthouse | ||
2 | - class IssueTrackers::LighthouseTracker < IssueTracker | ||
3 | - Label = "lighthouseapp" | ||
4 | - Fields = [ | ||
5 | - [:account, { | ||
6 | - :label => "Subdomain", | ||
7 | - :placeholder => "subdomain from http://{{subdomain}}.lighthouseapp.com" | ||
8 | - }], | ||
9 | - [:api_token, { | ||
10 | - :label => "API Token", | ||
11 | - :placeholder => "123456789abcdef123456789abcdef" | ||
12 | - }], | ||
13 | - [:project_id, { | ||
14 | - :label => "Project ID", | ||
15 | - :placeholder => "123456" | ||
16 | - }] | ||
17 | - ] | ||
18 | - | ||
19 | - def check_params | ||
20 | - if Fields.detect {|f| self[f[0]].blank? } | ||
21 | - errors.add :base, 'You must specify your Lighthouseapp Subdomain, API token and Project ID' | ||
22 | - end | ||
23 | - end | ||
24 | - | ||
25 | - def create_issue(problem, reported_by = nil) | ||
26 | - Lighthouse.account = account | ||
27 | - Lighthouse.token = api_token | ||
28 | - # updating lighthouse account | ||
29 | - Lighthouse::Ticket.site | ||
30 | - Lighthouse::Ticket.format = :xml | ||
31 | - ticket = Lighthouse::Ticket.new(:project_id => project_id) | ||
32 | - ticket.title = issue_title problem | ||
33 | - | ||
34 | - ticket.body = body_template.result(binding) | ||
35 | - | ||
36 | - ticket.tags << "errbit" | ||
37 | - ticket.save! | ||
38 | - problem.update_attributes( | ||
39 | - :issue_link => "#{Lighthouse::Ticket.site.to_s.sub(/#{Lighthouse::Ticket.site.path}$/, '')}#{Lighthouse::Ticket.element_path(ticket.id, :project_id => project_id)}".sub(/\.xml$/, ''), | ||
40 | - :issue_type => Label | ||
41 | - ) | ||
42 | - | ||
43 | - end | ||
44 | - | ||
45 | - def body_template | ||
46 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/lighthouseapp_body.txt.erb").gsub(/^\s*/, '')) | ||
47 | - end | ||
48 | - | ||
49 | - def url | ||
50 | - "http://#{account}.lighthouseapp.com" | ||
51 | - end | ||
52 | - end | ||
53 | -end | ||
54 | \ No newline at end of file | 0 | \ No newline at end of file |
app/models/issue_trackers/mingle_tracker.rb
@@ -1,71 +0,0 @@ | @@ -1,71 +0,0 @@ | ||
1 | -class IssueTrackers::MingleTracker < IssueTracker | ||
2 | - Label = "mingle" | ||
3 | - Note = "Note: <strong>CARD PROPERTIES</strong> must be comma separated <em>key = value</em> pairs" | ||
4 | - | ||
5 | - Fields = [ | ||
6 | - [:account, { | ||
7 | - :label => "Mingle URL", | ||
8 | - :placeholder => "http://mingle.example.com/" | ||
9 | - }], | ||
10 | - [:project_id, { | ||
11 | - :placeholder => "Mingle project" | ||
12 | - }], | ||
13 | - [:ticket_properties, { | ||
14 | - :label => "Card Properties", | ||
15 | - :placeholder => "card_type = Defect, defect_status = Open, priority = Essential" | ||
16 | - }], | ||
17 | - [:username, { | ||
18 | - :label => "Sign-in name", | ||
19 | - :placeholder => "Sign-in name for your account" | ||
20 | - }], | ||
21 | - [:password, { | ||
22 | - :placeholder => "Password for your account" | ||
23 | - }] | ||
24 | - ] | ||
25 | - | ||
26 | - def check_params | ||
27 | - if Fields.detect {|f| self[f[0]].blank? } or !ticket_properties_hash["card_type"] | ||
28 | - errors.add :base, 'You must specify your Mingle URL, Project ID, Card Type (in default card properties), Sign-in name, and Password' | ||
29 | - end | ||
30 | - end | ||
31 | - | ||
32 | - def create_issue(problem, reported_by = nil) | ||
33 | - properties = ticket_properties_hash | ||
34 | - basic_auth = account.gsub(/https?:\/\//, "https://#{username}:#{password}@") | ||
35 | - Mingle.set_site "#{basic_auth}/api/v1/projects/#{project_id}/" | ||
36 | - | ||
37 | - card = Mingle::Card.new | ||
38 | - card.card_type_name = properties.delete("card_type") | ||
39 | - card.name = issue_title(problem) | ||
40 | - card.description = body_template.result(binding) | ||
41 | - properties.each do |property, value| | ||
42 | - card.send("cp_#{property}=", value) | ||
43 | - end | ||
44 | - | ||
45 | - card.save! | ||
46 | - problem.update_attributes( | ||
47 | - :issue_link => URI.parse("#{account}/projects/#{project_id}/cards/#{card.id}").to_s, | ||
48 | - :issue_type => Label | ||
49 | - ) | ||
50 | - end | ||
51 | - | ||
52 | - def body_template | ||
53 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/textile_body.txt.erb")) | ||
54 | - end | ||
55 | - | ||
56 | - def ticket_properties_hash | ||
57 | - # Parses 'key=value, key2=value2' from ticket_properties into a ruby hash. | ||
58 | - self.ticket_properties.to_s.split(",").inject({}) do |hash, pair| | ||
59 | - key, value = pair.split("=").map(&:strip) | ||
60 | - hash[key] = value | ||
61 | - hash | ||
62 | - end | ||
63 | - end | ||
64 | - | ||
65 | - def url | ||
66 | - acc_url = account.start_with?('http') ? account : "http://#{account}" | ||
67 | - URI.parse("#{acc_url}/projects/#{project_id}").to_s | ||
68 | - rescue URI::InvalidURIError | ||
69 | - end | ||
70 | -end | ||
71 | - |
app/models/issue_trackers/pivotal_labs_tracker.rb
@@ -1,43 +0,0 @@ | @@ -1,43 +0,0 @@ | ||
1 | -if defined? PivotalTracker | ||
2 | - class IssueTrackers::PivotalLabsTracker < IssueTracker | ||
3 | - Label = "pivotal" | ||
4 | - Fields = [ | ||
5 | - [:api_token, { | ||
6 | - :placeholder => "API Token for your account" | ||
7 | - }], | ||
8 | - [:project_id, {}] | ||
9 | - ] | ||
10 | - | ||
11 | - def check_params | ||
12 | - if Fields.detect {|f| self[f[0]].blank? } | ||
13 | - errors.add :base, 'You must specify your Pivotal Tracker API token and Project ID' | ||
14 | - end | ||
15 | - end | ||
16 | - | ||
17 | - def create_issue(problem, reported_by = nil) | ||
18 | - PivotalTracker::Client.token = api_token | ||
19 | - PivotalTracker::Client.use_ssl = true | ||
20 | - project = PivotalTracker::Project.find project_id.to_i | ||
21 | - story = project.stories.create :name => issue_title(problem), | ||
22 | - :story_type => 'bug', :description => body_template.result(binding), | ||
23 | - :requested_by => reported_by.name | ||
24 | - | ||
25 | - if story.errors.present? | ||
26 | - raise IssueTrackers::IssueTrackerError, story.errors.first | ||
27 | - else | ||
28 | - problem.update_attributes( | ||
29 | - :issue_link => "https://www.pivotaltracker.com/story/show/#{story.id}", | ||
30 | - :issue_type => Label | ||
31 | - ) | ||
32 | - end | ||
33 | - end | ||
34 | - | ||
35 | - def body_template | ||
36 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/pivotal_body.txt.erb")) | ||
37 | - end | ||
38 | - | ||
39 | - def url | ||
40 | - "https://www.pivotaltracker.com/" | ||
41 | - end | ||
42 | - end | ||
43 | -end | ||
44 | \ No newline at end of file | 0 | \ No newline at end of file |
app/models/issue_trackers/redmine_tracker.rb
@@ -1,75 +0,0 @@ | @@ -1,75 +0,0 @@ | ||
1 | -if defined? RedmineClient | ||
2 | - class IssueTrackers::RedmineTracker < IssueTracker | ||
3 | - Label = "redmine" | ||
4 | - Fields = [ | ||
5 | - [:account, { | ||
6 | - :label => "Redmine URL", | ||
7 | - :placeholder => "http://www.redmine.org/" | ||
8 | - }], | ||
9 | - [:api_token, { | ||
10 | - :placeholder => "API Token for your account" | ||
11 | - }], | ||
12 | - [:username, { | ||
13 | - :placeholder => "Your username" | ||
14 | - }], | ||
15 | - [:password, { | ||
16 | - :placeholder => "Your password" | ||
17 | - }], | ||
18 | - [:project_id, { | ||
19 | - :label => "Ticket Project", | ||
20 | - :placeholder => "Redmine Project where tickets will be created" | ||
21 | - }], | ||
22 | - [:alt_project_id, { | ||
23 | - :optional => true, | ||
24 | - :label => "App Project", | ||
25 | - :placeholder => "Where app's files & revisions can be viewed. (Leave blank to use the above project by default)" | ||
26 | - }] | ||
27 | - ] | ||
28 | - | ||
29 | - def check_params | ||
30 | - if Fields.detect {|f| self[f[0]].blank? && !f[1][:optional]} | ||
31 | - errors.add :base, 'You must specify your Redmine URL, API token, Username, Password and Project ID' | ||
32 | - end | ||
33 | - end | ||
34 | - | ||
35 | - def create_issue(problem, reported_by = nil) | ||
36 | - token = api_token | ||
37 | - acc = account | ||
38 | - user = username | ||
39 | - passwd = password | ||
40 | - RedmineClient::Base.configure do | ||
41 | - self.token = token | ||
42 | - self.user = user | ||
43 | - self.password = passwd | ||
44 | - self.site = acc | ||
45 | - self.format = :xml | ||
46 | - end | ||
47 | - issue = RedmineClient::Issue.new(:project_id => project_id) | ||
48 | - issue.subject = issue_title problem | ||
49 | - issue.description = body_template.result(binding) | ||
50 | - issue.save! | ||
51 | - problem.update_attributes( | ||
52 | - :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}"), | ||
53 | - :issue_type => Label | ||
54 | - ) | ||
55 | - end | ||
56 | - | ||
57 | - def url_to_file(file_path, line_number = nil) | ||
58 | - # alt_project_id let's users specify a different project for tickets / app files. | ||
59 | - project = self.alt_project_id.present? ? self.alt_project_id : self.project_id | ||
60 | - url = "#{self.account.gsub(/\/$/, '')}/projects/#{project}/repository/revisions/#{app.repository_branch}/entry/#{file_path.sub(/\[PROJECT_ROOT\]/, '').sub(/^\//,'')}" | ||
61 | - line_number ? url << "#L#{line_number}" : url | ||
62 | - end | ||
63 | - | ||
64 | - def body_template | ||
65 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/redmine_body.txt.erb")) | ||
66 | - end | ||
67 | - | ||
68 | - def url | ||
69 | - acc_url = account.start_with?('http') ? account : "http://#{account}" | ||
70 | - acc_url = acc_url.gsub(/\/$/, '') | ||
71 | - URI.parse("#{acc_url}/projects/#{project_id}").to_s | ||
72 | - rescue URI::InvalidURIError | ||
73 | - end | ||
74 | - end | ||
75 | -end |
app/models/issue_trackers/unfuddle_tracker.rb
@@ -1,69 +0,0 @@ | @@ -1,69 +0,0 @@ | ||
1 | -class IssueTrackers::UnfuddleTracker < IssueTracker | ||
2 | - Label = "unfuddle" | ||
3 | - Fields = [ | ||
4 | - | ||
5 | - [:account, { | ||
6 | - :placeholder => "Your domain" | ||
7 | - }], | ||
8 | - | ||
9 | - | ||
10 | - [:username, { | ||
11 | - :placeholder => "Your username" | ||
12 | - }], | ||
13 | - | ||
14 | - [:password, { | ||
15 | - :placeholder => "Your password" | ||
16 | - }], | ||
17 | - | ||
18 | - [:project_id, { | ||
19 | - :label => "Ticket Project", | ||
20 | - :placeholder => "Project where tickets will be created" | ||
21 | - }], | ||
22 | - | ||
23 | - [:milestone_id, { | ||
24 | - :optional => true, | ||
25 | - :label => "Ticket Milestone", | ||
26 | - :placeholder => "Milestone where tickets will be created" | ||
27 | - }] | ||
28 | - | ||
29 | - | ||
30 | - ] | ||
31 | - | ||
32 | - def check_params | ||
33 | - if Fields.detect {|f| self[f[0]].blank? && !f[1][:optional]} | ||
34 | - errors.add :base, 'You must specify your Account, Username, Password and Project ID' | ||
35 | - end | ||
36 | - end | ||
37 | - | ||
38 | - def create_issue(problem, reported_by = nil) | ||
39 | - | ||
40 | - Unfuddle.config(account, username, password) | ||
41 | - begin | ||
42 | - issue_options = {:project_id => project_id, | ||
43 | - :summary => issue_title(problem), | ||
44 | - :priority => '5', | ||
45 | - :status => "new", | ||
46 | - :description => body_template.result(binding), | ||
47 | - 'description-format' => 'textile' } | ||
48 | - | ||
49 | - issue_options[:milestone_id] = milestone_id if milestone_id.present? | ||
50 | - | ||
51 | - issue = Unfuddle::Ticket.create(issue_options) | ||
52 | - problem.update_attributes( | ||
53 | - :issue_link => File.join("#{url}/tickets/#{issue.id}"), | ||
54 | - :issue_type => Label | ||
55 | - ) | ||
56 | - rescue ActiveResource::UnauthorizedAccess | ||
57 | - raise ActiveResource::UnauthorizedAccess, "Could not authenticate with Unfuddle. Please check your username and password." | ||
58 | - end | ||
59 | - | ||
60 | - end | ||
61 | - | ||
62 | - def body_template | ||
63 | - @@body_template ||= ERB.new(File.read(Rails.root + "app/views/issue_trackers/textile_body.txt.erb")) | ||
64 | - end | ||
65 | - | ||
66 | - def url | ||
67 | - "https://#{account}.unfuddle.com/projects/#{project_id}" | ||
68 | - end | ||
69 | -end |
app/models/problem.rb
@@ -62,12 +62,19 @@ class Problem | @@ -62,12 +62,19 @@ class Problem | ||
62 | env.present? ? where(:environment => env) : scoped | 62 | env.present? ? where(:environment => env) : scoped |
63 | end | 63 | end |
64 | 64 | ||
65 | + def url | ||
66 | + Rails.application.routes.url_helpers.app_problem_url(app, self, | ||
67 | + :host => Errbit::Config.host, | ||
68 | + :port => Errbit::Config.port | ||
69 | + ) | ||
70 | + end | ||
71 | + | ||
65 | def notices | 72 | def notices |
66 | Notice.for_errs(errs).ordered | 73 | Notice.for_errs(errs).ordered |
67 | end | 74 | end |
68 | 75 | ||
69 | def comments_allowed? | 76 | def comments_allowed? |
70 | - Errbit::Config.allow_comments_with_issue_tracker || !app.issue_tracker_configured? | 77 | + Errbit::Config.allow_comments_with_issue_tracker || app.comments_allowed? |
71 | end | 78 | end |
72 | 79 | ||
73 | def resolve! | 80 | def resolve! |
@@ -148,7 +155,7 @@ class Problem | @@ -148,7 +155,7 @@ class Problem | ||
148 | def issue_type | 155 | def issue_type |
149 | # Return issue_type if configured, but fall back to detecting app's issue tracker | 156 | # Return issue_type if configured, but fall back to detecting app's issue tracker |
150 | attributes['issue_type'] ||= | 157 | attributes['issue_type'] ||= |
151 | - (app.issue_tracker_configured? && app.issue_tracker.label) || nil | 158 | + (app.issue_tracker_configured? && app.issue_tracker.type_tracker) || nil |
152 | end | 159 | end |
153 | 160 | ||
154 | def self.search(value) | 161 | def self.search(value) |
app/views/apps/_fields.html.haml
@@ -32,9 +32,9 @@ | @@ -32,9 +32,9 @@ | ||
32 | = f.check_box :notify_on_errs, 'data-show-when-checked' => '.email_at_notices_nested' | 32 | = f.check_box :notify_on_errs, 'data-show-when-checked' => '.email_at_notices_nested' |
33 | = f.label :notify_on_errs, 'Notify on errors' | 33 | = f.label :notify_on_errs, 'Notify on errors' |
34 | - if Errbit::Config.per_app_email_at_notices | 34 | - if Errbit::Config.per_app_email_at_notices |
35 | - %div.email_at_notices_nested{:style => f.object.notify_on_errs ? '' : 'display: none;'} | 35 | + %div.email_at_notices_nested{:style => app_decorate.notify_err_display} |
36 | .field-helpertext Send a notification every | 36 | .field-helpertext Send a notification every |
37 | - = f.text_field :email_at_notices, :value => f.object.email_at_notices.join(", ") | 37 | + = f.text_field :email_at_notices, :value => app_decorate.email_at_notices |
38 | .field-helpertext times an error occurs (comma separated). | 38 | .field-helpertext times an error occurs (comma separated). |
39 | %div.checkbox | 39 | %div.checkbox |
40 | = f.check_box :notify_on_deploys | 40 | = f.check_box :notify_on_deploys |
@@ -45,13 +45,13 @@ | @@ -45,13 +45,13 @@ | ||
45 | = f.label :notify_all_users, 'Send notifications to all users' | 45 | = f.label :notify_all_users, 'Send notifications to all users' |
46 | 46 | ||
47 | 47 | ||
48 | -%fieldset.watchers.nested-wrapper{:style => f.object.notify_all_users ? 'display: none;' : ''} | 48 | +%fieldset.watchers.nested-wrapper{:style => app_decorate.notify_user_display} |
49 | %legend Watchers | 49 | %legend Watchers |
50 | = f.fields_for :watchers do |w| | 50 | = f.fields_for :watchers do |w| |
51 | %div.watcher.nested | 51 | %div.watcher.nested |
52 | %div.choose | 52 | %div.choose |
53 | = w.radio_button :watcher_type, :user | 53 | = w.radio_button :watcher_type, :user |
54 | - = label_tag :watcher_type_user, 'User', :for => label_for_attr(w, 'watcher_type_user') | 54 | + = w.label :watcher_type_user, 'User' |
55 | = w.radio_button :watcher_type, :email | 55 | = w.radio_button :watcher_type, :email |
56 | = label_tag :watcher_type_email, 'Email Address', :for => label_for_attr(w, 'watcher_type_email') | 56 | = label_tag :watcher_type_email, 'Email Address', :for => label_for_attr(w, 'watcher_type_email') |
57 | %div.watcher_params.user{:class => w.object.email.blank? ? 'chosen' : nil} | 57 | %div.watcher_params.user{:class => w.object.email.blank? ? 'chosen' : nil} |
app/views/apps/_issue_tracker_fields.html.haml
1 | %fieldset | 1 | %fieldset |
2 | - %legend Issue tracker | ||
3 | - = f.fields_for :issue_tracker do |w| | 2 | + %legend= t('.legend') |
3 | + = f.fields_for :issue_tracker do |issue_tracker_form| | ||
4 | %div.issue_tracker.nested | 4 | %div.issue_tracker.nested |
5 | %div.choose | 5 | %div.choose |
6 | - = label_tag :type_none, :for => label_for_attr(w, 'type_issuetracker'), :class => "label_radio none" do | ||
7 | - = w.radio_button :type, "IssueTracker", 'data-section' => 'none' | ||
8 | - (None) | ||
9 | - - IssueTracker.subclasses.each do |tracker| | ||
10 | - = label_tag "type_#{tracker.label}:", :for => label_for_attr(w, "type_#{tracker.name.downcase.gsub(':','')}"), :class => "label_radio #{tracker.label}" do | ||
11 | - = w.radio_button :type, tracker.name, 'data-section' => tracker.label | ||
12 | - = tracker.name[/::(.*)Tracker/,1].titleize | 6 | + - issue_tracker_form.object.issue_trackers do |tracker| |
7 | + = issue_tracker_form.label :type_tracker, :class => "label_radio #{tracker.label}", :value => tracker.key do | ||
8 | + = issue_tracker_form.radio_button :type_tracker, tracker.key, 'data-section' => tracker.label | ||
9 | + = tracker.label | ||
13 | 10 | ||
14 | - %div.tracker_params.none{:class => (w.object && !(w.object.class < IssueTracker)) ? 'chosen' : nil} | ||
15 | - %p When no issue tracker has been configured, you will be able to leave comments on errors. | ||
16 | - - IssueTracker.subclasses.each do |tracker| | ||
17 | - %div.tracker_params{:class => (w.object.is_a?(tracker) ? 'chosen ' : '') << tracker.label} | ||
18 | - - if defined?(tracker::Note) | ||
19 | - %p= tracker::Note.html_safe | ||
20 | - - tracker::Fields.each do |field, field_info| | ||
21 | - = w.label field, field_info[:label] || field.to_s.titleize | ||
22 | - - field_type = field == :password ? :password_field : :text_field | ||
23 | - = w.send field_type, field, :placeholder => field_info[:placeholder], :value => w.object.send(field) | 11 | + - issue_tracker_form.object.issue_trackers do |tracker| |
12 | + %div.tracker_params{:class => tracker.params_class(issue_tracker_form.object)} | ||
13 | + = issue_tracker_form.fields_for(:options) do |options_form| | ||
14 | + %p= tracker.note | ||
15 | + - tracker.fields do |field| | ||
16 | + = options_form.label field.key, field.label | ||
17 | + = field.input(options_form, issue_tracker_form.object) | ||
24 | 18 | ||
25 | .image_preloader | 19 | .image_preloader |
26 | - - (IssueTracker.subclasses.map{|t| t.label } << 'none').each do |tracker| | ||
27 | - = image_tag "#{tracker}_inactive.png" | ||
28 | - = image_tag "#{tracker}_create.png" | 20 | + - issue_tracker_form.object.issue_trackers do |tracker| |
21 | + = image_tag "#{tracker.label}_inactive.png" | ||
22 | + = image_tag "#{tracker.label}_create.png" | ||
29 | 23 |
app/views/apps/edit.html.haml
1 | -- content_for :title, 'Edit App' | 1 | +- content_for :title, t('.title') |
2 | - content_for :action_bar do | 2 | - content_for :action_bar do |
3 | = link_to_copy_attributes_from_other_app | 3 | = link_to_copy_attributes_from_other_app |
4 | = link_to 'delete all errs', destroy_all_app_problems_path(app), :method => :post, | 4 | = link_to 'delete all errs', destroy_all_app_problems_path(app), :method => :post, |
5 | :data => { :confirm => t('apps.confirm_destroy_all_problems') }, :class => 'button' | 5 | :data => { :confirm => t('apps.confirm_destroy_all_problems') }, :class => 'button' |
6 | = link_to 'delete application', app_path(app), :method => :delete, | 6 | = link_to 'delete application', app_path(app), :method => :delete, |
7 | :data => { :confirm => t('apps.confirm_delete') }, :class => 'button' | 7 | :data => { :confirm => t('apps.confirm_delete') }, :class => 'button' |
8 | - = link_to('cancel', app_path(app), :class => 'button') | 8 | + = link_to(t('.cancel'), app_path(app), :class => 'button') |
9 | 9 | ||
10 | -= form_for app do |f| | 10 | += form_for app_decorate do |f| |
11 | 11 | ||
12 | = render 'fields', :f => f | 12 | = render 'fields', :f => f |
13 | 13 | ||
14 | - %div.buttons= f.submit 'Update App' | 14 | + %div.buttons= f.submit t('.update') |
15 | 15 |
app/views/apps/index.html.haml
@@ -36,7 +36,7 @@ | @@ -36,7 +36,7 @@ | ||
36 | - if any_issue_trackers? | 36 | - if any_issue_trackers? |
37 | %td.issue_tracker | 37 | %td.issue_tracker |
38 | - if app.issue_tracker_configured? | 38 | - if app.issue_tracker_configured? |
39 | - - tracker_img = image_tag("#{app.issue_tracker.label}_goto.png") | 39 | + - tracker_img = image_tag("#{app.issue_tracker.type_tracker}_goto.png") |
40 | - if app.issue_tracker.url | 40 | - if app.issue_tracker.url |
41 | = link_to( tracker_img, app.issue_tracker.url ) | 41 | = link_to( tracker_img, app.issue_tracker.url ) |
42 | - else | 42 | - else |
app/views/apps/new.html.haml
1 | -- content_for :title, 'Add App' | 1 | +- content_for :title, t('.title') |
2 | - content_for :action_bar do | 2 | - content_for :action_bar do |
3 | = link_to_copy_attributes_from_other_app | 3 | = link_to_copy_attributes_from_other_app |
4 | - = link_to('cancel', apps_path, :class => 'button') | 4 | + = link_to(t('.cancel'), apps_path, :class => 'button') |
5 | 5 | ||
6 | -= form_for app do |f| | 6 | += form_for app_decorate do |f| |
7 | 7 | ||
8 | = render 'fields', :f => f | 8 | = render 'fields', :f => f |
9 | 9 | ||
10 | - %div.buttons= f.submit 'Add App' | 10 | + %div.buttons= f.submit t('.add_app') |
11 | 11 |
app/views/problems/_issue_tracker_links.html.haml
@@ -6,10 +6,10 @@ | @@ -6,10 +6,10 @@ | ||
6 | %span.disabled= link_to 'creating...', '#', :class => "#{problem.issue_type}_inactive create-issue" | 6 | %span.disabled= link_to 'creating...', '#', :class => "#{problem.issue_type}_inactive create-issue" |
7 | = link_to 'retry', create_issue_app_problem_path(app, problem), :method => :post | 7 | = link_to 'retry', create_issue_app_problem_path(app, problem), :method => :post |
8 | - else | 8 | - else |
9 | - - if app.github_repo? | 9 | + - if app.issue_tracker_configured? && !app.issue_tracker.type_tracker.eql?('github') |
10 | + %span= link_to 'create issue', create_issue_app_problem_path(app, problem), :method => :post, :class => "#{app.issue_tracker.type_tracker}_create create-issue" | ||
11 | + - elsif app.github_repo? | ||
10 | - if current_user.can_create_github_issues? | 12 | - if current_user.can_create_github_issues? |
11 | %span= link_to 'create issue', create_issue_app_problem_path(app, problem, :tracker => 'user_github'), :method => :post, :class => "github_create create-issue" | 13 | %span= link_to 'create issue', create_issue_app_problem_path(app, problem, :tracker => 'user_github'), :method => :post, :class => "github_create create-issue" |
12 | - - elsif app.issue_tracker_configured? && app.issue_tracker.label.eql?('github') | 14 | + - elsif app.issue_tracker_configured? && app.issue_tracker.type_tracker.eql?('github') |
13 | %span= link_to 'create issue', create_issue_app_problem_path(app, problem), :method => :post, :class => "github_create create-issue" | 15 | %span= link_to 'create issue', create_issue_app_problem_path(app, problem), :method => :post, :class => "github_create create-issue" |
14 | - - if app.issue_tracker_configured? && !app.issue_tracker.label.eql?('github') | ||
15 | - %span= link_to 'create issue', create_issue_app_problem_path(app, problem), :method => :post, :class => "#{app.issue_tracker.label}_create create-issue" |
config/application.rb
@@ -9,6 +9,8 @@ require "action_mailer/railtie" | @@ -9,6 +9,8 @@ require "action_mailer/railtie" | ||
9 | require 'mongoid/railtie' | 9 | require 'mongoid/railtie' |
10 | require "sprockets/railtie" | 10 | require "sprockets/railtie" |
11 | 11 | ||
12 | +require 'draper' | ||
13 | + | ||
12 | if defined?(Bundler) | 14 | if defined?(Bundler) |
13 | # If you precompile assets before deploying to production, use this line | 15 | # If you precompile assets before deploying to production, use this line |
14 | Bundler.require(*Rails.groups(:assets => %w(development test))) | 16 | Bundler.require(*Rails.groups(:assets => %w(development test))) |
config/initializers/issue_trackers.rb
@@ -1,7 +0,0 @@ | @@ -1,7 +0,0 @@ | ||
1 | -# Require all issue tracker apis in lib/issue_tracker_apis | ||
2 | -Dir.glob(Rails.root.join('lib/issue_trackers/apis/*.rb')).each {|t| require t } | ||
3 | -# Require issue tracker error classes | ||
4 | -require Rails.root.join('lib/issue_trackers/errors') | ||
5 | - | ||
6 | -# Include nested issue tracker models | ||
7 | -include IssueTrackers |
config/locales/en.yml
@@ -127,4 +127,16 @@ en: | @@ -127,4 +127,16 @@ en: | ||
127 | watchers: Watchers | 127 | watchers: Watchers |
128 | when: When | 128 | when: When |
129 | who: Who | 129 | who: Who |
130 | + issue_tracker_fields: | ||
131 | + legend: Issue Tracker | ||
132 | + new: | ||
133 | + add_app: "Add App" | ||
134 | + cancel: "cancel" | ||
135 | + title: "Add App" | ||
136 | + edit: | ||
137 | + title: 'Edit App' | ||
138 | + destroy: 'destroy application' | ||
139 | + cancel: 'cancel' | ||
140 | + seriously: 'Seriously?' | ||
141 | + update: 'Update App' | ||
130 | 142 |
db/migrate/20110812135951_move_issue_trackers_to_sti.rb
@@ -5,21 +5,27 @@ class MoveIssueTrackersToSti < Mongoid::Migration | @@ -5,21 +5,27 @@ class MoveIssueTrackersToSti < Mongoid::Migration | ||
5 | # All issue trackers now subclass the IssueTracker model, | 5 | # All issue trackers now subclass the IssueTracker model, |
6 | # and their class is stored in the '_type' field, which is | 6 | # and their class is stored in the '_type' field, which is |
7 | # also aliased to 'type'. | 7 | # also aliased to 'type'. |
8 | - if app.issue_tracker && app.issue_tracker.attributes["issue_tracker_type"] | ||
9 | - app.issue_tracker._type = case app.issue_tracker.issue_tracker_type | ||
10 | - when 'lighthouseapp'; "LighthouseTracker" | ||
11 | - when 'redmine'; "RedmineTracker" | ||
12 | - when 'pivotal'; "PivotalLabsTracker" | ||
13 | - when 'fogbugz'; "FogbugzTracker" | ||
14 | - when 'mingle'; "MingleTracker" | 8 | + tracker = app.attributes['issue_tracker'] |
9 | + if tracker && tracker['issue_tracker_type'] | ||
10 | + tracker['_type'] = case tracker['issue_tracker_type'] | ||
11 | + when 'lighthouseapp'; "IssueTrackers::LighthouseTracker" | ||
12 | + when 'redmine'; "IssueTrackers::RedmineTracker" | ||
13 | + when 'pivotal'; "IssueTrackers::PivotalLabsTracker" | ||
14 | + when 'fogbugz'; "IssueTrackers::FogbugzTracker" | ||
15 | + when 'mingle'; "IssueTrackers::MingleTracker" | ||
15 | else; nil | 16 | else; nil |
16 | end | 17 | end |
17 | - if app.issue_tracker.issue_tracker_type == "none" | ||
18 | - app.issue_tracker = nil | 18 | + |
19 | + if tracker['issue_tracker_type'] == "none" | ||
20 | + App.collection.where({ _id: app.id }).update({ | ||
21 | + "$unset" => { :issue_tracker => 1 } | ||
22 | + }) | ||
19 | else | 23 | else |
20 | - app.issue_tracker.issue_tracker_type = nil | 24 | + tracker.delete('issue_tracker_type') |
25 | + App.collection.where({ _id: app.id }).update({ | ||
26 | + "$set" => { :issue_tracker => tracker } | ||
27 | + }) | ||
21 | end | 28 | end |
22 | - app.save | ||
23 | end | 29 | end |
24 | end | 30 | end |
25 | end | 31 | end |
db/migrate/20120603112130_change_github_url_to_github_repo.rb
1 | class ChangeGithubUrlToGithubRepo < Mongoid::Migration | 1 | class ChangeGithubUrlToGithubRepo < Mongoid::Migration |
2 | + def self.normalize_github_repo(repo) | ||
3 | + return if repo.blank? | ||
4 | + github_host = URI.parse(Errbit::Config.github_url).host | ||
5 | + github_host = Regexp.escape(github_host) | ||
6 | + repo.strip! | ||
7 | + repo.sub!(/(git@|https?:\/\/)#{github_host}(\/|:)/, '') | ||
8 | + repo.sub!(/\.git$/, '') | ||
9 | + repo | ||
10 | + end | ||
11 | + | ||
2 | def self.up | 12 | def self.up |
3 | App.collection.find.update({'$rename' => {'github_url' => 'github_repo'}}, :multi => true, :safe => true) | 13 | App.collection.find.update({'$rename' => {'github_url' => 'github_repo'}}, :multi => true, :safe => true) |
4 | App.all.each do |app| | 14 | App.all.each do |app| |
5 | - app.send :normalize_github_repo | ||
6 | - app.save | 15 | + normalized_repo = self.normalize_github_repo(app.attributes['github_repo']) |
16 | + App.collection.where({ _id: app.id }).update({ | ||
17 | + "$set" => { :github_repo => normalized_repo } | ||
18 | + }) | ||
7 | end | 19 | end |
8 | end | 20 | end |
9 | 21 |
@@ -0,0 +1,48 @@ | @@ -0,0 +1,48 @@ | ||
1 | +class ExtractIssueTracker < Mongoid::Migration | ||
2 | + | ||
3 | + TRACKER_MAPPING = { | ||
4 | + 'ErrbitTracPlugin::IssueTracker' => 'trac', | ||
5 | + 'IssueTrackers::BitbucketIssuesTracker' => 'bitbucket', | ||
6 | + 'IssueTrackers::FogbugzTracker' => 'fogbugz', | ||
7 | + 'IssueTrackers::GithubIssuesTracker' => 'github', | ||
8 | + 'IssueTrackers::GitlabTracker' => 'gitlab', | ||
9 | + 'IssueTrackers::JiraTracker' => 'jira', | ||
10 | + 'IssueTrackers::LighthouseTracker' => 'lighthouse', | ||
11 | + 'IssueTrackers::PivotalLabsTracker' => 'pivotal', | ||
12 | + 'IssueTrackers::RedmineTracker' => 'redmine', | ||
13 | + 'IssueTrackers::UnfuddleTracker' => 'unfuddle' | ||
14 | + } | ||
15 | + | ||
16 | + def self.up | ||
17 | + App.all.each do |app| | ||
18 | + require 'pry' | ||
19 | + binding.pry | ||
20 | + next unless app.attributes['issue_tracker'].present? | ||
21 | + next unless app.attributes['issue_tracker']['_type'].present? | ||
22 | + | ||
23 | + options = app['issue_tracker'].dup | ||
24 | + options.delete('_type') | ||
25 | + options.delete('_id') | ||
26 | + | ||
27 | + _type = app.attributes['issue_tracker']['_type'] | ||
28 | + updated_at = options.delete('updated_at') | ||
29 | + created_at = options.delete('created_at') | ||
30 | + | ||
31 | + if TRACKER_MAPPING.include?(_type) | ||
32 | + tracker = { | ||
33 | + 'type_tracker' => TRACKER_MAPPING[_type], | ||
34 | + 'options' => options, | ||
35 | + 'updated_at' => updated_at, | ||
36 | + 'created_at' => created_at | ||
37 | + } | ||
38 | + | ||
39 | + App.collection.where({ _id: app.id }).update({ | ||
40 | + "$set" => { :issue_tracker => tracker } | ||
41 | + }) | ||
42 | + end | ||
43 | + end | ||
44 | + end | ||
45 | + | ||
46 | + def self.down | ||
47 | + end | ||
48 | +end |
docs/DEVELOPER-ADVANCED.md
@@ -20,3 +20,12 @@ After you just need launch the script with adapting runner of mongoid. | @@ -20,3 +20,12 @@ After you just need launch the script with adapting runner of mongoid. | ||
20 | 20 | ||
21 | In my case, the complete test suite down to 2min after a 16min long | 21 | In my case, the complete test suite down to 2min after a 16min long |
22 | before. | 22 | before. |
23 | + | ||
24 | +## Avoid running acceptance test with phantomjs | ||
25 | + | ||
26 | +Some acceptance test use phantomjs to interpret the Javascript in page. | ||
27 | +To avoid this test you can launch your test by skipping js tag | ||
28 | + | ||
29 | +``` | ||
30 | +bundle exec rspec spec --tag="~js" | ||
31 | +``` |
lib/issue_trackers/apis/mingle.rb
@@ -1,17 +0,0 @@ | @@ -1,17 +0,0 @@ | ||
1 | -require 'active_resource' | ||
2 | - | ||
3 | -module Mingle | ||
4 | - class Card < ActiveResource::Base | ||
5 | - # site template ~> "https://username:password@mingle.example.com/api/v1/projects/:project_id/" | ||
6 | - self.format = :xml | ||
7 | - end | ||
8 | - def self.set_site(site) | ||
9 | - # ActiveResource seems to clone and freeze the @site variable | ||
10 | - # after the first use. It seems that the only way to change @site | ||
11 | - # is to drop the subclass, and then reload it. | ||
12 | - Mingle.send(:remove_const, :Card) | ||
13 | - load File.join(Rails.root,'lib','issue_trackers', 'apis','mingle.rb') | ||
14 | - Mingle::Card.site = site | ||
15 | - end | ||
16 | -end | ||
17 | - |
lib/issue_trackers/apis/unfuddle.rb
@@ -1,13 +0,0 @@ | @@ -1,13 +0,0 @@ | ||
1 | -require 'active_resource' | ||
2 | - | ||
3 | -module Unfuddle | ||
4 | - class Ticket < ActiveResource::Base | ||
5 | - self.format = :xml | ||
6 | - end | ||
7 | - | ||
8 | - def self.config(account, username, password) | ||
9 | - Unfuddle::Ticket.site = "https://#{account}.unfuddle.com/api/v1/projects/:project_id" | ||
10 | - Unfuddle::Ticket.user = username | ||
11 | - Unfuddle::Ticket.password = password | ||
12 | - end | ||
13 | -end |
lib/issue_trackers/errors.rb
spec/acceptance/acceptance_helper.rb
spec/acceptance/app_regenerate_api_key_spec.rb
1 | require 'acceptance/acceptance_helper' | 1 | require 'acceptance/acceptance_helper' |
2 | 2 | ||
3 | feature "Regeneration api_Key" do | 3 | feature "Regeneration api_Key" do |
4 | - let!(:app) { Fabricate(:app) } | ||
5 | - let!(:admin) { Fabricate(:admin) } | 4 | + let(:app) { Fabricate(:app) } |
5 | + let(:admin) { Fabricate(:admin) } | ||
6 | let(:user) { | 6 | let(:user) { |
7 | Fabricate(:user_watcher, :app => app).user | 7 | Fabricate(:user_watcher, :app => app).user |
8 | } | 8 | } |
9 | 9 | ||
10 | + before do | ||
11 | + app && admin | ||
12 | + end | ||
13 | + | ||
10 | scenario "an admin change api_key" do | 14 | scenario "an admin change api_key" do |
11 | visit '/' | 15 | visit '/' |
12 | log_in admin | 16 | log_in admin |
@@ -28,5 +32,65 @@ feature "Regeneration api_Key" do | @@ -28,5 +32,65 @@ feature "Regeneration api_Key" do | ||
28 | click_link app.name if page.current_url != app_url(app) | 32 | click_link app.name if page.current_url != app_url(app) |
29 | expect(page).to_not have_button I18n.t('apps.show.edit') | 33 | expect(page).to_not have_button I18n.t('apps.show.edit') |
30 | end | 34 | end |
35 | +end | ||
36 | + | ||
37 | +feature "Create an application" do | ||
38 | + | ||
39 | + let(:admin) { Fabricate(:admin) } | ||
40 | + let(:user) { | ||
41 | + Fabricate(:user_watcher, :app => app).user | ||
42 | + } | ||
43 | + | ||
44 | + before do | ||
45 | + admin | ||
46 | + end | ||
31 | 47 | ||
48 | + scenario "create an apps without issue tracker and edit it" do | ||
49 | + visit '/' | ||
50 | + log_in admin | ||
51 | + click_on I18n.t('apps.index.new_app') | ||
52 | + fill_in 'app_name', :with => 'My new app' | ||
53 | + click_on I18n.t('apps.new.add_app') | ||
54 | + page.has_content?(I18n.t('controllers.apps.flash.create.success')) | ||
55 | + expect(App.where(:name => 'My new app').count).to eql 1 | ||
56 | + expect(App.where(:name => 'My new app 2').count).to eql 0 | ||
57 | + | ||
58 | + | ||
59 | + click_on I18n.t('shared.navigation.apps') | ||
60 | + click_on 'My new app' | ||
61 | + click_link I18n.t('apps.show.edit') | ||
62 | + fill_in 'app_name', :with => 'My new app 2' | ||
63 | + click_on I18n.t('apps.edit.update') | ||
64 | + page.has_content?(I18n.t('controllers.apps.flash.update.success')) | ||
65 | + expect(App.where(:name => 'My new app').count).to eql 0 | ||
66 | + expect(App.where(:name => 'My new app 2').count).to eql 1 | ||
67 | + | ||
68 | + end | ||
69 | + | ||
70 | + scenario "create an apps with issue tracker and edit it", :js => true do | ||
71 | + visit '/' | ||
72 | + log_in admin | ||
73 | + click_on I18n.t('apps.index.new_app') | ||
74 | + fill_in 'app_name', :with => 'My new app' | ||
75 | + find('.label_radio.github').click | ||
76 | + within ".github.tracker_params" do | ||
77 | + fill_in 'app_issue_tracker_attributes_options_username', :with => 'token' | ||
78 | + fill_in 'app_issue_tracker_attributes_options_password', :with => 'pass' | ||
79 | + end | ||
80 | + click_on I18n.t('apps.new.add_app') | ||
81 | + expect(page.has_content?(I18n.t('controllers.apps.flash.create.success'))).to eql true | ||
82 | + app = App.where(:name => 'My new app').first | ||
83 | + expect(app.issue_tracker.type_tracker).to eql 'github' | ||
84 | + expect(app.issue_tracker.options['username']).to eql 'token' | ||
85 | + expect(app.issue_tracker.options['password']).to eql 'pass' | ||
86 | + | ||
87 | + click_on I18n.t('shared.navigation.apps') | ||
88 | + click_on 'My new app' | ||
89 | + click_link I18n.t('apps.show.edit') | ||
90 | + find('.issue_tracker .label_radio.none').click | ||
91 | + click_on I18n.t('apps.edit.update') | ||
92 | + expect(page.has_content?(I18n.t('controllers.apps.flash.update.success'))).to eql true | ||
93 | + app = App.where(:name => 'My new app').first | ||
94 | + expect(app.issue_tracker.tracker).to be_a ErrbitPlugin::NoneIssueTracker | ||
95 | + end | ||
32 | end | 96 | end |
spec/controllers/apps_controller_spec.rb
@@ -299,7 +299,7 @@ describe AppsController do | @@ -299,7 +299,7 @@ describe AppsController do | ||
299 | context "unknown tracker type" do | 299 | context "unknown tracker type" do |
300 | before(:each) do | 300 | before(:each) do |
301 | put :update, :id => @app.id, :app => { :issue_tracker_attributes => { | 301 | put :update, :id => @app.id, :app => { :issue_tracker_attributes => { |
302 | - :type => 'unknown', :project_id => '1234', :api_token => '123123', :account => 'myapp' | 302 | + :type_tracker => 'unknown', :options => {:project_id => '1234', :api_token => '123123', :account => 'myapp'} |
303 | } } | 303 | } } |
304 | @app.reload | 304 | @app.reload |
305 | end | 305 | end |
@@ -309,33 +309,35 @@ describe AppsController do | @@ -309,33 +309,35 @@ describe AppsController do | ||
309 | end | 309 | end |
310 | end | 310 | end |
311 | 311 | ||
312 | - IssueTracker.subclasses.each do |tracker_klass| | ||
313 | - context tracker_klass do | 312 | + ErrbitPlugin::Registry.issue_trackers.each do |key, klass| |
313 | + context key do | ||
314 | it "should save tracker params" do | 314 | it "should save tracker params" do |
315 | - params = tracker_klass::Fields.inject({}){|hash,f| hash[f[0]] = "test_value"; hash } | ||
316 | - params[:ticket_properties] = "card_type = defect" if tracker_klass == MingleTracker | ||
317 | - params[:type] = tracker_klass.to_s | 315 | + params = { |
316 | + :options => klass.fields.inject({}){|hash,f| hash[f[0]] = "test_value"; hash }, | ||
317 | + :type_tracker => key.dup.to_s | ||
318 | + } | ||
318 | put :update, :id => @app.id, :app => {:issue_tracker_attributes => params} | 319 | put :update, :id => @app.id, :app => {:issue_tracker_attributes => params} |
319 | 320 | ||
320 | @app.reload | 321 | @app.reload |
321 | 322 | ||
322 | tracker = @app.issue_tracker | 323 | tracker = @app.issue_tracker |
323 | - expect(tracker).to be_a(tracker_klass) | ||
324 | - tracker_klass::Fields.each do |field, field_info| | 324 | + expect(tracker.tracker).to be_a(ErrbitPlugin::Registry.issue_trackers[key]) |
325 | + klass.fields.each do |field, field_info| | ||
325 | case field | 326 | case field |
326 | - when :ticket_properties | ||
327 | - expect(tracker.send(field.to_sym)).to eq 'card_type = defect' | ||
328 | - else | ||
329 | - expect(tracker.send(field.to_sym)).to eq 'test_value' | 327 | + when :ticket_properties; tracker.send(field.to_sym).should == 'card_type = defect' |
328 | + else tracker.options[field.to_s].should == 'test_value' | ||
330 | end | 329 | end |
331 | end | 330 | end |
332 | end | 331 | end |
333 | 332 | ||
334 | it "should show validation notice when sufficient params are not present" do | 333 | it "should show validation notice when sufficient params are not present" do |
335 | # Leave out one required param | 334 | # Leave out one required param |
336 | - params = tracker_klass::Fields[1..-1].inject({}){|hash,f| hash[f[0]] = "test_value"; hash } | ||
337 | - params[:type] = tracker_klass.to_s | ||
338 | - put :update, :id => @app.id, :app => {:issue_tracker_attributes => params} | 335 | + # TODO. previous test was not relevant because one params can be enough. So put noone |
336 | + put :update, :id => @app.id, :app => { | ||
337 | + :issue_tracker_attributes => { | ||
338 | + :type_tracker => key.dup.to_s | ||
339 | + } | ||
340 | + } | ||
339 | 341 | ||
340 | @app.reload | 342 | @app.reload |
341 | expect(@app.issue_tracker_configured?).to eq false | 343 | expect(@app.issue_tracker_configured?).to eq false |
spec/controllers/problems_controller_spec.rb
@@ -8,15 +8,16 @@ describe ProblemsController do | @@ -8,15 +8,16 @@ describe ProblemsController do | ||
8 | :params => {:app_id => 'dummyid', :id => 'dummyid'} | 8 | :params => {:app_id => 'dummyid', :id => 'dummyid'} |
9 | 9 | ||
10 | let(:app) { Fabricate(:app) } | 10 | let(:app) { Fabricate(:app) } |
11 | - let(:err) { Fabricate(:err, :problem => Fabricate(:problem, :app => app, :environment => "production")) } | 11 | + let(:err) { Fabricate(:err, :problem => problem) } |
12 | + let(:admin) { Fabricate(:admin) } | ||
13 | + let(:problem) { Fabricate(:problem, :app => app, :environment => "production") } | ||
12 | 14 | ||
13 | 15 | ||
14 | describe "GET /problems" do | 16 | describe "GET /problems" do |
15 | #render_views | 17 | #render_views |
16 | context 'when logged in as an admin' do | 18 | context 'when logged in as an admin' do |
17 | before(:each) do | 19 | before(:each) do |
18 | - @user = Fabricate(:admin) | ||
19 | - sign_in @user | 20 | + sign_in admin |
20 | @problem = Fabricate(:notice, :err => Fabricate(:err, :problem => Fabricate(:problem, :app => app, :environment => "production"))).problem | 21 | @problem = Fabricate(:notice, :err => Fabricate(:err, :problem => Fabricate(:problem, :app => app, :environment => "production"))).problem |
21 | end | 22 | end |
22 | 23 | ||
@@ -31,7 +32,7 @@ describe ProblemsController do | @@ -31,7 +32,7 @@ describe ProblemsController do | ||
31 | end | 32 | end |
32 | 33 | ||
33 | it "should be able to override default per_page value" do | 34 | it "should be able to override default per_page value" do |
34 | - @user.update_attribute :per_page, 10 | 35 | + admin.update_attribute :per_page, 10 |
35 | get :index | 36 | get :index |
36 | expect(controller.problems.to_a.size).to eq 10 | 37 | expect(controller.problems.to_a.size).to eq 10 |
37 | end | 38 | end |
@@ -98,7 +99,7 @@ describe ProblemsController do | @@ -98,7 +99,7 @@ describe ProblemsController do | ||
98 | describe "GET /problems - previously all" do | 99 | describe "GET /problems - previously all" do |
99 | context 'when logged in as an admin' do | 100 | context 'when logged in as an admin' do |
100 | it "gets a paginated list of all problems" do | 101 | it "gets a paginated list of all problems" do |
101 | - sign_in Fabricate(:admin) | 102 | + sign_in admin |
102 | problems = Kaminari.paginate_array((1..30).to_a) | 103 | problems = Kaminari.paginate_array((1..30).to_a) |
103 | 3.times { problems << Fabricate(:err).problem } | 104 | 3.times { problems << Fabricate(:err).problem } |
104 | 3.times { problems << Fabricate(:err, :problem => Fabricate(:problem, :resolved => true)).problem } | 105 | 3.times { problems << Fabricate(:err, :problem => Fabricate(:problem, :resolved => true)).problem } |
@@ -153,7 +154,7 @@ describe ProblemsController do | @@ -153,7 +154,7 @@ describe ProblemsController do | ||
153 | 154 | ||
154 | context 'when logged in as an admin' do | 155 | context 'when logged in as an admin' do |
155 | before do | 156 | before do |
156 | - sign_in Fabricate(:admin) | 157 | + sign_in admin |
157 | end | 158 | end |
158 | 159 | ||
159 | it "finds the app" do | 160 | it "finds the app" do |
@@ -217,7 +218,7 @@ describe ProblemsController do | @@ -217,7 +218,7 @@ describe ProblemsController do | ||
217 | 218 | ||
218 | describe "PUT /apps/:app_id/problems/:id/resolve" do | 219 | describe "PUT /apps/:app_id/problems/:id/resolve" do |
219 | before do | 220 | before do |
220 | - sign_in Fabricate(:admin) | 221 | + sign_in admin |
221 | 222 | ||
222 | @problem = Fabricate(:err) | 223 | @problem = Fabricate(:err) |
223 | App.stub(:find).with(@problem.app.id.to_s).and_return(@problem.app) | 224 | App.stub(:find).with(@problem.app.id.to_s).and_return(@problem.app) |
@@ -256,76 +257,50 @@ describe ProblemsController do | @@ -256,76 +257,50 @@ describe ProblemsController do | ||
256 | end | 257 | end |
257 | 258 | ||
258 | describe "POST /apps/:app_id/problems/:id/create_issue" do | 259 | describe "POST /apps/:app_id/problems/:id/create_issue" do |
259 | - #render_views | ||
260 | 260 | ||
261 | before(:each) do | 261 | before(:each) do |
262 | - sign_in Fabricate(:admin) | 262 | + sign_in admin |
263 | end | 263 | end |
264 | 264 | ||
265 | context "successful issue creation" do | 265 | context "successful issue creation" do |
266 | context "lighthouseapp tracker" do | 266 | context "lighthouseapp tracker" do |
267 | let(:notice) { Fabricate :notice } | 267 | let(:notice) { Fabricate :notice } |
268 | - let(:tracker) { Fabricate :lighthouse_tracker, :app => notice.app } | ||
269 | let(:problem) { notice.problem } | 268 | let(:problem) { notice.problem } |
270 | 269 | ||
271 | before(:each) do | 270 | before(:each) do |
272 | - number = 5 | ||
273 | - @issue_link = "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets/#{number}.xml" | ||
274 | - body = "<ticket><number type=\"integer\">#{number}</number></ticket>" | ||
275 | - stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml"). | ||
276 | - to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | ||
277 | - | 271 | + controller.stub(:problem).and_return(problem) |
272 | + controller.stub(:current_user).and_return(admin) | ||
273 | + IssueCreation.should_receive(:new).with(problem, admin, nil, request).and_return(double(:execute => true)) | ||
278 | post :create_issue, :app_id => problem.app.id, :id => problem.id | 274 | post :create_issue, :app_id => problem.app.id, :id => problem.id |
279 | - problem.reload | ||
280 | end | 275 | end |
281 | 276 | ||
282 | it "should redirect to problem page" do | 277 | it "should redirect to problem page" do |
283 | expect(response).to redirect_to( app_problem_path(problem.app, problem) ) | 278 | expect(response).to redirect_to( app_problem_path(problem.app, problem) ) |
279 | + expect(flash[:error]).to be_blank | ||
284 | end | 280 | end |
285 | end | 281 | end |
286 | end | 282 | end |
287 | 283 | ||
288 | - context "absent issue tracker" do | ||
289 | - let(:problem) { Fabricate :problem } | ||
290 | - | 284 | + context "error during request to a tracker" do |
291 | before(:each) do | 285 | before(:each) do |
286 | + IssueCreation.should_receive(:new).with(problem, admin, nil, request).and_return( | ||
287 | + double(:execute => false, :errors => double(:full_messages => ['not create'])) | ||
288 | + ) | ||
289 | + controller.stub(:problem).and_return(problem) | ||
292 | post :create_issue, :app_id => problem.app.id, :id => problem.id | 290 | post :create_issue, :app_id => problem.app.id, :id => problem.id |
293 | end | 291 | end |
294 | 292 | ||
295 | it "should redirect to problem page" do | 293 | it "should redirect to problem page" do |
296 | expect(response).to redirect_to( app_problem_path(problem.app, problem) ) | 294 | expect(response).to redirect_to( app_problem_path(problem.app, problem) ) |
297 | - end | ||
298 | - | ||
299 | - it "should set flash error message telling issue tracker of the app doesn't exist" do | ||
300 | - expect(flash[:error]).to eq "This app has no issue tracker setup." | 295 | + expect(flash[:error]).to eql 'not create' |
301 | end | 296 | end |
302 | end | 297 | end |
303 | 298 | ||
304 | - context "error during request to a tracker" do | ||
305 | - context "lighthouseapp tracker" do | ||
306 | - let(:tracker) { Fabricate :lighthouse_tracker } | ||
307 | - let(:err) { Fabricate(:err, :problem => Fabricate(:problem, :app => tracker.app)) } | ||
308 | - | ||
309 | - before(:each) do | ||
310 | - stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml").to_return(:status => 500) | ||
311 | - | ||
312 | - post :create_issue, :app_id => err.app.id, :id => err.problem.id | ||
313 | - end | ||
314 | - | ||
315 | - it "should redirect to problem page" do | ||
316 | - expect(response).to redirect_to( app_problem_path(err.app, err.problem) ) | ||
317 | - end | ||
318 | - | ||
319 | - it "should notify of connection error" do | ||
320 | - expect(flash[:error]).to include("There was an error during issue creation:") | ||
321 | - end | ||
322 | - end | ||
323 | - end | ||
324 | end | 299 | end |
325 | 300 | ||
326 | describe "DELETE /apps/:app_id/problems/:id/unlink_issue" do | 301 | describe "DELETE /apps/:app_id/problems/:id/unlink_issue" do |
327 | before(:each) do | 302 | before(:each) do |
328 | - sign_in Fabricate(:admin) | 303 | + sign_in admin |
329 | end | 304 | end |
330 | 305 | ||
331 | context "problem with issue" do | 306 | context "problem with issue" do |
@@ -361,7 +336,7 @@ describe ProblemsController do | @@ -361,7 +336,7 @@ describe ProblemsController do | ||
361 | 336 | ||
362 | describe "Bulk Actions" do | 337 | describe "Bulk Actions" do |
363 | before(:each) do | 338 | before(:each) do |
364 | - sign_in Fabricate(:admin) | 339 | + sign_in admin |
365 | @problem1 = Fabricate(:err, :problem => Fabricate(:problem, :resolved => true)).problem | 340 | @problem1 = Fabricate(:err, :problem => Fabricate(:problem, :resolved => true)).problem |
366 | @problem2 = Fabricate(:err, :problem => Fabricate(:problem, :resolved => false)).problem | 341 | @problem2 = Fabricate(:err, :problem => Fabricate(:problem, :resolved => false)).problem |
367 | end | 342 | end |
@@ -0,0 +1,37 @@ | @@ -0,0 +1,37 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe AppDecorator do | ||
4 | + | ||
5 | + describe "#email_at_notices" do | ||
6 | + | ||
7 | + it 'return the list separate by comma' do | ||
8 | + expect(AppDecorator.new(double(:email_at_notices => [2,3])).email_at_notices).to eql '2, 3' | ||
9 | + end | ||
10 | + | ||
11 | + end | ||
12 | + | ||
13 | + describe "#notify_user_display" do | ||
14 | + | ||
15 | + it 'return display:none if notify' do | ||
16 | + expect(AppDecorator.new(double(:notify_all_users => true)).notify_user_display).to eql 'display: none;' | ||
17 | + end | ||
18 | + | ||
19 | + it 'return blank if no notify' do | ||
20 | + expect(AppDecorator.new(double(:notify_all_users => false)).notify_user_display).to eql '' | ||
21 | + end | ||
22 | + | ||
23 | + end | ||
24 | + | ||
25 | + describe "#notify_err_display" do | ||
26 | + | ||
27 | + it 'return display:none if no notify' do | ||
28 | + expect(AppDecorator.new(double(:notify_on_errs => false)).notify_err_display).to eql 'display: none;' | ||
29 | + end | ||
30 | + | ||
31 | + it 'return blank if no notify' do | ||
32 | + expect(AppDecorator.new(double(:notify_on_errs => true)).notify_err_display).to eql '' | ||
33 | + end | ||
34 | + | ||
35 | + end | ||
36 | + | ||
37 | +end |
@@ -0,0 +1,55 @@ | @@ -0,0 +1,55 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe IssueTrackerDecorator do | ||
4 | + let(:fake_tracker) do | ||
5 | + Class.new(ErrbitPlugin::IssueTracker) do | ||
6 | + def self.label; 'fake'; end | ||
7 | + def self.note; 'a note'; end | ||
8 | + def self.fields | ||
9 | + { | ||
10 | + :foo => {:label => 'foo'}, | ||
11 | + :bar => {:label => 'bar'} | ||
12 | + } | ||
13 | + end | ||
14 | + | ||
15 | + def configured?; true; end | ||
16 | + end | ||
17 | + end | ||
18 | + | ||
19 | + let(:decorator) do | ||
20 | + IssueTrackerDecorator.new(fake_tracker, 'fake') | ||
21 | + end | ||
22 | + | ||
23 | + describe "#note" do | ||
24 | + it 'return the html_safe of Note' do | ||
25 | + expect(decorator.note).to eql fake_tracker.note | ||
26 | + end | ||
27 | + end | ||
28 | + | ||
29 | + describe "#issue_trackers" do | ||
30 | + it 'return an array of IssueTrackerDecorator' do | ||
31 | + decorator.issue_trackers do |it| | ||
32 | + expect(it).to be_a(IssueTrackerDecorator) | ||
33 | + end | ||
34 | + end | ||
35 | + end | ||
36 | + | ||
37 | + describe "#fields" do | ||
38 | + it 'return all Fields define decorate' do | ||
39 | + decorator.fields do |itf| | ||
40 | + expect(itf).to be_a(IssueTrackerFieldDecorator) | ||
41 | + expect([:foo, :bar]).to be_include(itf.object) | ||
42 | + expect([{:label => 'foo'}, {:label => 'bar'}]).to be_include(itf.field_info) | ||
43 | + end | ||
44 | + end | ||
45 | + end | ||
46 | + | ||
47 | + describe "#params_class" do | ||
48 | + it 'add the label in class' do | ||
49 | + expect(decorator.params_class(IssueTracker.new(:type_tracker => 'none'))).to eql 'fake' | ||
50 | + end | ||
51 | + it 'add chosen class if _type is same' do | ||
52 | + expect(decorator.params_class(IssueTracker.new(:type_tracker => 'fake'))).to eql 'chosen fake' | ||
53 | + end | ||
54 | + end | ||
55 | +end |
@@ -0,0 +1,14 @@ | @@ -0,0 +1,14 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe IssueTrackerFieldDecorator do | ||
4 | + | ||
5 | + describe "#label" do | ||
6 | + it 'return the label of field_info by default' do | ||
7 | + expect(IssueTrackerFieldDecorator.new(:foo, {:label => 'hello'}).label).to eq 'hello' | ||
8 | + end | ||
9 | + it 'return the key of field if no label define' do | ||
10 | + expect(IssueTrackerFieldDecorator.new(:foo, {}).label).to eq 'foo' | ||
11 | + end | ||
12 | + end | ||
13 | + | ||
14 | +end |
@@ -0,0 +1,17 @@ | @@ -0,0 +1,17 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe WatcherDecorator do | ||
4 | + describe "#email_choosen" do | ||
5 | + context "with email define" do | ||
6 | + it 'return blank' do | ||
7 | + expect(WatcherDecorator.new(double(:email => 'foo')).email_choosen).to eql '' | ||
8 | + end | ||
9 | + end | ||
10 | + | ||
11 | + context "without email define" do | ||
12 | + it 'return choosen' do | ||
13 | + expect(WatcherDecorator.new(double(:email => '')).email_choosen).to eql 'chosen' | ||
14 | + end | ||
15 | + end | ||
16 | + end | ||
17 | +end |
spec/fabricators/issue_tracker_fabricator.rb
1 | Fabricator :issue_tracker do | 1 | Fabricator :issue_tracker do |
2 | - app | ||
3 | - api_token { sequence :word } | ||
4 | - project_id { sequence :word } | ||
5 | - account { sequence :word } | ||
6 | - username { sequence :word } | ||
7 | - password { sequence :word } | ||
8 | -end | ||
9 | - | ||
10 | -%w(lighthouse pivotal_labs fogbugz).each do |t| | ||
11 | - Fabricator "#{t}_tracker".to_sym, :from => :issue_tracker, :class_name => "IssueTrackers::#{t.camelcase}Tracker" | ||
12 | -end | ||
13 | - | ||
14 | -Fabricator :redmine_tracker, :from => :issue_tracker, :class_name => "IssueTrackers::RedmineTracker" do | ||
15 | - account 'http://redmine.example.com' | ||
16 | -end | ||
17 | - | ||
18 | -Fabricator :gitlab_tracker, :from => :issue_tracker, :class_name => "IssueTrackers::GitlabTracker" do | ||
19 | - account 'http://gitlab.example.com' | ||
20 | - alt_project_id 'foo' | ||
21 | -end | 2 | + type_tracker 'fake' |
3 | + options {{ | ||
4 | + :foo => 'one', | ||
5 | + :bar => 'two' | ||
6 | + }} | ||
22 | 7 | ||
23 | -Fabricator :mingle_tracker, :from => :issue_tracker, :class_name => "IssueTrackers::MingleTracker" do | ||
24 | - account 'https://mingle.example.com' | ||
25 | - ticket_properties 'card_type = Defect, defect_status = open, priority = essential' | ||
26 | -end | ||
27 | - | ||
28 | -Fabricator :github_issues_tracker, :from => :issue_tracker, :class_name => "IssueTrackers::GithubIssuesTracker" do | ||
29 | - project_id 'test_account/test_project' | ||
30 | - username 'test_username' | ||
31 | -end | ||
32 | - | ||
33 | -Fabricator :bitbucket_issues_tracker, :from => :issue_tracker, :class_name => "IssueTrackers::BitbucketIssuesTracker" do | ||
34 | - project_id 'password' | ||
35 | - api_token 'test_username' | ||
36 | -end | ||
37 | - | ||
38 | -Fabricator :unfuddle_issues_tracker, :from => :issue_tracker, :class_name => "IssueTrackers::UnfuddleTracker" do | ||
39 | - account 'test' | ||
40 | - project_id 15 | 8 | + app |
41 | end | 9 | end |
spec/interactors/issue_creation_spec.rb
1 | require 'spec_helper' | 1 | require 'spec_helper' |
2 | 2 | ||
3 | describe IssueCreation do | 3 | describe IssueCreation do |
4 | - subject(:issue_creation) { IssueCreation.new(problem, user, tracker_name) } | 4 | + subject(:issue_creation) do |
5 | + IssueCreation.new(problem, user, tracker_name, request) | ||
6 | + end | ||
5 | 7 | ||
8 | + let(:request) do | ||
9 | + double(:request, | ||
10 | + :host => 'github.com', | ||
11 | + :port => '80', | ||
12 | + :scheme => 'http' | ||
13 | + ) | ||
14 | + end | ||
6 | let(:problem) { notice.problem } | 15 | let(:problem) { notice.problem } |
7 | let(:notice) { Fabricate(:notice) } | 16 | let(:notice) { Fabricate(:notice) } |
8 | let(:user) { Fabricate(:admin) } | 17 | let(:user) { Fabricate(:admin) } |
@@ -15,8 +24,7 @@ describe IssueCreation do | @@ -15,8 +24,7 @@ describe IssueCreation do | ||
15 | end | 24 | end |
16 | 25 | ||
17 | it 'creates an issue if issue tracker is configured' do | 26 | it 'creates an issue if issue tracker is configured' do |
18 | - tracker = Fabricate(:lighthouse_tracker, :app => notice.app) | ||
19 | - expect(tracker).to receive(:create_issue) | 27 | + problem.app.issue_tracker = Fabricate(:issue_tracker) |
20 | issue_creation.execute | 28 | issue_creation.execute |
21 | expect(errors).to be_empty | 29 | expect(errors).to be_empty |
22 | end | 30 | end |
@@ -44,7 +52,9 @@ describe IssueCreation do | @@ -44,7 +52,9 @@ describe IssueCreation do | ||
44 | user.github_oauth_token = 'oauthtoken' | 52 | user.github_oauth_token = 'oauthtoken' |
45 | user.save! | 53 | user.save! |
46 | 54 | ||
47 | - GithubIssuesTracker.any_instance.should_receive(:create_issue) | 55 | + ErrbitGithubPlugin::IssueTracker.should_receive(:new).and_return( |
56 | + double(:create_issue => true) | ||
57 | + ) | ||
48 | issue_creation.execute | 58 | issue_creation.execute |
49 | expect(errors).to be_empty | 59 | expect(errors).to be_empty |
50 | end | 60 | end |
spec/interactors/problem_updater_cache_spec.rb
@@ -74,7 +74,7 @@ describe ProblemUpdaterCache do | @@ -74,7 +74,7 @@ describe ProblemUpdaterCache do | ||
74 | end | 74 | end |
75 | 75 | ||
76 | it 'update last_notice_at' do | 76 | it 'update last_notice_at' do |
77 | - expect(problem.last_notice_at.to_i).to be_within(1).of(notice.created_at.to_i) | 77 | + expect(problem.last_notice_at.to_i).to be_within(2).of(notice.created_at.to_i) |
78 | end | 78 | end |
79 | 79 | ||
80 | it 'update stats messages' do | 80 | it 'update stats messages' do |
spec/models/app_spec.rb
1 | require 'spec_helper' | 1 | require 'spec_helper' |
2 | 2 | ||
3 | describe App do | 3 | describe App do |
4 | + | ||
4 | context "Attributes" do | 5 | context "Attributes" do |
5 | it { should have_field(:_id).of_type(String) } | 6 | it { should have_field(:_id).of_type(String) } |
6 | it { should have_field(:name).of_type(String) } | 7 | it { should have_field(:name).of_type(String) } |
@@ -220,5 +221,26 @@ describe App do | @@ -220,5 +221,26 @@ describe App do | ||
220 | end | 221 | end |
221 | end | 222 | end |
222 | 223 | ||
224 | + describe "#comments_allowed?" do | ||
225 | + context "without issue_tracker" do | ||
226 | + it 'return true' do | ||
227 | + expect(App.new.comments_allowed?).to be_true | ||
228 | + end | ||
229 | + end | ||
230 | + | ||
231 | + context "with issue_tracker" do | ||
232 | + let(:issue_tracker) do | ||
233 | + ist = IssueTracker.new | ||
234 | + ist.stub(:comments_allowed?).and_return('foo') | ||
235 | + ist | ||
236 | + end | ||
237 | + it 'delegate to issue_tracker' do | ||
238 | + expect(App.new( | ||
239 | + :issue_tracker => issue_tracker | ||
240 | + ).comments_allowed?).to eql 'foo' | ||
241 | + end | ||
242 | + end | ||
243 | + end | ||
244 | + | ||
223 | end | 245 | end |
224 | 246 |
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe IssueTracker do | ||
4 | + describe "Association" do | ||
5 | + it { should be_embedded_in(:app) } | ||
6 | + end | ||
7 | + | ||
8 | + describe "Attributes" do | ||
9 | + it { should have_field(:type_tracker).of_type(String) } | ||
10 | + it { should have_field(:options).of_type(Hash).with_default_value_of({}) } | ||
11 | + end | ||
12 | + | ||
13 | + describe "#tracker" do | ||
14 | + context "with type_tracker class not exist" do | ||
15 | + it 'return NullIssueTracker' do | ||
16 | + expect(IssueTracker.new(:type_tracker => 'Foo').tracker).to be_a ErrbitPlugin::NoneIssueTracker | ||
17 | + end | ||
18 | + end | ||
19 | + end | ||
20 | +end |
spec/models/issue_trackers/bitbucket_issues_tracker_spec.rb
@@ -1,40 +0,0 @@ | @@ -1,40 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe IssueTrackers::BitbucketIssuesTracker do | ||
4 | - it "should create an issue on BitBucket Issues with problem params, and set issue link for problem" do | ||
5 | - repo = "test_user/test_repo" | ||
6 | - notice = Fabricate :notice | ||
7 | - notice.app.bitbucket_repo = repo | ||
8 | - tracker = Fabricate :bitbucket_issues_tracker, :app => notice.app | ||
9 | - problem = notice.problem | ||
10 | - | ||
11 | - number = 123 | ||
12 | - @issue_link = "https://bitbucket.org/#{repo}/issue/#{number}/" | ||
13 | - body = <<EOF | ||
14 | -{ | ||
15 | - "status": "new", | ||
16 | - "priority": "critical", | ||
17 | - "title": "[production][foo#bar] FooError: Too Much Bar", | ||
18 | - "comment_count": 0, | ||
19 | - "content": "This is the content", | ||
20 | - "created_on": "2012-07-29 04:35:38", | ||
21 | - "local_id": 123, | ||
22 | - "follower_count": 0, | ||
23 | - "utc_created_on": "2012-07-29 02:35:38+00:00", | ||
24 | - "resource_uri": "/1.0/repositories/test_user/test_repo/issue/123/", | ||
25 | - "is_spam": false | ||
26 | -} | ||
27 | -EOF | ||
28 | - | ||
29 | - stub_request(:post, "https://#{tracker.api_token}:#{tracker.project_id}@bitbucket.org/api/1.0/repositories/test_user/test_repo/issues/").to_return(:status => 200, :headers => {}, :body => body ) | ||
30 | - | ||
31 | - problem.app.issue_tracker.create_issue(problem) | ||
32 | - problem.reload | ||
33 | - | ||
34 | - requested = have_requested(:post, "https://#{tracker.api_token}:#{tracker.project_id}@bitbucket.org/api/1.0/repositories/test_user/test_repo/issues/") | ||
35 | - expect(WebMock).to requested.with(:title => /[production][foo#bar] FooError: Too Much Bar/) | ||
36 | - expect(WebMock).to requested.with(:content => /See this exception on Errbit/) | ||
37 | - | ||
38 | - expect(problem.issue_link).to eq @issue_link | ||
39 | - end | ||
40 | -end |
spec/models/issue_trackers/fogbugz_tracker_spec.rb
@@ -1,23 +0,0 @@ | @@ -1,23 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe IssueTrackers::FogbugzTracker do | ||
4 | - it "should create an issue on Fogbugz with problem params, and set issue link for problem" do | ||
5 | - notice = Fabricate :notice | ||
6 | - tracker = Fabricate :fogbugz_tracker, :app => notice.app | ||
7 | - problem = notice.problem | ||
8 | - | ||
9 | - number = 123 | ||
10 | - @issue_link = "https://#{tracker.account}.fogbugz.com/default.asp?#{number}" | ||
11 | - response = "<response><token>12345</token><case><ixBug>123</ixBug></case></response>" | ||
12 | - http_mock = double | ||
13 | - expect(http_mock).to receive(:new).and_return(http_mock) | ||
14 | - expect(http_mock).to receive(:request).twice.and_return(response) | ||
15 | - Fogbugz.adapter[:http] = http_mock | ||
16 | - | ||
17 | - problem.app.issue_tracker.create_issue(problem) | ||
18 | - problem.reload | ||
19 | - | ||
20 | - expect(problem.issue_link).to eq @issue_link | ||
21 | - end | ||
22 | -end | ||
23 | - |
spec/models/issue_trackers/github_issues_tracker_spec.rb
@@ -1,73 +0,0 @@ | @@ -1,73 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe IssueTrackers::GithubIssuesTracker do | ||
4 | - | ||
5 | - let(:repo) { "test_user/test_repo" } | ||
6 | - | ||
7 | - let(:notice) do | ||
8 | - Fabricate :notice | ||
9 | - end | ||
10 | - | ||
11 | - let(:problem) do | ||
12 | - notice.problem | ||
13 | - end | ||
14 | - | ||
15 | - let!(:tracker) do | ||
16 | - notice.app.github_repo = repo | ||
17 | - Fabricate :github_issues_tracker, app: notice.app | ||
18 | - end | ||
19 | - | ||
20 | - let(:number) { 5 } | ||
21 | - let(:issue_link) { "https://github.com/#{repo}/issues/#{number}" } | ||
22 | - let(:body) do | ||
23 | - <<EOF | ||
24 | -{ | ||
25 | - "position": 1.0, | ||
26 | - "number": #{number}, | ||
27 | - "votes": 0, | ||
28 | - "created_at": "2010/01/21 13:45:59 -0800", | ||
29 | - "comments": 0, | ||
30 | - "body": "Test Body", | ||
31 | - "title": "Test Issue", | ||
32 | - "user": "test_user", | ||
33 | - "state": "open", | ||
34 | - "html_url": "#{issue_link}" | ||
35 | -} | ||
36 | -EOF | ||
37 | -end | ||
38 | - | ||
39 | - it "should create an issue on GitHub Issues with problem params, and set issue link for problem" do | ||
40 | - stub_request(:post, | ||
41 | - "https://#{tracker.username}:#{tracker.password}@api.github.com/repos/#{repo}/issues"). | ||
42 | - to_return(:status => 201, | ||
43 | - :headers => { | ||
44 | - 'Location' => issue_link, | ||
45 | - 'Content-Type' => 'application/json', | ||
46 | - }, | ||
47 | - :body => body ) | ||
48 | - | ||
49 | - problem.app.issue_tracker.create_issue(problem) | ||
50 | - problem.reload | ||
51 | - | ||
52 | - requested = have_requested(:post, "https://#{tracker.username}:#{tracker.password}@api.github.com/repos/#{repo}/issues") | ||
53 | - expect(WebMock).to requested.with(:body => /[production][foo#bar] FooError: Too Much Bar/) | ||
54 | - expect(WebMock).to requested.with(:body => /See this exception on Errbit/) | ||
55 | - | ||
56 | - expect(problem.issue_link).to eq issue_link | ||
57 | - end | ||
58 | - | ||
59 | - it "should create an issue with oauth token" do | ||
60 | - issue_tracker = problem.app.issue_tracker | ||
61 | - issue_tracker.oauth_token = 'secret_token' | ||
62 | - | ||
63 | - stub_request(:post, "https://api.github.com/repos/#{repo}/issues"). | ||
64 | - to_return({ | ||
65 | - status: 201, | ||
66 | - headers: {'Location' => issue_link, 'Content-Type' => 'application/json' }, | ||
67 | - body: body }) | ||
68 | - | ||
69 | - issue_tracker.create_issue(problem) | ||
70 | - requested = have_requested(:post, "https://api.github.com/repos/#{repo}/issues") | ||
71 | - expect(WebMock).to requested.with(headers: {'Authorization'=>'token secret_token'}) | ||
72 | - end | ||
73 | -end |
spec/models/issue_trackers/gitlab_issues_tracker_spec.rb
@@ -1,32 +0,0 @@ | @@ -1,32 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe IssueTrackers::GitlabTracker do | ||
4 | - it "should create an issue on Gitlab with problem params" do | ||
5 | - notice = Fabricate :notice | ||
6 | - tracker = Fabricate :gitlab_tracker, :app => notice.app | ||
7 | - problem = notice.problem | ||
8 | - | ||
9 | - issue_id = 5 | ||
10 | - note_id = 10 | ||
11 | - issue_body = {:id => issue_id, :title => "[production][foo#bar] FooError: Too Much Bar", :description => "[See this exception on Errbit]"}.to_json | ||
12 | - note_body = {:id => note_id, :body => "Example note body"}.to_json | ||
13 | - | ||
14 | - stub_request(:post, "#{tracker.account}/api/v3/projects/#{tracker.project_id}/issues?private_token=#{tracker.api_token}"). | ||
15 | - with(:body => /.+/, :headers => {'Accept'=>'application/json'}). | ||
16 | - to_return(:status => 200, :body => issue_body, :headers => {'Accept'=>'application/json'}) | ||
17 | - | ||
18 | - stub_request(:post, "#{tracker.account}/api/v3/projects/#{tracker.project_id}/issues/#{issue_id}/notes?private_token=#{tracker.api_token}"). | ||
19 | - with(:body => /.+/, :headers => {'Accept'=>'application/json'}). | ||
20 | - to_return(:status => 200, :body => note_body, :headers => {'Accept'=>'application/json'}) | ||
21 | - | ||
22 | - problem.app.issue_tracker.create_issue(problem) | ||
23 | - problem.reload | ||
24 | - | ||
25 | - requested_issue = have_requested(:post, "#{tracker.account}/api/v3/projects/#{tracker.project_id}/issues?private_token=#{tracker.api_token}").with(:body => /.+/, :headers => {'Accept'=>'application/json'}) | ||
26 | - requested_note = have_requested(:post, "#{tracker.account}/api/v3/projects/#{tracker.project_id}/issues/#{issue_id}/notes?private_token=#{tracker.api_token}") | ||
27 | - expect(WebMock).to requested_issue.with(:body => /%5Bproduction%5D%5Bfoo%23bar%5D%20FooError%3A%20Too%20Much%20Bar/) | ||
28 | - expect(WebMock).to requested_issue.with(:body => /See%20this%20exception%20on%20Errbit/) | ||
29 | - | ||
30 | - end | ||
31 | -end | ||
32 | - |
spec/models/issue_trackers/lighthouse_tracker_spec.rb
@@ -1,27 +0,0 @@ | @@ -1,27 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe IssueTrackers::LighthouseTracker do | ||
4 | - it "should create an issue on Lighthouse with problem params, and set issue link for problem" do | ||
5 | - notice = Fabricate :notice | ||
6 | - tracker = Fabricate :lighthouse_tracker, :app => notice.app | ||
7 | - problem = notice.problem | ||
8 | - | ||
9 | - number = 5 | ||
10 | - @issue_link = "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets/#{number}.xml" | ||
11 | - body = "<ticket><number type=\"integer\">#{number}</number></ticket>" | ||
12 | - stub_request(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml"). | ||
13 | - to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | ||
14 | - | ||
15 | - problem.app.issue_tracker.create_issue(problem) | ||
16 | - problem.reload | ||
17 | - | ||
18 | - requested = have_requested(:post, "http://#{tracker.account}.lighthouseapp.com/projects/#{tracker.project_id}/tickets.xml") | ||
19 | - expect(WebMock).to requested.with(:headers => {'X-Lighthousetoken' => tracker.api_token}) | ||
20 | - expect(WebMock).to requested.with(:body => /<tag>errbit<\/tag>/) | ||
21 | - expect(WebMock).to requested.with(:body => /<title>\[#{ problem.environment }\]\[#{problem.where}\] #{problem.message.to_s.truncate(100)}<\/title>/) | ||
22 | - expect(WebMock).to requested.with(:body => /<body>.+<\/body>/m) | ||
23 | - | ||
24 | - expect(problem.issue_link).to eq @issue_link.sub(/\.xml$/, '') | ||
25 | - end | ||
26 | -end | ||
27 | - |
spec/models/issue_trackers/mingle_tracker_spec.rb
@@ -1,28 +0,0 @@ | @@ -1,28 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe IssueTrackers::MingleTracker do | ||
4 | - it "should create an issue on Mingle with problem params, and set issue link for problem" do | ||
5 | - notice = Fabricate :notice | ||
6 | - tracker = Fabricate :mingle_tracker, :app => notice.app | ||
7 | - problem = notice.problem | ||
8 | - | ||
9 | - number = 5 | ||
10 | - @issue_link = "#{tracker.account}/projects/#{tracker.project_id}/cards/#{number}.xml" | ||
11 | - @basic_auth = tracker.account.gsub("://", "://#{tracker.username}:#{tracker.password}@") | ||
12 | - body = "<card><id type=\"integer\">#{number}</id></card>" | ||
13 | - stub_request(:post, "#{@basic_auth}/api/v1/projects/#{tracker.project_id}/cards.xml"). | ||
14 | - to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | ||
15 | - | ||
16 | - problem.app.issue_tracker.create_issue(problem) | ||
17 | - problem.reload | ||
18 | - | ||
19 | - requested = have_requested(:post, "#{@basic_auth}/api/v1/projects/#{tracker.project_id}/cards.xml") | ||
20 | - expect(WebMock).to requested.with(:headers => {'Content-Type' => 'application/xml'}) | ||
21 | - expect(WebMock).to requested.with(:body => /FooError: Too Much Bar/) | ||
22 | - expect(WebMock).to requested.with(:body => /See this exception on Errbit/) | ||
23 | - expect(WebMock).to requested.with(:body => /<card-type-name>Defect<\/card-type-name>/) | ||
24 | - | ||
25 | - expect(problem.issue_link).to eq @issue_link.sub(/\.xml$/, '') | ||
26 | - end | ||
27 | -end | ||
28 | - |
spec/models/issue_trackers/pivotal_labs_tracker_spec.rb
@@ -1,46 +0,0 @@ | @@ -1,46 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe IssueTrackers::PivotalLabsTracker do | ||
4 | - | ||
5 | - let(:user) { Fabricate(:user) } | ||
6 | - let(:notice) { Fabricate(:notice) } | ||
7 | - let(:tracker) { Fabricate :pivotal_labs_tracker, :app => notice.app, :project_id => 10 } | ||
8 | - let(:problem) { notice.problem } | ||
9 | - let(:story_id) { 5 } | ||
10 | - let(:issue_link) { "https://www.pivotaltracker.com/story/show/#{story_id}" } | ||
11 | - | ||
12 | - it "creates an issue on Pivotal Tracker with problem params, and set issue link for problem" do | ||
13 | - project_body = "<project><id>#{tracker.project_id}</id><name>TestProject</name></project>" | ||
14 | - stub_request(:get, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}"). | ||
15 | - to_return(:status => 200, :headers => {'Location' => issue_link}, :body => project_body ) | ||
16 | - story_body = "<story><name>Test Story</name><id>#{story_id}</id></story>" | ||
17 | - stub_request(:post, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}/stories"). | ||
18 | - to_return(:status => 201, :headers => {'Location' => issue_link}, :body => story_body ) | ||
19 | - | ||
20 | - problem.app.issue_tracker.create_issue(problem, user) | ||
21 | - problem.reload | ||
22 | - | ||
23 | - requested = have_requested(:post, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}/stories") | ||
24 | - expect(WebMock).to requested.with(:headers => {'X-Trackertoken' => tracker.api_token}) | ||
25 | - expect(WebMock).to requested.with(:body => /See this exception on Errbit/) | ||
26 | - expect(WebMock).to requested.with(:body => /<name>\[#{ problem.environment }\]\[#{problem.where}\] #{problem.message.to_s.truncate(100)}<\/name>/) | ||
27 | - expect(WebMock).to requested.with(:body => /<description>.+<\/description>/m) | ||
28 | - | ||
29 | - expect(problem.issue_link).to eq issue_link | ||
30 | - end | ||
31 | - | ||
32 | - it "raises IssueTrackers::IssueTrackerError exception when invalid params and does not set issue link for problem" do | ||
33 | - project_body = "<project><id>#{tracker.project_id}</id><name>TestProject</name></project>" | ||
34 | - stub_request(:get, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}"). | ||
35 | - to_return(:status => 200, :body => project_body ) | ||
36 | - story_body = "<errors><error>Requested by can't be blank</error><error>Requested by can't be blank</error></errors>" | ||
37 | - stub_request(:post, "https://www.pivotaltracker.com/services/v3/projects/#{tracker.project_id}/stories"). | ||
38 | - to_return(:status => 422, :body => story_body ) | ||
39 | - | ||
40 | - expect{ | ||
41 | - problem.app.issue_tracker.create_issue(problem, user) | ||
42 | - }.to raise_exception(IssueTrackers::IssueTrackerError, "Requested by can't be blank") | ||
43 | - expect(problem.issue_link).to be_nil | ||
44 | - end | ||
45 | -end | ||
46 | - |
spec/models/issue_trackers/redmine_tracker_spec.rb
@@ -1,45 +0,0 @@ | @@ -1,45 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe IssueTrackers::RedmineTracker do | ||
4 | - it "should create an issue on Redmine with problem params, and set issue link for problem" do | ||
5 | - notice = Fabricate(:notice) | ||
6 | - tracker = Fabricate(:redmine_tracker, :app => notice.app, :project_id => 10) | ||
7 | - problem = notice.problem | ||
8 | - number = 5 | ||
9 | - @issue_link = "#{tracker.account}/issues/#{number}.xml?project_id=#{tracker.project_id}" | ||
10 | - body = "<issue><subject>my subject</subject><id>#{number}</id></issue>" | ||
11 | - | ||
12 | - # Build base url with account URL, and username/password basic auth | ||
13 | - base_url = tracker.account.gsub 'http://', "http://#{tracker.username}:#{tracker.password}@" | ||
14 | - | ||
15 | - stub_request(:post, "#{base_url}/issues.xml"). | ||
16 | - to_return(:status => 201, :headers => {'Location' => @issue_link}, :body => body ) | ||
17 | - | ||
18 | - problem.app.issue_tracker.create_issue(problem) | ||
19 | - problem.reload | ||
20 | - | ||
21 | - requested = have_requested(:post, "#{base_url}/issues.xml") | ||
22 | - expect(WebMock).to requested.with(:headers => {'X-Redmine-API-Key' => tracker.api_token}) | ||
23 | - expect(WebMock).to requested.with(:body => /<project-id>#{tracker.project_id}<\/project-id>/) | ||
24 | - expect(WebMock).to requested.with(:body => /<subject>\[#{ problem.environment }\]\[#{problem.where}\] #{problem.message.to_s.truncate(100)}<\/subject>/) | ||
25 | - expect(WebMock).to requested.with(:body => /<description>.+<\/description>/m) | ||
26 | - | ||
27 | - expect(problem.issue_link).to eq @issue_link.sub(/\.xml/, '') | ||
28 | - end | ||
29 | - | ||
30 | - it "should generate a url where a file with line number can be viewed" do | ||
31 | - t = Fabricate(:redmine_tracker, :account => 'http://redmine.example.com', :project_id => "errbit") | ||
32 | - expect(t.url_to_file("/example/file")). | ||
33 | - to eq 'http://redmine.example.com/projects/errbit/repository/revisions/master/entry/example/file' | ||
34 | - expect(t.url_to_file("/example/file", 25)). | ||
35 | - to eq 'http://redmine.example.com/projects/errbit/repository/revisions/master/entry/example/file#L25' | ||
36 | - end | ||
37 | - | ||
38 | - it "should use the alt_project_id to generate a file/linenumber url, if given" do | ||
39 | - t = Fabricate(:redmine_tracker, :account => 'http://redmine.example.com', | ||
40 | - :project_id => "errbit", | ||
41 | - :alt_project_id => "actual_project") | ||
42 | - expect(t.url_to_file("/example/file", 25)). | ||
43 | - to eq 'http://redmine.example.com/projects/actual_project/repository/revisions/master/entry/example/file#L25' | ||
44 | - end | ||
45 | -end |
spec/models/issue_trackers/unfuddle_issues_tracker_spec.rb
@@ -1,92 +0,0 @@ | @@ -1,92 +0,0 @@ | ||
1 | -require 'spec_helper' | ||
2 | - | ||
3 | -describe IssueTrackers::UnfuddleTracker do | ||
4 | - | ||
5 | - let(:issue_link) { "https://test.unfuddle.com/projects/15/tickets/2436" } | ||
6 | - let(:notice) { Fabricate :notice } | ||
7 | - let(:tracker) { Fabricate :unfuddle_issues_tracker, :app => notice.app } | ||
8 | - let(:problem) { notice.problem } | ||
9 | - | ||
10 | - it "should create an issue on Unfuddle Issues with problem params, and set issue link for problem" do | ||
11 | -project_xml = <<EOF | ||
12 | -<?xml version="1.0" encoding="UTF-8"?> | ||
13 | -<project> | ||
14 | - <account-id type="integer">1</account-id> | ||
15 | - <archived type="boolean">false</archived> | ||
16 | - <assignee-on-resolve>reporter</assignee-on-resolve> | ||
17 | - <backup-frequency type="integer">0</backup-frequency> | ||
18 | - <close-ticket-simultaneously-default type="boolean">false</close-ticket-simultaneously-default> | ||
19 | - <default-ticket-report-id type="integer" nil="true"></default-ticket-report-id> | ||
20 | - <description nil="true"></description> | ||
21 | - <disk-usage type="integer">27932</disk-usage> | ||
22 | - <enable-time-tracking type="boolean">true</enable-time-tracking> | ||
23 | - <id type="integer">#{tracker.project_id}</id> | ||
24 | - <s3-access-key-id></s3-access-key-id> | ||
25 | - <s3-backup-enabled type="boolean">false</s3-backup-enabled> | ||
26 | - <s3-bucket-name></s3-bucket-name> | ||
27 | - <short-name>test-project</short-name> | ||
28 | - <theme>blue</theme> | ||
29 | - <ticket-field1-active type="boolean">false</ticket-field1-active> | ||
30 | - <ticket-field1-disposition>text</ticket-field1-disposition> | ||
31 | - <ticket-field1-title>Field 1</ticket-field1-title> | ||
32 | - <ticket-field2-active type="boolean">false</ticket-field2-active> | ||
33 | - <ticket-field2-disposition>text</ticket-field2-disposition> | ||
34 | - <ticket-field2-title>Field 2</ticket-field2-title> | ||
35 | - <ticket-field3-active type="boolean">false</ticket-field3-active> | ||
36 | - <ticket-field3-disposition>text</ticket-field3-disposition> | ||
37 | - <ticket-field3-title>Field 3</ticket-field3-title> | ||
38 | - <title>test-project</title> | ||
39 | - <created-at>2011-04-25T09:21:43Z</created-at> | ||
40 | - <updated-at>2013-03-08T08:03:02Z</updated-at> | ||
41 | -</project> | ||
42 | -EOF | ||
43 | - | ||
44 | - ticket_xml =<<EOF | ||
45 | -<?xml version="1.0" encoding="UTF-8"?> | ||
46 | -<ticket> | ||
47 | - <assignee-id type="integer">40</assignee-id> | ||
48 | - <component-id type="integer" nil="true"></component-id> | ||
49 | - <description nil="true"></description> | ||
50 | - <description-format>markdown</description-format> | ||
51 | - <due-on type="date" nil="true"></due-on> | ||
52 | - <field1-value-id type="integer" nil="true"></field1-value-id> | ||
53 | - <field2-value-id type="integer" nil="true"></field2-value-id> | ||
54 | - <field3-value-id type="integer" nil="true"></field3-value-id> | ||
55 | - <hours-estimate-current type="float">1268.7</hours-estimate-current> | ||
56 | - <hours-estimate-initial type="float">0.0</hours-estimate-initial> | ||
57 | - <id type="integer">2436</id> | ||
58 | - <milestone-id type="integer">78</milestone-id> | ||
59 | - <number type="integer">119</number> | ||
60 | - <priority>3</priority> | ||
61 | - <project-id type="integer">15</project-id> | ||
62 | - <reporter-id type="integer">40</reporter-id> | ||
63 | - <resolution></resolution> | ||
64 | - <resolution-description></resolution-description> | ||
65 | - <resolution-description-format>markdown</resolution-description-format> | ||
66 | - <severity-id type="integer" nil="true"></severity-id> | ||
67 | - <status>reopened</status> | ||
68 | - <summary>TEST-ticket.</summary> | ||
69 | - <version-id type="integer" nil="true"></version-id> | ||
70 | - <created-at>2012-06-27T17:49:06Z</created-at> | ||
71 | - <updated-at>2013-03-07T16:04:05Z</updated-at> | ||
72 | -</ticket> | ||
73 | -EOF | ||
74 | - | ||
75 | - stub_request(:get, "https://#{tracker.username}:#{tracker.password}@test.unfuddle.com/api/v1/projects/#{tracker.project_id}.xml"). | ||
76 | - to_return(:status => 200, :body => project_xml, :headers => {}) | ||
77 | - | ||
78 | - | ||
79 | - stub_request(:post, "https://#{tracker.username}:#{tracker.password}@test.unfuddle.com/api/v1/projects/#{tracker.project_id}/tickets.xml"). | ||
80 | - to_return(:status => 200, :body => ticket_xml, :headers => {}) | ||
81 | - | ||
82 | - problem.app.issue_tracker.create_issue(problem) | ||
83 | - problem.reload | ||
84 | - | ||
85 | - requested = have_requested(:post,"https://#{tracker.username}:#{tracker.password}@test.unfuddle.com/api/v1/projects/#{tracker.project_id}/tickets.xml" ) | ||
86 | - expect(WebMock).to requested.with(:title => /[production][foo#bar] FooError: Too Much Bar/) | ||
87 | - expect(WebMock).to requested.with(:content => /See this exception on Errbit/) | ||
88 | - | ||
89 | - expect(problem.issue_link).to eq issue_link | ||
90 | - expect(problem.issue_type).to eq IssueTrackers::UnfuddleTracker::Label | ||
91 | - end | ||
92 | -end |
spec/models/problem_spec.rb
@@ -370,6 +370,53 @@ describe Problem do | @@ -370,6 +370,53 @@ describe Problem do | ||
370 | end | 370 | end |
371 | end | 371 | end |
372 | 372 | ||
373 | + describe "#issue_type" do | ||
374 | + context "without issue_type fill in Problem" do | ||
375 | + let(:problem) do | ||
376 | + Problem.new(:app => app) | ||
377 | + end | ||
378 | + | ||
379 | + let(:app) do | ||
380 | + App.new(:issue_tracker => issue_tracker) | ||
381 | + end | ||
382 | + | ||
383 | + context "without issue_tracker associate to app" do | ||
384 | + let(:issue_tracker) do | ||
385 | + nil | ||
386 | + end | ||
387 | + it 'return nil' do | ||
388 | + expect(problem.issue_type).to be_nil | ||
389 | + end | ||
390 | + end | ||
391 | + | ||
392 | + context "with issue_tracker valid associate to app" do | ||
393 | + let(:issue_tracker) do | ||
394 | + Fabricate(:issue_tracker) | ||
395 | + end | ||
396 | + | ||
397 | + it 'return the issue_tracker label' do | ||
398 | + expect(problem.issue_type).to eql 'fake' | ||
399 | + end | ||
400 | + end | ||
401 | + | ||
402 | + context "with issue_tracker not valid associate to app" do | ||
403 | + let(:issue_tracker) do | ||
404 | + IssueTracker.new(:type_tracker => 'fake') | ||
405 | + end | ||
406 | + | ||
407 | + it 'return nil' do | ||
408 | + expect(problem.issue_type).to be_nil | ||
409 | + end | ||
410 | + end | ||
411 | + end | ||
412 | + | ||
413 | + context "with issue_type fill in Problem" do | ||
414 | + it 'return the value associate' do | ||
415 | + expect(Problem.new(:issue_type => 'foo').issue_type).to eql 'foo' | ||
416 | + end | ||
417 | + end | ||
418 | + end | ||
419 | + | ||
373 | 420 | ||
374 | end | 421 | end |
375 | 422 |
spec/spec_helper.rb
@@ -19,11 +19,13 @@ end | @@ -19,11 +19,13 @@ end | ||
19 | 19 | ||
20 | require File.expand_path("../../config/environment", __FILE__) | 20 | require File.expand_path("../../config/environment", __FILE__) |
21 | require 'rspec/rails' | 21 | require 'rspec/rails' |
22 | +require 'email_spec' | ||
22 | require 'database_cleaner' | 23 | require 'database_cleaner' |
23 | require 'webmock/rspec' | 24 | require 'webmock/rspec' |
24 | require 'xmpp4r' | 25 | require 'xmpp4r' |
25 | require 'xmpp4r/muc' | 26 | require 'xmpp4r/muc' |
26 | require 'mongoid-rspec' | 27 | require 'mongoid-rspec' |
28 | +require 'errbit_plugin/issue_trackers/fake' | ||
27 | 29 | ||
28 | 30 | ||
29 | # Requires supporting files with custom matchers and macros, etc, | 31 | # Requires supporting files with custom matchers and macros, etc, |
@@ -54,8 +56,8 @@ RSpec.configure do |config| | @@ -54,8 +56,8 @@ RSpec.configure do |config| | ||
54 | init_haml_helpers | 56 | init_haml_helpers |
55 | end | 57 | end |
56 | 58 | ||
57 | - config.after(:all) do | ||
58 | - WebMock.disable_net_connect! :allow => /coveralls\.io/ | 59 | + config.before(:all) do |
60 | + WebMock.disable_net_connect! :allow => /coveralls\.io|127\.0\.0\.1/ | ||
59 | end | 61 | end |
60 | end | 62 | end |
61 | 63 |
spec/views/apps/edit.html.haml_spec.rb
@@ -2,8 +2,10 @@ require 'spec_helper' | @@ -2,8 +2,10 @@ require 'spec_helper' | ||
2 | 2 | ||
3 | describe "apps/edit.html.haml" do | 3 | describe "apps/edit.html.haml" do |
4 | let(:app) { stub_model(App) } | 4 | let(:app) { stub_model(App) } |
5 | + let(:app_decorate) { AppDecorator.new(app) } | ||
5 | before do | 6 | before do |
6 | view.stub(:app).and_return(app) | 7 | view.stub(:app).and_return(app) |
8 | + view.stub(:app_decorate).and_return(app_decorate) | ||
7 | controller.stub(:current_user) { stub_model(User) } | 9 | controller.stub(:current_user) { stub_model(User) } |
8 | end | 10 | end |
9 | 11 |
spec/views/apps/new.html.haml_spec.rb
@@ -2,8 +2,10 @@ require 'spec_helper' | @@ -2,8 +2,10 @@ require 'spec_helper' | ||
2 | 2 | ||
3 | describe "apps/new.html.haml" do | 3 | describe "apps/new.html.haml" do |
4 | let(:app) { stub_model(App) } | 4 | let(:app) { stub_model(App) } |
5 | + let(:app_decorate) { AppDecorator.new(app) } | ||
5 | before do | 6 | before do |
6 | view.stub(:app).and_return(app) | 7 | view.stub(:app).and_return(app) |
8 | + view.stub(:app_decorate).and_return(app_decorate) | ||
7 | controller.stub(:current_user) { stub_model(User) } | 9 | controller.stub(:current_user) { stub_model(User) } |
8 | end | 10 | end |
9 | 11 |
spec/views/problems/show.html.haml_spec.rb
@@ -3,6 +3,28 @@ require 'spec_helper' | @@ -3,6 +3,28 @@ require 'spec_helper' | ||
3 | describe "problems/show.html.haml" do | 3 | describe "problems/show.html.haml" do |
4 | let(:problem) { Fabricate(:problem) } | 4 | let(:problem) { Fabricate(:problem) } |
5 | let(:comment) { Fabricate(:comment) } | 5 | let(:comment) { Fabricate(:comment) } |
6 | + let(:pivotal_tracker) { | ||
7 | + Class.new(ErrbitPlugin::IssueTracker) do | ||
8 | + def self.label; 'pivotal'; end | ||
9 | + def initialize(app, params); end | ||
10 | + def configured?; true; end | ||
11 | + def comments_allowed?; false; end | ||
12 | + end | ||
13 | + } | ||
14 | + let(:github_tracker) { | ||
15 | + Class.new(ErrbitPlugin::IssueTracker) do | ||
16 | + def initialize(app, params); end | ||
17 | + def label; 'github'; end | ||
18 | + def configured?; true; end | ||
19 | + def comments_allowed?; false; end | ||
20 | + end | ||
21 | + } | ||
22 | + let(:trackers) { | ||
23 | + { | ||
24 | + 'github' => github_tracker, | ||
25 | + 'pivotal' => pivotal_tracker | ||
26 | + } | ||
27 | + } | ||
6 | 28 | ||
7 | before do | 29 | before do |
8 | view.stub(:app).and_return(problem.app) | 30 | view.stub(:app).and_return(problem.app) |
@@ -16,7 +38,8 @@ describe "problems/show.html.haml" do | @@ -16,7 +38,8 @@ describe "problems/show.html.haml" do | ||
16 | end | 38 | end |
17 | 39 | ||
18 | def with_issue_tracker(tracker, problem) | 40 | def with_issue_tracker(tracker, problem) |
19 | - problem.app.issue_tracker = tracker.new :api_token => "token token token", :project_id => "1234" | 41 | + problem.app.issue_tracker = IssueTracker.new :type_tracker => tracker, :options => {:api_token => "token token token", :project_id => "1234"} |
42 | + ErrbitPlugin::Registry.stub(:issue_trackers).and_return(trackers) | ||
20 | view.stub(:problem).and_return(problem) | 43 | view.stub(:problem).and_return(problem) |
21 | view.stub(:app).and_return(problem.app) | 44 | view.stub(:app).and_return(problem.app) |
22 | end | 45 | end |
@@ -75,7 +98,7 @@ describe "problems/show.html.haml" do | @@ -75,7 +98,7 @@ describe "problems/show.html.haml" do | ||
75 | 98 | ||
76 | it 'should allow creating issue for github if application has a github tracker' do | 99 | it 'should allow creating issue for github if application has a github tracker' do |
77 | problem = Fabricate(:problem_with_comments, :app => Fabricate(:app, :github_repo => "test_user/test_repo")) | 100 | problem = Fabricate(:problem_with_comments, :app => Fabricate(:app, :github_repo => "test_user/test_repo")) |
78 | - with_issue_tracker(GithubIssuesTracker, problem) | 101 | + with_issue_tracker("github", problem) |
79 | view.stub(:problem).and_return(problem) | 102 | view.stub(:problem).and_return(problem) |
80 | view.stub(:app).and_return(problem.app) | 103 | view.stub(:app).and_return(problem.app) |
81 | render | 104 | render |
@@ -96,30 +119,53 @@ describe "problems/show.html.haml" do | @@ -96,30 +119,53 @@ describe "problems/show.html.haml" do | ||
96 | 119 | ||
97 | end | 120 | end |
98 | 121 | ||
99 | - context "with lighthouse tracker on app" do | ||
100 | - let(:app) { App.new(:new_record => false, :issue_tracker => tracker ) } | ||
101 | - let(:tracker) { | ||
102 | - IssueTrackers::LighthouseTracker.new(:project_id => 'x') | ||
103 | - } | ||
104 | - context "with problem without issue link" do | 122 | + context "with tracker associate on app" do |
123 | + before do | ||
124 | + with_issue_tracker("pivotal", problem) | ||
125 | + end | ||
126 | + | ||
127 | + context "with app having github_repo" do | ||
128 | + let(:app) { App.new(:new_record => false, :github_repo => 'foo/bar') } | ||
105 | let(:problem){ Problem.new(:new_record => false, :app => app) } | 129 | let(:problem){ Problem.new(:new_record => false, :app => app) } |
106 | - it 'not see link if no issue tracker' do | ||
107 | - view.stub(:problem).and_return(problem) | ||
108 | - view.stub(:app).and_return(problem.app) | 130 | + |
131 | + before do | ||
132 | + problem.issue_link = nil | ||
133 | + user = Fabricate(:user, :github_login => 'test_user', :github_oauth_token => 'abcdef') | ||
134 | + controller.stub(:current_user) { user } | ||
135 | + end | ||
136 | + | ||
137 | + it 'links to the associated tracker' do | ||
109 | render | 138 | render |
110 | - expect(view.content_for(:action_bar)).to match(/create issue/) | 139 | + expect(view.content_for(:action_bar)).to match(".pivotal_create.create-issue") |
111 | end | 140 | end |
112 | 141 | ||
142 | + it 'does not link to github' do | ||
143 | + render | ||
144 | + expect(view.content_for(:action_bar)).to_not match(".github_create.create-issue") | ||
145 | + end | ||
113 | end | 146 | end |
114 | 147 | ||
115 | - context "with problem with issue link" do | ||
116 | - let(:problem){ Problem.new(:new_record => false, :app => app, :issue_link => 'http://foo') } | 148 | + context "without app having github_repo" do |
149 | + context "with problem without issue link" do | ||
150 | + before do | ||
151 | + problem.issue_link = nil | ||
152 | + end | ||
153 | + it 'not see link if no issue tracker' do | ||
154 | + render | ||
155 | + expect(view.content_for(:action_bar)).to match(/create issue/) | ||
156 | + end | ||
117 | 157 | ||
118 | - it 'not see link if no issue tracker' do | ||
119 | - view.stub(:problem).and_return(problem) | ||
120 | - view.stub(:app).and_return(problem.app) | ||
121 | - render | ||
122 | - expect(view.content_for(:action_bar)).to_not match(/create issue/) | 158 | + end |
159 | + | ||
160 | + context "with problem with issue link" do | ||
161 | + before do | ||
162 | + problem.issue_link = 'http://foo' | ||
163 | + end | ||
164 | + | ||
165 | + it 'not see link if no issue tracker' do | ||
166 | + render | ||
167 | + expect(view.content_for(:action_bar)).to_not match(/create issue/) | ||
168 | + end | ||
123 | end | 169 | end |
124 | end | 170 | end |
125 | 171 | ||
@@ -147,7 +193,7 @@ describe "problems/show.html.haml" do | @@ -147,7 +193,7 @@ describe "problems/show.html.haml" do | ||
147 | context "with issue tracker" do | 193 | context "with issue tracker" do |
148 | it 'should not display the comments section' do | 194 | it 'should not display the comments section' do |
149 | problem = Fabricate(:problem) | 195 | problem = Fabricate(:problem) |
150 | - with_issue_tracker(PivotalLabsTracker, problem) | 196 | + with_issue_tracker("pivotal", problem) |
151 | render | 197 | render |
152 | expect(view.view_flow.get(:comments)).to be_blank | 198 | expect(view.view_flow.get(:comments)).to be_blank |
153 | end | 199 | end |
@@ -155,7 +201,7 @@ describe "problems/show.html.haml" do | @@ -155,7 +201,7 @@ describe "problems/show.html.haml" do | ||
155 | it 'should display existing comments' do | 201 | it 'should display existing comments' do |
156 | problem = Fabricate(:problem_with_comments) | 202 | problem = Fabricate(:problem_with_comments) |
157 | problem.reload | 203 | problem.reload |
158 | - with_issue_tracker(PivotalLabsTracker, problem) | 204 | + with_issue_tracker("pivotal", problem) |
159 | render | 205 | render |
160 | 206 | ||
161 | expect(view.content_for(:comments)).to include('Test comment') | 207 | expect(view.content_for(:comments)).to include('Test comment') |