rocketjob_mission_control 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/rocket_job_mission_control/base.js.coffee +3 -13
  3. data/app/assets/stylesheets/rocket_job_mission_control/base.scss +38 -5
  4. data/app/assets/stylesheets/rocket_job_mission_control/bootstrap_and_overrides.scss +1 -1
  5. data/app/controllers/rocket_job_mission_control/active_processes_controller.rb +8 -7
  6. data/app/controllers/rocket_job_mission_control/jobs/index_filters_controller.rb +14 -14
  7. data/app/controllers/rocket_job_mission_control/jobs_controller.rb +9 -4
  8. data/app/controllers/rocket_job_mission_control/workers_controller.rb +1 -1
  9. data/app/datatables/rocket_job_mission_control/active_processes_datatable.rb +11 -11
  10. data/app/datatables/rocket_job_mission_control/completed_jobs_datatable.rb +2 -1
  11. data/app/datatables/rocket_job_mission_control/interrupted_jobs_datatable.rb +1 -0
  12. data/app/datatables/rocket_job_mission_control/jobs_datatable.rb +40 -3
  13. data/app/datatables/rocket_job_mission_control/queued_jobs_datatable.rb +1 -0
  14. data/app/datatables/rocket_job_mission_control/running_jobs_datatable.rb +1 -0
  15. data/app/datatables/rocket_job_mission_control/scheduled_jobs_datatable.rb +1 -6
  16. data/app/helpers/rocket_job_mission_control/jobs_helper.rb +1 -9
  17. data/app/views/layouts/rocket_job_mission_control/partials/_header.html.haml +3 -4
  18. data/app/views/rocket_job_mission_control/active_processes/index.html.haml +1 -1
  19. data/app/views/rocket_job_mission_control/dirmon_entries/_sidebar.html.haml +1 -1
  20. data/app/views/rocket_job_mission_control/jobs/edit.html.haml +34 -0
  21. data/app/views/rocket_job_mission_control/jobs/index.html.haml +2 -1
  22. data/app/views/rocket_job_mission_control/jobs/index_filters/aborted.html.haml +2 -1
  23. data/app/views/rocket_job_mission_control/jobs/index_filters/completed.html.haml +2 -1
  24. data/app/views/rocket_job_mission_control/jobs/index_filters/failed.html.haml +2 -1
  25. data/app/views/rocket_job_mission_control/jobs/index_filters/paused.html.haml +2 -1
  26. data/app/views/rocket_job_mission_control/jobs/index_filters/queued.html.haml +2 -1
  27. data/app/views/rocket_job_mission_control/jobs/index_filters/running.html.haml +2 -1
  28. data/app/views/rocket_job_mission_control/jobs/index_filters/scheduled.html.haml +2 -2
  29. data/app/views/rocket_job_mission_control/jobs/show.html.haml +21 -36
  30. data/app/views/rocket_job_mission_control/workers/index.html.haml +1 -1
  31. data/config/routes.rb +1 -1
  32. data/lib/rocket_job_mission_control/engine.rb +6 -0
  33. data/lib/rocket_job_mission_control/version.rb +1 -1
  34. data/spec/controllers/dirmon_entries/index_filters_controller_spec.rb +74 -0
  35. data/spec/controllers/jobs/index_filters_controller_spec.rb +65 -0
  36. data/spec/controllers/jobs_controller_spec.rb +1 -1
  37. data/spec/controllers/workers/index_filters_controller_spec.rb +59 -0
  38. data/spec/dummy/log/test.log +792 -0
  39. data/spec/rails_helper.rb +2 -0
  40. metadata +16 -8
@@ -1,6 +1,5 @@
1
1
  module RocketJobMissionControl
2
2
  class ScheduledJobsDatatable < JobsDatatable
3
- delegate :run_now_job_path, to: :@view
4
3
  private
5
4
 
6
5
  def data
@@ -10,7 +9,7 @@ module RocketJobMissionControl
10
9
  '1' => h(job.description.try(:truncate, 50)),
11
10
  '2' => h(time_till_run(job)),
12
11
  '3' => h(cron_schedule(job)),
13
- '4' => run_now(job),
12
+ '4' => action_buttons(job),
14
13
  'DT_RowClass' => "card callout callout-#{job.state}"
15
14
  }
16
15
  end
@@ -28,9 +27,5 @@ module RocketJobMissionControl
28
27
  def cron_schedule(job)
29
28
  job.cron_schedule if job.respond_to?(:cron_schedule)
30
29
  end
