marty 9.5.1 → 10.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/README.md +40 -31
  4. data/app/components/marty/background_job/schedule_jobs_dashboard.rb +2 -2
  5. data/app/components/marty/background_job/schedule_jobs_grid.rb +22 -5
  6. data/app/components/marty/background_job/schedule_jobs_logs.rb +12 -0
  7. data/app/components/marty/base_rule_view.rb +0 -4
  8. data/app/components/marty/delorean_rule_view.rb +11 -5
  9. data/app/components/marty/promise_view.rb +15 -0
  10. data/app/jobs/marty/cron_job.rb +30 -28
  11. data/app/jobs/marty/delorean_background_job.rb +8 -0
  12. data/app/models/marty/background_job/schedule.rb +24 -1
  13. data/app/models/marty/delorean_rule.rb +1 -1
  14. data/app/models/marty/vw_promise.rb +6 -4
  15. data/app/services/marty/background_job/fetch_missing_in_schedule_cron_jobs.rb +8 -6
  16. data/app/services/marty/background_job/update_schedule.rb +15 -8
  17. data/app/services/marty/jobs/schedule.rb +8 -4
  18. data/app/services/marty/promises/delorean/create.rb +2 -1
  19. data/app/services/marty/promises/ruby/create.rb +2 -1
  20. data/config/locales/en.yml +1 -0
  21. data/db/migrate/510_schedule_job_to_remove_old_promises.rb +4 -2
  22. data/db/migrate/523_add_timeout_to_promises.rb +5 -0
  23. data/db/migrate/524_add_timeout_to_promise_view.rb +45 -0
  24. data/db/migrate/525_add_arguments_to_jobs_schedules.rb +10 -0
  25. data/db/migrate/526_add_schedule_id_to_delayed_jobs.rb +14 -0
  26. data/lib/marty/delayed_job/queue_adapter.rb +38 -0
  27. data/lib/marty/delayed_job/scheduled_job_plugin.rb +9 -6
  28. data/lib/marty/diagnostic/environment_variables.rb +1 -1
  29. data/lib/marty/monkey.rb +11 -0
  30. data/lib/marty/promise_job.rb +2 -1
  31. data/lib/marty/promise_ruby_job.rb +2 -1
  32. data/lib/marty/version.rb +1 -1
  33. data/spec/dummy/.foreman +2 -0
  34. data/spec/dummy/Procfile +2 -0
  35. data/spec/dummy/app/jobs/test_failing_job.rb +5 -1
  36. data/spec/dummy/app/models/gemini/my_rule.rb +2 -0
  37. data/spec/dummy/app/models/gemini/xyz_rule.rb +2 -0
  38. data/spec/dummy/delorean/jobs.dl +6 -0
  39. data/spec/features/delayed_jobs_grid_spec.rb +3 -2
  40. data/spec/features/schedule_jobs_dashboard_spec.rb +12 -12
  41. data/spec/jobs/cron_job_spec.rb +16 -12
  42. data/spec/jobs/delorean_background_job_spec.rb +50 -0
  43. data/spec/models/promise_spec.rb +1 -0
  44. data/spec/services/background_job/fetch_missing_in_schedule_cron_jobs_spec.rb +5 -3
  45. data/spec/services/jobs/schedule_spec.rb +5 -4
  46. metadata +12 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e457dd04b51af2cefe76859a59f80b9f7a62b6184825597dab79777147666330
4
- data.tar.gz: d891d5effa9cea8092d34c595503dbef827afe86098d25bc109ff58824deb89a
3
+ metadata.gz: f9a92073fa74d9777548dbafce731dbb8100918a4b3e696cdf00b06b1b20bbd8
4
+ data.tar.gz: b2b048bd619a886a1ebafbfaba5e73f017c44fdaaa1f55190ade7d7abb32cea7
5
5
  SHA512:
