marty 8.3.1 → 8.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/components/marty/background_job/delayed_jobs_grid.rb +71 -0
- data/app/components/marty/background_job/schedule_jobs_dashboard.rb +53 -0
- data/app/components/marty/background_job/schedule_jobs_grid.rb +136 -0
- data/app/components/marty/{schedule_jobs_grid → background_job/schedule_jobs_grid}/client/schedule_jobs_grid.js +0 -0
- data/app/components/marty/background_job/schedule_jobs_logs.rb +87 -0
- data/app/components/marty/{schedule_jobs_logs → background_job/schedule_jobs_logs}/client/schedule_jobs_logs.js +0 -0
- data/app/components/marty/main_auth_app.rb +25 -7
- data/lib/marty/data_conversion.rb +1 -1
- data/lib/marty/version.rb +1 -1
- data/spec/features/delayed_jobs_grid_spec.rb +80 -0
- data/spec/lib/data_conversion_spec.rb +54 -0
- metadata +10 -7
- data/app/components/marty/schedule_jobs_dashboard.rb +0 -52
- data/app/components/marty/schedule_jobs_grid.rb +0 -118
- data/app/components/marty/schedule_jobs_logs.rb +0 -85
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d2b7a71540fdd0a796cc8bd62725ddfa80231af28875eff79b5cbf1e6b8d65a
|
4
|
+
data.tar.gz: de508c108b52dc8b89d9d873030173594952a5c48ee507c68438f2e01a21d136
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: efbcc2b834193e2525809e494d6e12936d75606b03f7c2fb9970008eb3adac30bb4a193c9bd64323691db0fdd983401aba0e73907df16f839bfb4675551094af
|
7
|
+
data.tar.gz: 1c2834966a5b98f4177a14a297eed967b88a94b7f56c6c3597445e6dcd25ab96cad08465bcbf31f4675a76cc2d977f0a198efae2b15f47e64f771159ae2dfdf6
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Marty
|
2
|
+
module BackgroundJob
|
3
|
+
class DelayedJobsGrid < Marty::Grid
|
4
|
+
ACCESSIBLE_BY = [:admin]
|
5
|
+
|
6
|
+
has_marty_permissions(
|
7
|
+
read: ACCESSIBLE_BY,
|
8
|
+
create: nil,
|
9
|
+
update: nil,
|
10
|
+
delete: nil,
|
11
|
+
destroy: ACCESSIBLE_BY,
|
12
|
+
job_run: ACCESSIBLE_BY,
|
13
|
+
edit_window__edit_form__submit: ACCESSIBLE_BY,
|
14
|
+
add_window__add_form__submit: ACCESSIBLE_BY
|
15
|
+
)
|
16
|
+
|
17
|
+
def configure(c)
|
18
|
+
super
|
19
|
+
|
20
|
+
c.title ||= I18n.t(
|
21
|
+
'schedule_jobs_dashboard_view_title',
|
22
|
+
default: 'Delayed Jobs Dashboard'
|
23
|
+
)
|
24
|
+
|
25
|
+
c.model = 'Delayed::Job'
|
26
|
+
c.paging = :buffered
|
27
|
+
c.editing = :in_form
|
28
|
+
c.multi_select = false
|
29
|
+
|
30
|
+
c.attributes = [
|
31
|
+
:id,
|
32
|
+
:handler,
|
33
|
+
:run_at,
|
34
|
+
:locked_at,
|
35
|
+
:locked_by,
|
36
|
+
:created_at,
|
37
|
+
:cron,
|
38
|
+
:last_error
|
39
|
+
]
|
40
|
+
|
41
|
+
c.store_config.merge!(
|
42
|
+
sorters: [
|
43
|
+
{ property: :locked_at, direction: 'DESC' },
|
44
|
+
{ property: :run_at, direction: 'DESC' }
|
45
|
+
])
|
46
|
+
|
47
|
+
# c.scope = lambda do |r|
|
48
|
+
# r.order('locked_at DESC NULLS LAST')
|
49
|
+
# end
|
50
|
+
end
|
51
|
+
|
52
|
+
attribute :locked_at do |c|
|
53
|
+
c.sorting_scope = lambda do |relation, dir|
|
54
|
+
relation.order("locked_at #{dir} NULLS LAST")
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def default_context_menu
|
59
|
+
[]
|
60
|
+
end
|
61
|
+
|
62
|
+
def default_bbar
|
63
|
+
[]
|
64
|
+
end
|
65
|
+
|
66
|
+
attribute :cron do |c|
|
67
|
+
c.width = 400
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'marty/background_job/schedule_jobs_grid'
|
2
|
+
|
3
|
+
module Marty
|
4
|
+
module BackgroundJob
|
5
|
+
class ScheduleJobsDashboard < Marty::Form
|
6
|
+
include Marty::Extras::Layout
|
7
|
+
|
8
|
+
def configure(c)
|
9
|
+
super
|
10
|
+
c.items = [
|
11
|
+
:schedule_jobs_grid,
|
12
|
+
:schedule_jobs_warnings
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
def prepare_warnings
|
17
|
+
djs = ::Marty::BackgroundJob::FetchMissingInScheduleCronJobs.call
|
18
|
+
|
19
|
+
messages = djs.map do |dj|
|
20
|
+
handler_str = dj.handler[/job_class.*\n/]
|
21
|
+
job_class = handler_str.gsub('job_class:', '').strip
|
22
|
+
|
23
|
+
"#{job_class} with cron #{dj.cron} is present " \
|
24
|
+
'in delayed_jobs table, but is missing in the Dashboard.'
|
25
|
+
end
|
26
|
+
|
27
|
+
messages.join('<br>')
|
28
|
+
end
|
29
|
+
|
30
|
+
client_class do |c|
|
31
|
+
c.header = false
|
32
|
+
c.defaults = { body_style: 'padding:0px' }
|
33
|
+
end
|
34
|
+
|
35
|
+
component :schedule_jobs_grid do |c|
|
36
|
+
c.klass = Marty::BackgroundJob::ScheduleJobsGrid
|
37
|
+
c.region = :north
|
38
|
+
c.min_height = 500
|
39
|
+
end
|
40
|
+
|
41
|
+
component :schedule_jobs_warnings do |c|
|
42
|
+
c.klass = Marty::Panel
|
43
|
+
c.title = I18n.t('jobs.schedule_dashboard.warnings')
|
44
|
+
c.html = prepare_warnings
|
45
|
+
c.min_height = 200
|
46
|
+
end
|
47
|
+
|
48
|
+
def default_bbar
|
49
|
+
[]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Marty
|
2
|
+
module BackgroundJob
|
3
|
+
class ScheduleJobsGrid < Marty::Grid
|
4
|
+
ACCESSIBLE_BY = [:admin]
|
5
|
+
|
6
|
+
has_marty_permissions(
|
7
|
+
read: ACCESSIBLE_BY,
|
8
|
+
create: ACCESSIBLE_BY,
|
9
|
+
update: ACCESSIBLE_BY,
|
10
|
+
delete: ACCESSIBLE_BY,
|
11
|
+
destroy: ACCESSIBLE_BY,
|
12
|
+
job_run: ACCESSIBLE_BY,
|
13
|
+
edit_window__edit_form__submit: ACCESSIBLE_BY,
|
14
|
+
add_window__add_form__submit: ACCESSIBLE_BY
|
15
|
+
)
|
16
|
+
|
17
|
+
def configure(c)
|
18
|
+
super
|
19
|
+
|
20
|
+
c.title ||= I18n.t(
|
21
|
+
'schedule_jobs_dashboard_view_title',
|
22
|
+
default: 'Schedule Jobs Dashboard'
|
23
|
+
)
|
24
|
+
|
25
|
+
c.model = 'Marty::BackgroundJob::Schedule'
|
26
|
+
c.paging = :buffered
|
27
|
+
c.editing = :in_form
|
28
|
+
c.multi_select = false
|
29
|
+
|
30
|
+
c.attributes = [
|
31
|
+
:job_class,
|
32
|
+
:cron,
|
33
|
+
:state
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
def default_context_menu
|
38
|
+
[]
|
39
|
+
end
|
40
|
+
|
41
|
+
def default_bbar
|
42
|
+
super + [:do_job_run]
|
43
|
+
end
|
44
|
+
|
45
|
+
attribute :job_class do |c|
|
46
|
+
c.width = 400
|
47
|
+
end
|
48
|
+
|
49
|
+
attribute :cron do |c|
|
50
|
+
c.width = 400
|
51
|
+
end
|
52
|
+
|
53
|
+
attribute :state do |c|
|
54
|
+
c.width = 150
|
55
|
+
editor_config = {
|
56
|
+
trigger_action: :all,
|
57
|
+
xtype: :combo,
|
58
|
+
store: Marty::BackgroundJob::Schedule::ALL_STATES,
|
59
|
+
forceSelection: true,
|
60
|
+
}
|
61
|
+
|
62
|
+
c.column_config = { editor: editor_config }
|
63
|
+
c.field_config = editor_config
|
64
|
+
end
|
65
|
+
|
66
|
+
action :do_job_run do |a|
|
67
|
+
a.text = 'Run'
|
68
|
+
a.tooltip = 'Run'
|
69
|
+
a.icon_cls = 'fa fa-play glyph'
|
70
|
+
a.disabled = true
|
71
|
+
end
|
72
|
+
|
73
|
+
endpoint :edit_window__edit_form__submit do |params|
|
74
|
+
result = super(params)
|
75
|
+
next result if result.empty?
|
76
|
+
|
77
|
+
obj_hash = result.first
|
78
|
+
|
79
|
+
Marty::BackgroundJob::UpdateSchedule.call(
|
80
|
+
id: obj_hash['id'],
|
81
|
+
job_class: obj_hash['job_class']
|
82
|
+
)
|
83
|
+
|
84
|
+
result
|
85
|
+
end
|
86
|
+
|
87
|
+
endpoint :add_window__add_form__submit do |params|
|
88
|
+
result = super(params)
|
89
|
+
next result if result.empty?
|
90
|
+
|
91
|
+
obj_hash = result.first
|
92
|
+
|
93
|
+
Marty::BackgroundJob::UpdateSchedule.call(
|
94
|
+
id: obj_hash['id'],
|
95
|
+
job_class: obj_hash['job_class']
|
96
|
+
)
|
97
|
+
|
98
|
+
result
|
99
|
+
end
|
100
|
+
|
101
|
+
endpoint :multiedit_window__multiedit_form__submit do |_params|
|
102
|
+
client.netzke_notify 'Multiediting is disabled for cron schedules'
|
103
|
+
end
|
104
|
+
|
105
|
+
endpoint :destroy do |params|
|
106
|
+
res = params.each_with_object({}) do |id, hash|
|
107
|
+
job_class = model.find_by(id: id)&.job_class
|
108
|
+
result = super([id])
|
109
|
+
|
110
|
+
# Do nothing If it wasn't destroyed
|
111
|
+
next hash.merge(result) unless result[id.to_i] == 'ok'
|
112
|
+
|
113
|
+
Marty::BackgroundJob::UpdateSchedule.call(
|
114
|
+
id: id,
|
115
|
+
job_class: job_class
|
116
|
+
)
|
117
|
+
|
118
|
+
hash.merge(result)
|
119
|
+
end
|
120
|
+
|
121
|
+
res
|
122
|
+
end
|
123
|
+
|
124
|
+
endpoint :job_run do
|
125
|
+
begin
|
126
|
+
s = Marty::BackgroundJob::Schedule.find(client_config['selected'])
|
127
|
+
klass = s.job_class
|
128
|
+
klass.constantize.new.perform
|
129
|
+
rescue StandardError => e
|
130
|
+
next client.netzke_notify(e.message)
|
131
|
+
end
|
132
|
+
client.netzke_notify("#{klass.demodulize} ran successfully.")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
File without changes
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Marty
|
2
|
+
module BackgroundJob
|
3
|
+
class ScheduleJobsLogs < Marty::Grid
|
4
|
+
ACCESSIBLE_BY = [:admin]
|
5
|
+
|
6
|
+
has_marty_permissions(
|
7
|
+
read: ACCESSIBLE_BY,
|
8
|
+
create: ACCESSIBLE_BY,
|
9
|
+
update: ACCESSIBLE_BY,
|
10
|
+
delete: ACCESSIBLE_BY,
|
11
|
+
destroy: ACCESSIBLE_BY,
|
12
|
+
destroy_all: ACCESSIBLE_BY,
|
13
|
+
ignore: ACCESSIBLE_BY,
|
14
|
+
edit_window__edit_form__submit: ACCESSIBLE_BY,
|
15
|
+
add_window__add_form__submit: ACCESSIBLE_BY
|
16
|
+
)
|
17
|
+
|
18
|
+
def configure(c)
|
19
|
+
super
|
20
|
+
|
21
|
+
c.title ||= I18n.t('schedule_jobs_dashboard_view_title', default: "Scheduled Job's Logs")
|
22
|
+
c.model = 'Marty::BackgroundJob::Log'
|
23
|
+
c.paging = :buffered
|
24
|
+
c.editing = :in_form
|
25
|
+
c.multi_select = true
|
26
|
+
|
27
|
+
c.attributes = [
|
28
|
+
:job_class,
|
29
|
+
:status,
|
30
|
+
:error,
|
31
|
+
:created_at
|
32
|
+
]
|
33
|
+
|
34
|
+
c.store_config.merge!(sorters: [{ property: :id, direction: 'DESC' }])
|
35
|
+
end
|
36
|
+
|
37
|
+
def default_context_menu
|
38
|
+
[]
|
39
|
+
end
|
40
|
+
|
41
|
+
def default_bbar
|
42
|
+
[:delete, :destroy_all, :ignore]
|
43
|
+
end
|
44
|
+
|
45
|
+
attribute :job_class do |c|
|
46
|
+
c.width = 400
|
47
|
+
c.read_only = true
|
48
|
+
end
|
49
|
+
|
50
|
+
attribute :status do |c|
|
51
|
+
c.read_only = true
|
52
|
+
end
|
53
|
+
|
54
|
+
attribute :error do |c|
|
55
|
+
c.width = 800
|
56
|
+
c.read_only = true
|
57
|
+
c.getter = ->(record) { record.error.to_json }
|
58
|
+
end
|
59
|
+
|
60
|
+
action :destroy_all do |a|
|
61
|
+
a.text = 'Delete all'
|
62
|
+
a.tooltip = 'Delete all logs'
|
63
|
+
a.icon_cls = 'fa fa-trash glyph'
|
64
|
+
end
|
65
|
+
|
66
|
+
action :ignore do |a|
|
67
|
+
a.text = 'Ignore in diag'
|
68
|
+
a.tooltip = 'Ignore in diag'
|
69
|
+
a.icon_cls = 'fa fa-trash glyph'
|
70
|
+
end
|
71
|
+
|
72
|
+
endpoint :destroy_all do
|
73
|
+
Marty::BackgroundJob::Log.delete_all
|
74
|
+
client.reload
|
75
|
+
end
|
76
|
+
|
77
|
+
endpoint :ignore do |ids|
|
78
|
+
Marty::BackgroundJob::Log.
|
79
|
+
where(id: ids).
|
80
|
+
where(status: :failure).
|
81
|
+
each { |record| record.update(status: :failure_ignore) }
|
82
|
+
|
83
|
+
client.reload
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
File without changes
|
@@ -1,20 +1,21 @@
|
|
1
1
|
require 'marty/api_auth_view'
|
2
2
|
require 'marty/api_config_view'
|
3
3
|
require 'marty/api_log_view'
|
4
|
+
require 'marty/background_job/delayed_jobs_grid'
|
5
|
+
require 'marty/background_job/schedule_jobs_dashboard'
|
6
|
+
require 'marty/background_job/schedule_jobs_logs'
|
4
7
|
require 'marty/config_view'
|
5
8
|
require 'marty/data_grid_view'
|
6
|
-
require 'marty/schedule_jobs_dashboard'
|
7
|
-
require 'marty/schedule_jobs_logs'
|
8
9
|
require 'marty/data_grid_user_view'
|
9
10
|
require 'marty/import_type_view'
|
10
11
|
require 'marty/new_posting_window'
|
12
|
+
require 'marty/notifications/config_view'
|
13
|
+
require 'marty/notifications/deliveries_view'
|
11
14
|
require 'marty/posting_window'
|
12
15
|
require 'marty/promise_view'
|
13
16
|
require 'marty/reporting'
|
14
17
|
require 'marty/scripting'
|
15
18
|
require 'marty/user_view'
|
16
|
-
require 'marty/notifications/config_view'
|
17
|
-
require 'marty/notifications/deliveries_view'
|
18
19
|
|
19
20
|
class Marty::MainAuthApp < Marty::AuthApp
|
20
21
|
extend ::Marty::Permissions
|
@@ -116,6 +117,7 @@ class Marty::MainAuthApp < Marty::AuthApp
|
|
116
117
|
:bg_status,
|
117
118
|
:bg_stop,
|
118
119
|
:bg_restart,
|
120
|
+
:delayed_jobs_grid,
|
119
121
|
:schedule_jobs_dashboard,
|
120
122
|
:schedule_jobs_logs,
|
121
123
|
]
|
@@ -298,6 +300,14 @@ class Marty::MainAuthApp < Marty::AuthApp
|
|
298
300
|
a.disabled = !self.class.has_perm?(:admin)
|
299
301
|
end
|
300
302
|
|
303
|
+
action :delayed_jobs_grid do |a|
|
304
|
+
a.text = 'Delayed Jobs Dashboard'
|
305
|
+
a.tooltip = 'Show running delayed jobs'
|
306
|
+
a.icon_cls = 'fa fa-clock glyph'
|
307
|
+
a.disabled = !self.class.has_perm?(:admin)
|
308
|
+
a.handler = :netzke_load_component_by_action
|
309
|
+
end
|
310
|
+
|
301
311
|
action :schedule_jobs_dashboard do |a|
|
302
312
|
a.text = 'Schedule Jobs Dashboard'
|
303
313
|
a.tooltip = 'Edit Delayed Jobs Cron schedules'
|
@@ -438,9 +448,9 @@ class Marty::MainAuthApp < Marty::AuthApp
|
|
438
448
|
|
439
449
|
component :api_config_view
|
440
450
|
|
441
|
-
component :
|
442
|
-
|
443
|
-
|
451
|
+
component :delayed_jobs_grid do |c|
|
452
|
+
c.klass = ::Marty::BackgroundJob::DelayedJobsGrid
|
453
|
+
end
|
444
454
|
|
445
455
|
component :config_view
|
446
456
|
|
@@ -472,6 +482,14 @@ class Marty::MainAuthApp < Marty::AuthApp
|
|
472
482
|
|
473
483
|
component :reporting
|
474
484
|
|
485
|
+
component :schedule_jobs_dashboard do |c|
|
486
|
+
c.klass = ::Marty::BackgroundJob::ScheduleJobsDashboard
|
487
|
+
end
|
488
|
+
|
489
|
+
component :schedule_jobs_logs do |c|
|
490
|
+
c.klass = ::Marty::BackgroundJob::ScheduleJobsLogs
|
491
|
+
end
|
492
|
+
|
475
493
|
component :scripting do |c|
|
476
494
|
c.allow_edit = self.class.has_scripting_perm?
|
477
495
|
end
|
@@ -56,7 +56,7 @@ class Marty::DataConversion
|
|
56
56
|
# Dates are kept as float in Google spreadsheets. Need to
|
57
57
|
# convert them to dates.
|
58
58
|
begin
|
59
|
-
FLOAT_PAT.match?(v) ? EXCEL_START_DATE + v.to_f :
|
59
|
+
FLOAT_PAT.match?(v.to_s) ? EXCEL_START_DATE + v.to_f :
|
60
60
|
Mcfly.is_infinity(v) ? 'infinity' : v.to_date
|
61
61
|
rescue StandardError => e
|
62
62
|
raise "date conversion failed for #{v.inspect}}"
|
data/lib/marty/version.rb
CHANGED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
feature 'Delayed Jobs Dashboard', js: true do
|
4
|
+
before do
|
5
|
+
Delayed::Job.delete_all
|
6
|
+
populate_test_users
|
7
|
+
end
|
8
|
+
|
9
|
+
context 'as dev' do
|
10
|
+
before do
|
11
|
+
log_in_as('dev1')
|
12
|
+
wait_for_ajax
|
13
|
+
press('System')
|
14
|
+
press('Background Jobs')
|
15
|
+
press('Schedule Jobs Dashboard')
|
16
|
+
wait_for_ajax
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'access denied' do
|
20
|
+
expect(page).to_not have_content 'Schedule Jobs Dashboard'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'as admin' do
|
25
|
+
let(:jobs_view) { netzke_find('delayed_jobs_grid') }
|
26
|
+
|
27
|
+
let!(:schedule) do
|
28
|
+
['TestJob', 'Test2Job', 'TestFailingJob'].each do |klass_name|
|
29
|
+
Marty::BackgroundJob::Schedule.create(
|
30
|
+
job_class: klass_name,
|
31
|
+
cron: '0 0 * * *',
|
32
|
+
state: 'on'
|
33
|
+
).tap do |job|
|
34
|
+
Marty::BackgroundJob::UpdateSchedule.call(
|
35
|
+
id: job.id,
|
36
|
+
job_class: job.job_class
|
37
|
+
)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
let(:time1) { 10.hours.ago }
|
43
|
+
|
44
|
+
let(:time2) { 20.hours.ago }
|
45
|
+
|
46
|
+
before do
|
47
|
+
@djs = Delayed::Job.all
|
48
|
+
@djs.first.update!(locked_at: time1)
|
49
|
+
@djs.last.update!(locked_at: time2)
|
50
|
+
|
51
|
+
log_in_as('admin1')
|
52
|
+
wait_for_ajax
|
53
|
+
press('System')
|
54
|
+
press('Background Jobs')
|
55
|
+
press('Delayed Jobs Dashboard')
|
56
|
+
|
57
|
+
wait_for_ajax
|
58
|
+
|
59
|
+
expect(page).to have_content 'Delayed Jobs Dashboard'
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'shows delayed jobs' do
|
63
|
+
expect(jobs_view.row_count).to eq @djs.size
|
64
|
+
expect(jobs_view.row_count).to eq 3
|
65
|
+
locked_at = jobs_view.get_col_vals('locked_at', 3, 0, false).map do |str|
|
66
|
+
next unless str
|
67
|
+
|
68
|
+
Time.zone.parse(str).to_s
|
69
|
+
end
|
70
|
+
|
71
|
+
expect(locked_at).to eq(
|
72
|
+
[
|
73
|
+
time1.to_s,
|
74
|
+
time2.to_s,
|
75
|
+
nil
|
76
|
+
]
|
77
|
+
)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Marty
|
4
|
+
describe DataConversion do
|
5
|
+
describe '#convert' do
|
6
|
+
describe 'date' do
|
7
|
+
let(:date) { 1.day.ago.to_date }
|
8
|
+
|
9
|
+
it 'converts float strings to date' do
|
10
|
+
res = described_class.convert('40000.0', :date)
|
11
|
+
expect(res).to eq Date.new(2009, 7, 6)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'converts date to date' do
|
15
|
+
res = described_class.convert(date, :date)
|
16
|
+
expect(res).to eq date
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'converts infinity' do
|
20
|
+
['Infinity', 'infinity', ::Float::INFINITY].each do |value|
|
21
|
+
res = described_class.convert(value, :date)
|
22
|
+
expect(res).to eq 'infinity'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'raises error if the value is not valid' do
|
27
|
+
expect { described_class.convert(true, :date) }.
|
28
|
+
to raise_error(/date conversion failed for true/)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'boolean' do
|
33
|
+
it 'converts true' do
|
34
|
+
['true', '1', 'y', 't'].each do |value|
|
35
|
+
res = described_class.convert(value, :boolean)
|
36
|
+
expect(res).to eq true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'converts false' do
|
41
|
+
['false', '0', 'n', 'f'].each do |value|
|
42
|
+
res = described_class.convert(value, :boolean)
|
43
|
+
expect(res).to eq false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'raises error if the value is not valid' do
|
48
|
+
expect { described_class.convert(1.day.ago, :boolean) }.
|
49
|
+
to raise_error(/unknown boolean/)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: marty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 8.
|
4
|
+
version: 8.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arman Bostani
|
@@ -14,7 +14,7 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date: 2019-12-
|
17
|
+
date: 2019-12-05 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: actioncable
|
@@ -310,6 +310,12 @@ files:
|
|
310
310
|
- app/components/marty/api_log_view.rb
|
311
311
|
- app/components/marty/auth_app.rb
|
312
312
|
- app/components/marty/auth_app/client/auth_app.js
|
313
|
+
- app/components/marty/background_job/delayed_jobs_grid.rb
|
314
|
+
- app/components/marty/background_job/schedule_jobs_dashboard.rb
|
315
|
+
- app/components/marty/background_job/schedule_jobs_grid.rb
|
316
|
+
- app/components/marty/background_job/schedule_jobs_grid/client/schedule_jobs_grid.js
|
317
|
+
- app/components/marty/background_job/schedule_jobs_logs.rb
|
318
|
+
- app/components/marty/background_job/schedule_jobs_logs/client/schedule_jobs_logs.js
|
313
319
|
- app/components/marty/base_rule_view.rb
|
314
320
|
- app/components/marty/base_rule_view/client/base_rule_view.js
|
315
321
|
- app/components/marty/config_view.rb
|
@@ -356,11 +362,6 @@ files:
|
|
356
362
|
- app/components/marty/report_select/client/report_select.js
|
357
363
|
- app/components/marty/reporting.rb
|
358
364
|
- app/components/marty/reporting/client/reporting.js
|
359
|
-
- app/components/marty/schedule_jobs_dashboard.rb
|
360
|
-
- app/components/marty/schedule_jobs_grid.rb
|
361
|
-
- app/components/marty/schedule_jobs_grid/client/schedule_jobs_grid.js
|
362
|
-
- app/components/marty/schedule_jobs_logs.rb
|
363
|
-
- app/components/marty/schedule_jobs_logs/client/schedule_jobs_logs.js
|
364
365
|
- app/components/marty/script_form.rb
|
365
366
|
- app/components/marty/script_form/client/script_form.js
|
366
367
|
- app/components/marty/script_grid.rb
|
@@ -1695,6 +1696,7 @@ files:
|
|
1695
1696
|
- spec/features/data_blame_report_spec.rb
|
1696
1697
|
- spec/features/data_grid_spec.rb
|
1697
1698
|
- spec/features/data_import_spec.rb
|
1699
|
+
- spec/features/delayed_jobs_grid_spec.rb
|
1698
1700
|
- spec/features/endpoint_access.rb
|
1699
1701
|
- spec/features/enum_spec.rb
|
1700
1702
|
- spec/features/enum_values_report_spec.rb
|
@@ -1737,6 +1739,7 @@ files:
|
|
1737
1739
|
- spec/job_helper.rb
|
1738
1740
|
- spec/jobs/cron_job_spec.rb
|
1739
1741
|
- spec/lib/data_blame_spec.rb
|
1742
|
+
- spec/lib/data_conversion_spec.rb
|
1740
1743
|
- spec/lib/data_exporter_spec.rb
|
1741
1744
|
- spec/lib/data_importer_spec.rb
|
1742
1745
|
- spec/lib/delorean_query_spec.rb
|
@@ -1,52 +0,0 @@
|
|
1
|
-
require 'marty/schedule_jobs_grid'
|
2
|
-
|
3
|
-
class Marty::ScheduleJobsDashboard < Marty::Form
|
4
|
-
include Marty::Extras::Layout
|
5
|
-
|
6
|
-
def configure(c)
|
7
|
-
super
|
8
|
-
c.items = [
|
9
|
-
:schedule_jobs_grid,
|
10
|
-
:schedule_jobs_warnings
|
11
|
-
]
|
12
|
-
end
|
13
|
-
|
14
|
-
def prepare_warnings
|
15
|
-
djs = ::Marty::BackgroundJob::FetchMissingInScheduleCronJobs.call
|
16
|
-
|
17
|
-
messages = djs.map do |dj|
|
18
|
-
handler_str = dj.handler[/job_class.*\n/]
|
19
|
-
job_class = handler_str.gsub('job_class:', '').strip
|
20
|
-
|
21
|
-
"#{job_class} with cron #{dj.cron} is present in delayed_jobs table, " \
|
22
|
-
'but is missing in the Dashboard.'
|
23
|
-
end
|
24
|
-
|
25
|
-
messages.join('<br>')
|
26
|
-
end
|
27
|
-
|
28
|
-
client_class do |c|
|
29
|
-
c.header = false
|
30
|
-
# c.layout = :border
|
31
|
-
c.defaults = { body_style: 'padding:0px' }
|
32
|
-
end
|
33
|
-
|
34
|
-
component :schedule_jobs_grid do |c|
|
35
|
-
c.klass = Marty::ScheduleJobsGrid
|
36
|
-
c.region = :north
|
37
|
-
c.min_height = 500
|
38
|
-
end
|
39
|
-
|
40
|
-
component :schedule_jobs_warnings do |c|
|
41
|
-
c.klass = Marty::Panel
|
42
|
-
c.title = I18n.t('jobs.schedule_dashboard.warnings')
|
43
|
-
c.html = prepare_warnings
|
44
|
-
c.min_height = 200
|
45
|
-
end
|
46
|
-
|
47
|
-
def default_bbar
|
48
|
-
[]
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
ScheduleJobsDashboard = Marty::ScheduleJobsDashboard
|
@@ -1,118 +0,0 @@
|
|
1
|
-
class Marty::ScheduleJobsGrid < Marty::Grid
|
2
|
-
ACCESSIBLE_BY = [:admin]
|
3
|
-
|
4
|
-
has_marty_permissions(
|
5
|
-
read: ACCESSIBLE_BY,
|
6
|
-
create: ACCESSIBLE_BY,
|
7
|
-
update: ACCESSIBLE_BY,
|
8
|
-
delete: ACCESSIBLE_BY,
|
9
|
-
destroy: ACCESSIBLE_BY,
|
10
|
-
job_run: ACCESSIBLE_BY,
|
11
|
-
edit_window__edit_form__submit: ACCESSIBLE_BY,
|
12
|
-
add_window__add_form__submit: ACCESSIBLE_BY
|
13
|
-
)
|
14
|
-
|
15
|
-
def configure(c)
|
16
|
-
super
|
17
|
-
|
18
|
-
c.title ||= I18n.t('schedule_jobs_dashboard_view_title', default: 'Schedule Jobs Dashboard')
|
19
|
-
c.model = 'Marty::BackgroundJob::Schedule'
|
20
|
-
c.paging = :buffered
|
21
|
-
c.editing = :in_form
|
22
|
-
c.multi_select = false
|
23
|
-
|
24
|
-
c.attributes = [
|
25
|
-
:job_class,
|
26
|
-
:cron,
|
27
|
-
:state
|
28
|
-
]
|
29
|
-
end
|
30
|
-
|
31
|
-
def default_context_menu
|
32
|
-
[]
|
33
|
-
end
|
34
|
-
|
35
|
-
def default_bbar
|
36
|
-
super + [:do_job_run]
|
37
|
-
end
|
38
|
-
|
39
|
-
attribute :job_class do |c|
|
40
|
-
c.width = 400
|
41
|
-
end
|
42
|
-
|
43
|
-
attribute :cron do |c|
|
44
|
-
c.width = 400
|
45
|
-
end
|
46
|
-
|
47
|
-
attribute :state do |c|
|
48
|
-
c.width = 150
|
49
|
-
editor_config = {
|
50
|
-
trigger_action: :all,
|
51
|
-
xtype: :combo,
|
52
|
-
store: Marty::BackgroundJob::Schedule::ALL_STATES,
|
53
|
-
forceSelection: true,
|
54
|
-
}
|
55
|
-
|
56
|
-
c.column_config = { editor: editor_config }
|
57
|
-
c.field_config = editor_config
|
58
|
-
end
|
59
|
-
|
60
|
-
action :do_job_run do |a|
|
61
|
-
a.text = 'Run'
|
62
|
-
a.tooltip = 'Run'
|
63
|
-
a.icon_cls = 'fa fa-play glyph'
|
64
|
-
a.disabled = true
|
65
|
-
end
|
66
|
-
|
67
|
-
endpoint :edit_window__edit_form__submit do |params|
|
68
|
-
result = super(params)
|
69
|
-
next result if result.empty?
|
70
|
-
|
71
|
-
obj_hash = result.first
|
72
|
-
Marty::BackgroundJob::UpdateSchedule.call(id: obj_hash['id'], job_class: obj_hash['job_class'])
|
73
|
-
|
74
|
-
result
|
75
|
-
end
|
76
|
-
|
77
|
-
endpoint :add_window__add_form__submit do |params|
|
78
|
-
result = super(params)
|
79
|
-
next result if result.empty?
|
80
|
-
|
81
|
-
obj_hash = result.first
|
82
|
-
Marty::BackgroundJob::UpdateSchedule.call(id: obj_hash['id'], job_class: obj_hash['job_class'])
|
83
|
-
|
84
|
-
result
|
85
|
-
end
|
86
|
-
|
87
|
-
endpoint :multiedit_window__multiedit_form__submit do |_params|
|
88
|
-
client.netzke_notify 'Multiediting is disabled for cron schedules'
|
89
|
-
end
|
90
|
-
|
91
|
-
endpoint :destroy do |params|
|
92
|
-
res = params.each_with_object({}) do |id, hash|
|
93
|
-
job_class = model.find_by(id: id)&.job_class
|
94
|
-
result = super([id])
|
95
|
-
|
96
|
-
# Do nothing If it wasn't destroyed
|
97
|
-
next hash.merge(result) unless result[id.to_i] == 'ok'
|
98
|
-
|
99
|
-
Marty::BackgroundJob::UpdateSchedule.call(id: id, job_class: job_class)
|
100
|
-
hash.merge(result)
|
101
|
-
end
|
102
|
-
|
103
|
-
res
|
104
|
-
end
|
105
|
-
|
106
|
-
endpoint :job_run do
|
107
|
-
begin
|
108
|
-
s = Marty::BackgroundJob::Schedule.find(client_config['selected'])
|
109
|
-
klass = s.job_class
|
110
|
-
klass.constantize.new.perform
|
111
|
-
rescue StandardError => e
|
112
|
-
next client.netzke_notify(e.message)
|
113
|
-
end
|
114
|
-
client.netzke_notify("#{klass.demodulize} ran successfully.")
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
ScheduleJobsGrid = Marty::ScheduleJobsGrid
|
@@ -1,85 +0,0 @@
|
|
1
|
-
class Marty::ScheduleJobsLogs < Marty::Grid
|
2
|
-
ACCESSIBLE_BY = [:admin]
|
3
|
-
|
4
|
-
has_marty_permissions(
|
5
|
-
read: ACCESSIBLE_BY,
|
6
|
-
create: ACCESSIBLE_BY,
|
7
|
-
update: ACCESSIBLE_BY,
|
8
|
-
delete: ACCESSIBLE_BY,
|
9
|
-
destroy: ACCESSIBLE_BY,
|
10
|
-
destroy_all: ACCESSIBLE_BY,
|
11
|
-
ignore: ACCESSIBLE_BY,
|
12
|
-
edit_window__edit_form__submit: ACCESSIBLE_BY,
|
13
|
-
add_window__add_form__submit: ACCESSIBLE_BY
|
14
|
-
)
|
15
|
-
|
16
|
-
def configure(c)
|
17
|
-
super
|
18
|
-
|
19
|
-
c.title ||= I18n.t('schedule_jobs_dashboard_view_title', default: "Scheduled Job's Logs")
|
20
|
-
c.model = 'Marty::BackgroundJob::Log'
|
21
|
-
c.paging = :buffered
|
22
|
-
c.editing = :in_form
|
23
|
-
c.multi_select = true
|
24
|
-
|
25
|
-
c.attributes = [
|
26
|
-
:job_class,
|
27
|
-
:status,
|
28
|
-
:error,
|
29
|
-
:created_at
|
30
|
-
]
|
31
|
-
|
32
|
-
c.store_config.merge!(sorters: [{ property: :id, direction: 'DESC' }])
|
33
|
-
end
|
34
|
-
|
35
|
-
def default_context_menu
|
36
|
-
[]
|
37
|
-
end
|
38
|
-
|
39
|
-
def default_bbar
|
40
|
-
[:delete, :destroy_all, :ignore]
|
41
|
-
end
|
42
|
-
|
43
|
-
attribute :job_class do |c|
|
44
|
-
c.width = 400
|
45
|
-
c.read_only = true
|
46
|
-
end
|
47
|
-
|
48
|
-
attribute :status do |c|
|
49
|
-
c.read_only = true
|
50
|
-
end
|
51
|
-
|
52
|
-
attribute :error do |c|
|
53
|
-
c.width = 800
|
54
|
-
c.read_only = true
|
55
|
-
c.getter = ->(record) { record.error.to_json }
|
56
|
-
end
|
57
|
-
|
58
|
-
action :destroy_all do |a|
|
59
|
-
a.text = 'Delete all'
|
60
|
-
a.tooltip = 'Delete all logs'
|
61
|
-
a.icon_cls = 'fa fa-trash glyph'
|
62
|
-
end
|
63
|
-
|
64
|
-
action :ignore do |a|
|
65
|
-
a.text = 'Ignore in diag'
|
66
|
-
a.tooltip = 'Ignore in diag'
|
67
|
-
a.icon_cls = 'fa fa-trash glyph'
|
68
|
-
end
|
69
|
-
|
70
|
-
endpoint :destroy_all do
|
71
|
-
Marty::BackgroundJob::Log.delete_all
|
72
|
-
client.reload
|
73
|
-
end
|
74
|
-
|
75
|
-
endpoint :ignore do |ids|
|
76
|
-
Marty::BackgroundJob::Log.
|
77
|
-
where(id: ids).
|
78
|
-
where(status: :failure).
|
79
|
-
each { |record| record.update(status: :failure_ignore) }
|
80
|
-
|
81
|
-
client.reload
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
ScheduleJobsLogs = Marty::ScheduleJobsLogs
|