31
-
32
- def run_now(job)
33
- "#{ link_to 'Run', run_now_job_path(job), method: :patch, class: 'btn btn-default btn-xs', data: { confirm: "Run #{job.class.name} now?" } }"
34
- end
35
30
  end
36
31
  end
@@ -20,20 +20,12 @@ module RocketJobMissionControl
20
20
  action,
21
21
  path,
22
22
  method: http_method,
23
+ title: "#{action} job",
23
24
  class: 'btn btn-default',
24
25
  data: {confirm: t(:confirm, scope: [:job, :action], action: action)}
25
26
  )
26
27
  end
27
28
 
28
- def view_logs_link(job)
29
- link_to(
30
- 'view logs',
31
- "http://localhost:5601/app/kibana#/discover?_g=(refreshInterval:(display:'5%20seconds',pause:!f,section:1,value:5000),time:(from:now-15m,mode:quick,to:now))&_a=(columns:!(level,duration,name,message,host),index:'semantic_logger-*',interval:auto,query:(query_string:(analyze_wildcard:!t,query:'tags:#{job.id.to_s}')),sort:!(timestamp,desc))",
32
- class: 'btn btn-default',
33
- target: '_blank'
34
- )
35
- end
36
-
37
29
  def job_states
38
30
  @job_states ||= RocketJob::Job.aasm.states.map { |state| state.name.to_s }
39
31
  end
@@ -7,9 +7,8 @@
7
7
  %span.icon-bar
8
8
  %span.icon-bar
9
9
  %a.navbar-brand.brand{ href: 'http://rocketjob.io', target: '_blank'}
10
- = image_tag('rocket_job_mission_control/rocket-icon-64x64.png', size: '32x32')
11
- Rocket
12
- %span Job
10
+ = image_tag('rocket_job_mission_control/rocket-icon-64x64.png')
11
+ Rocket Job
13
12
  .navbar-collapse.collapse
14
13
  %ul.nav.navbar-nav
15
14
  %li
@@ -30,4 +29,4 @@
30
29
  Directory Monitors
31
30
  %ul.nav.navbar-nav.navbar-right
32
31
  %li
33
- %a{:href => main_app.root_path } Home
32
+ %a{:href => '/' } Home
@@ -2,7 +2,7 @@
2
2
  .list
3
3
  .row
4
4
  .col-sm-10
5
- %h2 Active Processes
5
+ %h2 Active Workers
6
6
  .col-sm-2
7
7
  .btn.btn-default.pull-right.dt-reload{ data: { behavior: 'reload' } }
8
8
  %i.fa.fa-refresh
@@ -2,7 +2,7 @@
2
2
  %li
3
3
  = link_to(new_dirmon_entry_path) do
4
4
  %i.fa.fa-plus
5
- New Entry
5
+ Create
6
6
  %li
7
7
  = link_to(dirmon_entries_path) do
8
8
  %i.fa.fa-list
@@ -0,0 +1,34 @@
1
+ .container-fluid
2
+ .lead= "Edit #{@job._type}"
3
+
4
+ - if @job.errors.present?
5
+ .alert.alert-alert
6
+ Invalid Job!
7
+ - @job.errors.messages.each_pair do |field, message|
8
+ .message
9
+ = "#{field.to_s.humanize} is not valid."
10
+
11
+ = form_for(@job, as: :job, url: job_path(@job)) do |f|
12
+ .row
13
+ .col-md-8
14
+ = f.label :description
15
+ = f.text_area :description, value: @job.description, class: 'form-control description-block'
16
+ .col-md-4
17
+ = f.label :priority
18
+ = f.number_field :priority, value: @job.priority, class: 'form-control'
19
+ .col-md-4
20
+ = f.label :log_level
21
+ = f.select :log_level, ['', 'trace', 'debug', 'info', 'warn', 'error', 'fatal'], {}, { class: 'form-control' }
22
+ - if @job.method_exists?(:max_active_workers)
23
+ .col-md-4
24
+ = f.label :max_active_workers
25
+ = f.number_field :max_active_workers, class: 'form-control'
26
+ - if @job.method_exists?(:cron_schedule)
27
+ .col-md-4
28
+ = f.label :cron_schedule
29
+ = f.text_field :cron_schedule, class: 'form-control'
30
+ .row
31
+ .col-md-12
32
+ .buttons
33
+ = f.submit 'Update', class: 'btn btn-primary'
34
+ = link_to 'Cancel', :back, class: 'btn btn-default'
@@ -13,12 +13,13 @@
13
13
  %th Description
14
14
  %th Created
15
15
  %th Duration
16
+ %th Actions
16
17
  %tbody