6
- metadata.gz: fe9bd7f32d4fef940f91ee36c609054fc28d1cf55936c0fccdfad50f212ad8da8dbb771faefb31f94d84cecb633f24694cf2c7a52850180e03e264c7156713fe
7
- data.tar.gz: 4ce7a00444928c18ddcdf56eeb6cb6c2a40fc5d0952e545fd3ddb7367fb64bcb838276cdc0bc5677a0daf49ac68b0ceed7a42447420c3d28699e9454afe40598
6
+ metadata.gz: 82e24b64bee5533356e3c4a0f6d22bb850d9fe24fd300e9d921f98beb7193b119ccc9a31ba11ee766bb0ade94d2fa8a961318b828805ea611dbfedc375c61503
7
+ data.tar.gz: 76d6757d910bc340d53a4930f846f698a9fa855c8827ec8fff1bd9a82c21346d55f703c41fc5d0f4f7f8871593282125ad628ced7610eee743518a15858dfc49
@@ -51,6 +51,8 @@ Security/MarshalLoad:
51
51
 
52
52
  Rails/TimeZone:
53
53
  EnforcedStyle: strict
54
+ Exclude:
55
+ - 'lib/marty/delayed_job/queue_adapter.rb'
54
56
 
55
57
  Rails/OutputSafety:
56
58
  Exclude:
data/README.md CHANGED
@@ -18,44 +18,45 @@ and delorean scripts.
18
18
  To create the correct migrations for a Marty-based application (see below for
19
19
  getting the internal dummy application to work):
20
20
 
21
- ```
22
- $ rake marty:install:migrations
21
+ ```bash
22
+ rake marty:install:migrations
23
23
  ```
24
24
 
25
25
  The Marty database needs to be seeded with specific configuration
26
26
  information. Before running your Marty application for the first time you will
27
27
  need to run:
28
28
 
29
- ```
30
- $ rake marty:seed
29
+ ```bash
30
+ rake marty:seed
31
31
  ```
32
32
 
33
33
  If you are using Delorean scripts in your application you can load them
34
34
  with a rake task. By default these scripts will be picked up from
35
35
  `app/delorean_scripts`. To load scripts:
36
36
 
37
- ```
38
- $ rake marty:load_scripts
37
+ ```bash
38
+ rake marty:load_scripts
39
39
  ```
40
40
 
41
41
  You can override the default directory by setting the `LOAD_DIR` environment
42
42
  variable:
43
43
 
44
- ```
45
- $ LOAD_DIR=<delorean script directory> marty:load_scripts
44
+ ```bash
45
+ LOAD_DIR=<delorean script directory> marty:load_scripts
46
46
  ```
47
47
 
48
48
  To delete scripts:
49
49
 
50
+ ``` bash
51
+ rake marty:delete_scripts
50
52
  ```
51
- $ rake marty:delete_scripts
52
- ```
53
+
53
54
  # Scheduled Job
54
55
 
55
- To use scheduled backgroud jobs, add to `config/application.rb`:
56
+ To use scheduled background jobs, add to `config/application.rb`:
56
57
 
57
- ```
58
- config.active_job.queue_adapter = :delayed_job
58
+ ```bash
59
+ config.active_job.queue_adapter = :delayed_job
59
60
  ```
60
61
 
61
62
  # Dummy Application & Testing
@@ -66,45 +67,53 @@ dummy app at spec/dummy/public.
66
67
  Docker doesn't support symlinks, so in order to run it in Docker you will have to copy extjs files.
67
68
 
68
69
  ```bash
69
- $ cp -r PATH/TO/YOUR/EXTJS/FILES spec/dummy/public/extjs
70
+ cp -r PATH/TO/YOUR/EXTJS/FILES spec/dummy/public/extjs
70
71
  ```
71
72
 
72
- You can run it with docker:
73
+ You can run it with Docker:
73
74
 
74
75
  ```bash
75
- $ make app-initialise-docker
76
-
77
- $ make app-start
76
+ make app-initialise-docker
77
+ make app-start
78
78
  ```
79
79
 
80
80
  To run tests:
81
81
 
82
82
  ```bash
83
- $ make app-bash
84
-
85
- $ HEADLESS=true rspec
83
+ make app-bash
84
+ HEADLESS=true rspec
86
85
  ```
87
86
 
88
- To run without docker:
87
+ To run without Docker:
89
88
 