17
18
 
18
19
  :javascript
19
20
  jQuery(function() {
20
21
  new RjmcDatatable(
21
22
  $(".jobs-datatable"),
22
- [{data: '0'}, {data: '1'}, {data: '2'}, {data: '3', orderable: false}]
23
+ [{data: '0'}, {data: '1'}, {data: '2'}, {data: '3', orderable: false}, {data: '4', orderable: false}]
23
24
  );
24
25
  });
@@ -12,11 +12,12 @@
12
12
  %th Class
13
13
  %th Description
14
14
  %th Aborted
15
+ %th Actions
15
16
  %tbody
16
17
 
17
18
  :javascript
18
19
  jQuery(function() {
19
20
  new RjmcDatatable(
20
21
  $(".jobs-datatable"),
21
- [{data: '0'}, {data: '1'}, {data: '2'}]);
22
+ [{data: '0', width: '30%'}, {data: '1', width: '30%'}, {data: '2'}, {data: '3', orderable: false}]);
22
23
  });
@@ -13,11 +13,12 @@
13
13
  %th Description
14
14
  %th Duration
15
15
  %th Completed
16
+ %th Actions
16
17
  %tbody
17
18
 
18
19
  :javascript
19
20
  jQuery(function() {
20
21
  new RjmcDatatable(
21
22
  $(".jobs-datatable"),
22
- [{data: '0'}, {data: '1'}, {data: '2', orderable: false}, {data: '3'}]);
23
+ [{data: '0'}, {data: '1'}, {data: '2', orderable: false}, {data: '3'}, {data: '4', orderable: false}]);
23
24
  });
@@ -12,11 +12,12 @@
12
12
  %th Class
13
13
  %th Description
14
14
  %th Failed
15
+ %th Actions
15
16
  %tbody
16
17
 
17
18
  :javascript
18
19
  jQuery(function() {
19
20
  new RjmcDatatable(
20
21
  $(".jobs-datatable"),
21
- [{data: '0'}, {data: '1'}, {data: '2'}]);
22
+ [{data: '0', width: '30%'}, {data: '1', width: '30%'}, {data: '2'}, {data: '3', orderable: false}]);
22
23
  });
@@ -12,11 +12,12 @@
12
12
  %th Class
13
13
  %th Description
14
14
  %th Paused
15
+ %th Actions
15
16
  %tbody
16
17
 
17
18
  :javascript
18
19
  jQuery(function() {
19
20
  new RjmcDatatable(
20
21
  $(".jobs-datatable"),
21
- [{data: '0'}, {data: '1'}, {data: '2'}]);
22
+ [{data: '0', width: '30%'}, {data: '1', width: '30%'}, {data: '2'}, {data: '3', orderable: false}]);
22
23
  });
@@ -13,11 +13,12 @@
13
13
  %th Description
14
14
  %th Priority
15
15
  %th Queued For
16
+ %th Actions
16
17
  %tbody
17
18
 
18
19
  :javascript
19
20
  jQuery(function() {
20
21
  new RjmcDatatable(
21
22
  $(".jobs-datatable"),
22
- [{data: '0'}, {data: '1'}, {data: '2'}, {data: '3', orderable: false}]);
23
+ [{data: '0'}, {data: '1'}, {data: '2'}, {data: '3', orderable: false}, {data: '4', orderable: false}]);
23
24
  });
@@ -14,11 +14,12 @@
14
14
  %th Progress
15
15
  %th Priority
16
16
  %th Started
17
+ %th Actions
17
18
  %tbody
18
19
 
19
20
  :javascript
20
21
  jQuery(function() {
21
22
  new RjmcDatatable(
22
23
  $(".jobs-datatable"),
23
- [{data: '0'}, {data: '1'}, {data: '2', orderable: false}, {data: '3'}, {data: '4'}]);
24
+ [{data: '0'}, {data: '1'}, {data: '2', orderable: false}, {data: '3'}, {data: '4'}, {data: '5', orderable: false}]);
24
25
  });
@@ -13,12 +13,12 @@
13
13
  %th Description
14
14
  %th Runs in
15
15
  %th Cron Schedule
16
- %th Run Now
16
+ %th Actions
17
17
  %tbody
18
18
 
19
19
  :javascript
20
20
  jQuery(function() {
21
21
  new RjmcDatatable(
22
22
  $(".jobs-datatable"),
23
- [{data: '0'}, {data: '1'}, {data: '2', orderable: false}, {data: '3'}, {data: '4', orderable: false}]);
23
+ [{data: '0'}, {data: '1'}, {data: '2'}, {data: '3'}, {data: '4', orderable: false}]);
24
24
  });
@@ -8,46 +8,31 @@
8
8
  .right{ class: @job.state.to_s }
9
9
  = @job.state
10
10
 
11
- .priority.pull-right
12
- = form_for(@job, as: :job, url: job_path(@job), html: {method: :patch}) do |f|
11
+ .btn-toolbar.job-actions.pull-right
12
+ .btn-group
13
+ - valid_events = @job.aasm.events.collect{ |e| e.name }
14
+ - if valid_events.include?(:abort)
15
+ = job_action_link('Abort', rocket_job_mission_control.abort_job_path(@job), :patch)
13
16
 
14
- .input-group.priority-group
15
- = f.text_field(:priority, value: @job.priority, class: 'form-control')
17
+ - if @job.completed? || @job.aborted?
18
+ = job_action_link('Destroy', rocket_job_mission_control.job_path(@job), :delete)
16
19
 
17
- .input-group-btn-vertical
18
- %button#increase_priority.btn.btn-default.up{ type: 'button' }
19
- %i.fa.fa-caret-up
20
+ - if valid_events.include?(:fail)
21
+ = job_action_link('Fail', rocket_job_mission_control.fail_job_path(@job), :patch)
20
22
 
21
- %button#decrease_priority.btn.btn-default.down{ type: 'button' }
22
- %i.fa.fa-caret-down
23
+ - if valid_events.include?(:pause)
24
+ = job_action_link('Pause', rocket_job_mission_control.pause_job_path(@job), :patch)
23
25
 
24
- = submit_tag "set", class: "btn btn-sm btn-primary priority-set"
26
+ - if valid_events.include?(:resume)
27
+ = job_action_link('Resume', rocket_job_mission_control.resume_job_path(@job), :patch)
25
28
 
26
- .job-actions.btn-group.pull-right{ style: 'margin-right: 1em;' }
27
- - valid_events = @job.aasm.events.collect{ |e| e.name }
28
- - if valid_events.include?(:abort)
29
- = job_action_link('abort', rocket_job_mission_control.abort_job_path(@job), :patch)
29
+ - if valid_events.include?(:retry)
30
+ = job_action_link('Retry', rocket_job_mission_control.retry_job_path(@job), :patch)
30
31
 
31
- - if @job.completed? || @job.aborted?
32
- = job_action_link('destroy', rocket_job_mission_control.job_path(@job), :delete)
32
+ - if @job.respond_to?(:input) && @job.input.failed_count > 0
33
+ = link_to('view errors', job_failures_path(@job), class: 'btn btn-default')
34
+ .btn-group
35
+ = link_to "Edit", edit_job_path(@job), class: "btn btn-default"
33
36
 
34
- - if valid_events.include?(:fail)
35
- = job_action_link('fail', rocket_job_mission_control.fail_job_path(@job), :patch)
36
-
37
- - if valid_events.include?(:pause)
38
- = job_action_link('pause', rocket_job_mission_control.pause_job_path(@job), :patch)
39
-
40
- - if valid_events.include?(:resume)
41
- = job_action_link('resume', rocket_job_mission_control.resume_job_path(@job), :patch)
42
-
43
- - if valid_events.include?(:retry)
44
- = job_action_link('retry', rocket_job_mission_control.retry_job_path(@job), :patch)
45
-
46
- - if @job.respond_to?(:input) && @job.input.failed_count > 0
47
- = link_to('view errors', job_failures_path(@job), class: 'btn btn-default')
48
-
49
- = view_logs_link(@job)
50
-
51
- .clearfix
52
-
53
- = render partial: 'status', locals: { job: @job }
37
+ .col-md-12
38
+ = render partial: 'status', locals: { job: @job }
@@ -33,5 +33,5 @@
33
33
  jQuery(function() {
34
34
  new RjmcDatatable(
35
35
  $('.workers-datatable'),
36
- [{data: '0'}, {data: '1', orderable: false}, {data: '2', orderable: false}, {data: '3', orderable: false}, {data: '4', orderable: false}]);
36
+ [{data: '0'}, {data: '1', orderable: false}, {data: '2'}, {data: '3'}, {data: '4', orderable: false}]);
37
37
  });
@@ -1,6 +1,6 @@
1
1
  RocketJobMissionControl::Engine.routes.draw do
2
2
 
3
- resources :jobs, only: [:index, :show, :update, :destroy] do
3
+ resources :jobs, only: [:index, :show, :update, :destroy, :edit] do
4
4
  collection do