90
- Marty currently only runs with postgresql. To be able to run the tests
89
+ Marty currently only runs with PostgreSQL. To be able to run the tests
91
90
  you will first need to create a `database.yml` file in `spec/dummy/config`.
92
91
  You can use the example file by doing:
93
92
 
94
93
  ```bash
95
- $ cp spec/dummy/config/database.yml.example spec/dummy/config/database.yml
94
+ cp spec/dummy/config/database.yml.example spec/dummy/config/database.yml
96
95
  ```
97
96
 
98
97
  To initialize the dummy application for a demo run:
99
98
 
100
99
  ```bash
101
- $ bundle install
102
- $ bundle exec rake db:create db:migrate db:seed app:marty:load_scripts
103
- $ cd spec/dummy
104
- $ rails s
100
+ bundle install
101
+ bundle exec rake db:create db:migrate db:seed app:marty:load_scripts
102
+ cd spec/dummy
103
+ bundle exec rails s
104
+ ```
105
+
106
+ Or, to run the application with the job workers running:
107
+
108
+ ```bash
109
+ bundle install
110
+ bundle exec rake db:create db:migrate db:seed app:marty:load_scripts
111
+ cd spec/dummy
112
+ gem install foreman
113
+ foreman start
105
114
  ```
106
115
 
107
- The marty dummy app should now be accessible in your browser:
116
+ The Marty dummy app should now be accessible in your browser:
108
117
  `localhost:3000`
109
118
 
110
119
  You can log in using `marty` as both user and password.
@@ -112,13 +121,13 @@ You can log in using `marty` as both user and password.
112
121
  To create the test database in prepartion to run your tests:
113
122
 
114
123
  ```bash
115
- $ RAILS_ENV=test bundle exec rake db:create
124
+ RAILS_ENV=test bundle exec rake db:create
116
125
  ```
117
126
 
118
127
  Then to run the tests:
119
128
 
120
129
  ```bash
121
- $ bundle exec rspec
130
+ bundle exec rspec
122
131
  ```
123
132
 
124
133
  # History & Status
@@ -20,8 +20,8 @@ module Marty
20
20
  handler_str = dj.handler[/job_class.*\n/]
21
21
  job_class = handler_str.gsub('job_class:', '').strip
22
22
 
23
- "#{job_class} with cron #{dj.cron} is present " \
24
- 'in delayed_jobs table, but is missing in the Dashboard.'
23
+ "#{job_class} with cron #{dj.cron} and schedule_id #{dj.schedule_id}" \
24
+ 'is present in delayed_jobs table, but is missing in the Dashboard.'
25
25
  end
26
26
 
27
27
  messages.join('<br>')
@@ -29,6 +29,7 @@ module Marty
29
29
 
30
30
  c.attributes = [
31
31
  :job_class,
32
+ :arguments,
32
33
  :cron,
33
34
  :state
34
35
  ]
@@ -46,6 +47,19 @@ module Marty
46
47
  c.width = 400
47
48
  end
48
49
 
50
+ attribute :arguments do |c|
51
+ c.width = 400
52
+
53
+ c.getter = lambda do |record|
54
+ record.arguments.to_json
55
+ end
56
+
57
+ c.setter = lambda do |record, value|
58
+ # FIXME: hacky way to parse JSON with single quotes
59
+ record.arguments = JSON.parse(value.tr("'", '"'))
60
+ end
61
+ end
62
+
49
63
  attribute :cron do |c|
50
64
  c.width = 400
51
65
  end
@@ -76,6 +90,8 @@ module Marty
76
90
  end
77
91
 
78
92
  endpoint :edit_window__edit_form__submit do |params|
93
+ id = JSON.parse(params['data'])['id']
94
+
79
95
  result = super(params)
80
96
  next result if result.empty?
81
97
 
@@ -83,7 +99,7 @@ module Marty
83
99
 
84
100
  Marty::BackgroundJob::UpdateSchedule.call(
85
101
  id: obj_hash['id'],
86
- job_class: obj_hash['job_class']
102
+ job_class: obj_hash['job_class'],
87
103
  )