5
5
  get :running, to: 'jobs/index_filters#running'
6
6
  get :scheduled, to: 'jobs/index_filters#scheduled'
@@ -13,5 +13,11 @@ module RocketJobMissionControl
13
13
  require 'coffee-rails'
14
14
  require 'kaminari'
15
15
  require 'jquery-datatables-rails'
16
+
17
+ config.to_prepare do
18
+ Rails.application.config.assets.precompile += %w(
19
+ rocket_job_mission_control/rocket-icon-64x64.png
20
+ )
21
+ end
16
22
  end
17
23
  end
@@ -1,3 +1,3 @@
1
1
  module RocketJobMissionControl
2
- VERSION = '2.0.0'
2
+ VERSION = '2.1.0'
3
3
  end
@@ -0,0 +1,74 @@
1
+ require 'rails_helper'
2
+
3
+ class AJob < RocketJob::Job
4
+ def perform(id)
5
+ id
6
+ end
7
+ end
8
+
9
+ module RocketJobMissionControl
10
+ module DirmonEntries
11
+ RSpec.describe IndexFiltersController do
12
+ routes { Engine.routes }
13
+
14
+ states = %w(pending enabled failed disabled)
15
+
16
+ states.each_with_index do |state, i|
17
+ describe "GET ##{state}" do
18
+ describe "with no #{state} dirmons" do
19
+ before do
20
+ get state.to_sym
21
+ end
22
+
23
+ it "succeeds" do
24
+ expect(response.status).to be(200)
25
+ end
26
+
27
+ it 'renders template' do
28
+ expect(response).to render_template(state)
29
+ end
30
+
31
+ it "returns no jobs" do
32
+ expect(assigns(:dirmons).count).to eq(0)
33
+ end
34
+ end
35
+
36
+ describe "with #{state} dirmons" do
37
+ let(:not_state) { states[i-1] }
38
+ let!(:state_dirmon) { RocketJob::DirmonEntry.create!(state: state,
39
+ pattern: '21',
40
+ arguments: ['42'],
41
+ job_class_name: 'AJob') }
42
+
43
+ before do
44
+ RocketJob::DirmonEntry.create!(
45
+ name: 'Test',
46
+ state: not_state,
47
+ arguments: ['42'],
48
+ pattern: '21',
49
+ job_class_name: 'AJob')
50
+ get state.to_sym
51
+ end
52
+
53
+ after do
54
+ DatabaseCleaner.clean
55
+ end
56
+
57
+ it "succeeds" do
58
+ expect(response.status).to be(200)
59
+ end
60
+
61
+ it 'renders template' do
62
+ expect(response).to render_template(state)
63
+ end
64
+
65
+ it "grabs a filtered list of dirmons" do
66
+ expect(assigns(:dirmons)).to match_array([state_dirmon])
67
+
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,65 @@
1
+ require 'rails_helper'
2
+
3
+ module RocketJobMissionControl
4
+ module Jobs
5
+ RSpec.describe IndexFiltersController do
6
+ routes { Engine.routes }
7
+
8
+ states = %w(running paused completed aborted failed queued scheduled)
9
+
10
+ states.each_with_index do |state, i|
11
+ describe "GET ##{state}" do
12
+ describe "with no #{state} jobs" do
13
+ before do
14
+ get state.to_sym
15
+ end
16
+
17
+ it "succeeds" do
18
+ expect(response.status).to be(200)
19
+ end
20
+
21
+ it 'renders template' do
22
+ expect(response).to render_template(state)
23
+ end
24
+
25
+ it "returns no jobs" do
26
+ expect(assigns(:jobs).count).to eq(0)
27
+ end
28
+ end
29
+
30
+ describe "with #{state} jobs" do
31
+ let(:not_state) { states[i-1] }
32
+ let(:state_job) {
33
+ if state == 'scheduled'
34
+ RocketJob::Job.create(state: :queued, run_at: Date.tomorrow)
35
+ else
36
+ RocketJob::Job.create(state: state)
37
+ end
38
+ }
39
+
40
+ before do
41
+ RocketJob::Job.create(state: not_state)
42
+ get state.to_sym
43
+ end
44
+
45
+ after do
46
+ DatabaseCleaner.clean
47
+ end
48
+
49
+ it "succeeds" do
50
+ expect(response.status).to be(200)
51
+ end
52
+
53
+ it 'renders template' do
54
+ expect(response).to render_template(state)
55
+ end
56
+
57
+ it "grabs a filtered list of rocket jobs" do
58
+ expect(assigns(:jobs)).to match_array([state_job])
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end