88
104
 
89
105
  result
@@ -97,7 +113,7 @@ module Marty
97
113
 
98
114
  Marty::BackgroundJob::UpdateSchedule.call(
99
115
  id: obj_hash['id'],
100
- job_class: obj_hash['job_class']
116
+ job_class: obj_hash['job_class'],
101
117
  )
102
118
 
103
119
  result
@@ -109,7 +125,8 @@ module Marty
109
125
 
110
126
  endpoint :destroy do |params|
111
127
  res = params.each_with_object({}) do |id, hash|
112
- job_class = model.find_by(id: id)&.job_class
128
+ record = model.find_by(id: id)
129
+ job_class = record&.job_class
113
130
  result = super([id])
114
131
 
115
132
  # Do nothing If it wasn't destroyed
@@ -117,7 +134,7 @@ module Marty
117
134
 
118
135
  Marty::BackgroundJob::UpdateSchedule.call(
119
136
  id: id,
120
- job_class: job_class
137
+ job_class: job_class,
121
138
  )
122
139
 
123
140
  hash.merge(result)
@@ -130,7 +147,7 @@ module Marty
130
147
  begin
131
148
  s = Marty::BackgroundJob::Schedule.find(client_config['selected'])
132
149
  klass = s.job_class
133
- klass.constantize.new.perform
150
+ klass.constantize.new.perform(*s.arguments)
134
151
  rescue StandardError => e
135
152
  next client.netzke_notify(e.message)
136
153
  end
@@ -26,6 +26,7 @@ module Marty
26
26
 
27
27
  c.attributes = [
28
28
  :job_class,
29
+ :arguments,
29
30
  :status,
30
31
  :error,
31
32
  :created_at
@@ -47,6 +48,17 @@ module Marty
47
48
  c.read_only = true
48
49
  end
49
50
 
51
+ attribute :arguments do |c|
52
+ c.getter = lambda do |record|
53
+ record.arguments.to_json
54
+ end
55
+
56
+ c.setter = lambda do |record, value|
57
+ # FIXME: hacky way to parse JSON with single quotes
58
+ record.arguments = JSON.parse(value.tr("'", '"'))
59
+ end
60
+ end
61
+
50
62
  attribute :status do |c|
51
63
  c.read_only = true
52
64
  end
@@ -410,10 +410,6 @@ class Marty::BaseRuleView < Marty::McflyGridPanel
410
410
  c.format = 'Y-m-d H:i'
411
411
  end
412
412
 
413
- attribute :rule_type do |c|
414
- c.width = 200
415
- end
416
-
417
413
  def self.init_fields
418
414
  klass.guard_info.each do |namestr, h|
419
415
  field_maker(namestr, h, :simple_guards)
@@ -6,7 +6,17 @@ class Marty::DeloreanRuleView < Marty::BaseRuleView
6
6
  end
7
7
 
8
8
  def self.base_fields
9
- super + [:rule_type, :start_dt, :end_dt]
9
+ fields = if klass&.column_names&.include?('rule_type')
10
+ [:rule_type, :start_dt, :end_dt]
11
+ else
12
+ [:start_dt, :end_dt]
13
+ end
14
+
15
+ super + fields
16
+ end
17
+
18
+ attribute :rule_type do |c|
19
+ c.width = 200
10
20
  end
11
21
 
12
22
  attribute :start_dt do |c|
@@ -18,8 +28,4 @@ class Marty::DeloreanRuleView < Marty::BaseRuleView
18
28
  c.width = 150
19
29
  c.format = 'Y-m-d H:i'
20
30
  end
21
-
22
- attribute :rule_type do |c|
23
- c.width = 200
24
- end
25
31
  end
@@ -29,9 +29,11 @@ class Marty::PromiseView < Marty::Tree
29
29
  :priority,
30
30
  :start_dt,
31
31
  :end_dt,
32
+ :total_time,
32
33
  :status,
33
34
  :cformat,
34
35
  :error,
36
+ :timeout
35
37
  ]
36
38
  c.root_visible = false
37
39
  c.paging = :none
@@ -122,6 +124,19 @@ class Marty::PromiseView < Marty::Tree
122
124
  config.text = I18n.t('jobs.end_dt')
123
125
  end
124
126
 
127
+ attribute :total_time do |config|
128
+ config.text = I18n.t('jobs.total_time')
129
+ config.width = 90
130
+
131
+ config.getter = ->(record) do
132
+ next unless record.start_dt
133
+ next unless record.end_dt
134
+
135
+ time_diff = record.end_dt - record.start_dt
136
+ Time.zone.at(time_diff.to_i.abs).utc.strftime '%H:%M:%S'
137
+ end
138
+ end
139
+
125
140
  attribute :status do |config|
126
141
  config.hidden = true
127
142
  end
@@ -1,15 +1,23 @@
1
1
  class Marty::CronJob < ActiveJob::Base
2
- around_perform do |_job, block|
2
+ attr_accessor :schedule_id
3
+
4
+ def enqueue(options = {})
5
+ self.cron = options[:cron] if options[:cron]
6
+ self.schedule_id = options[:schedule_id] if options[:schedule_id]
7
+ super
8
+ end
9
+
10
+ around_perform do |job, block|
3
11
  begin
4
12
  block.call
5
- log_success
13
+ log_success(job.arguments)
6
14
  rescue StandardError => e
7
- log_failure(e)
15
+ log_failure(e, job.arguments)
8
16
  raise e
9
17
  end
10
18
  end
11
19
 
12
- def log_failure(exception)
20
+ def log_failure(exception, arguments)
13
21
  error = {
14
22
  message: exception.message,
15
23
  backtrace: exception.backtrace
@@ -17,55 +25,49 @@ class Marty::CronJob < ActiveJob::Base
17
25
 
18
26
  ::Marty::BackgroundJob::Log.create!(
19
27
  job_class: self.class.name,
28
+ arguments: arguments,
20
29
  status: :failure,
21
30
  error: error
22
31
  )
23
32
  end
24
33
 
25
- def log_success
34
+ def log_success(arguments)
26
35
  ::Marty::BackgroundJob::Log.create!(
27
36
  job_class: self.class.name,
37
+ arguments: arguments,
28
38
  status: :success
29
39
  )
30
40
  end
31
41
 
32
42
  class << self
33
- def schedule
34
- return reschedule if scheduled?
43
+ def schedule(schedule_obj:)
44
+ dj = schedule_obj.delayed_job
45
+
46
+ return reschedule_obj(schedule_obj: schedule_obj) if dj.present?
35
47
 
36
- cron = cron_expression
48
+ cron = schedule_obj.cron
37
49
 
38
50
  return if cron.blank?
39
51
 
40
- set(cron: cron).perform_later
52
+ set(cron: cron, schedule_id: schedule_obj.id).perform_later(*schedule_obj.arguments)
41
53
  end
42
54
 
43
- def reschedule
44
- dj = delayed_job
45
- return dj.update(cron: cron_expression) if dj.locked_by?
55
+ def reschedule(schedule_obj:)
56
+ dj = schedule_obj.delayed_job
57
+ return dj.update(cron: schedule_obj.cron) if dj.locked_by?
46
58
 
47
- remove
48
- schedule
59
+ remove(dj)
60
+ schedule(schedule_obj: schedule_obj)
49
61
  end
50
62
 
51
- def remove
52
- delayed_job.destroy if scheduled?
63
+ def remove(dj)
64
+ dj.destroy if dj.present?
53
65
  end
54
66
 
55
67
  alias remove_schedule remove
56
68
 
57
- def scheduled?
58
- delayed_job.present?
59
- end
60
-
61
- def delayed_job
62
- Delayed::Job.
63
- where('handler LIKE ?', "%job_class: #{name}\n%").
64
- first
65
- end
66
-
67
- def cron_expression
68
- ::Marty::BackgroundJob::Schedule.on.find_by(job_class: name)&.cron
69
+ def scheduled?(schedule_id:)
70
+ Delayed::Job.find_by(schedule_id: schedule_id).present?
69
71
  end
70
72
  end
71
73
  end