marty 6.1.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.gitlab-ci.yml +17 -3
  4. data/.rubocop.yml +5 -0
  5. data/.rubocop_todo.yml +3 -2
  6. data/Gemfile +2 -1
  7. data/app/assets/javascripts/marty/extjs/extensions/marty.js +23 -1
  8. data/app/assets/stylesheets/marty/application.css +4 -1
  9. data/app/assets/stylesheets/marty/dark_mode.css +17 -0
  10. data/app/components/marty/auth_app.rb +10 -1
  11. data/app/components/marty/auth_app/client/auth_app.js +4 -0
  12. data/app/components/marty/extras/layout.rb +2 -2
  13. data/app/components/marty/extras/misc.rb +1 -1
  14. data/app/components/marty/log_view.rb +0 -1
  15. data/app/components/marty/main_auth_app.rb +9 -12
  16. data/app/components/marty/promise_view.rb +5 -0
  17. data/app/components/marty/promise_view/client/promise_view.js +11 -0
  18. data/app/components/marty/schedule_jobs_dashboard.rb +30 -96
  19. data/app/components/marty/schedule_jobs_grid.rb +118 -0
  20. data/app/controllers/marty/application_controller.rb +6 -2
  21. data/app/models/marty/base.rb +48 -48
  22. data/app/models/marty/config.rb +14 -2
  23. data/app/models/marty/user.rb +12 -0
  24. data/app/services/marty/background_job/fetch_missing_in_schedule_cron_jobs.rb +19 -0
  25. data/app/services/marty/enums/report.rb +18 -0
  26. data/app/services/marty/promises/delorean/create.rb +16 -27
  27. data/app/services/marty/promises/ruby/create.rb +10 -1
  28. data/app/views/layouts/marty/application.html.erb +4 -1
  29. data/app/views/marty/diagnostic/op.html.erb +3 -1
  30. data/config/locales/en.yml +2 -0
  31. data/db/migrate/512_add_promise_priority.rb +9 -0
  32. data/db/migrate/513_add_priority_to_promise_view.rb +44 -0
  33. data/db/migrate/514_remove_marty_events.rb +13 -0
  34. data/delorean/enum_report.dl +11 -0
  35. data/delorean/table_report.dl +7 -0
  36. data/docker-compose.dummy.yml +7 -4
  37. data/lib/marty.rb +5 -2
  38. data/lib/marty/api/base.rb +15 -9
  39. data/lib/marty/cache_adapters.rb +2 -0
  40. data/lib/marty/cache_adapters/mcfly_ruby_cache.rb +1 -5
  41. data/lib/marty/cache_adapters/memory_and_redis.rb +93 -0
  42. data/lib/marty/cache_adapters/redis.rb +63 -0
  43. data/lib/marty/delayed_job/scheduled_job_plugin.rb +33 -0
  44. data/lib/marty/diagnostic/database.rb +1 -2
  45. data/lib/marty/logger.rb +50 -17
  46. data/lib/marty/monkey.rb +26 -6
  47. data/lib/marty/promise_ruby_job.rb +2 -0
  48. data/lib/marty/rails_app.rb +29 -0
  49. data/lib/marty/railtie.rb +1 -0
  50. data/lib/marty/version.rb +1 -1
  51. data/marty.gemspec +1 -0
  52. data/spec/controllers/job_controller_spec.rb +2 -2
  53. data/spec/dummy/app/components/gemini/cm_auth_app.rb +12 -0
  54. data/spec/dummy/app/components/gemini/simple_view.rb +17 -0
  55. data/spec/dummy/app/jobs/test_failing_job.rb +14 -0
  56. data/spec/dummy/app/models/gemini/helper.rb +90 -1
  57. data/spec/dummy/config/application.rb +1 -0
  58. data/spec/dummy/db/migrate/20191101132729_add_activity_flag_to_simple.rb +10 -0
  59. data/spec/dummy/delorean/blame_report.dl +1 -0
  60. data/spec/dummy/delorean/enum_report.dl +1 -0
  61. data/spec/dummy/delorean/marty_fields.dl +1 -0
  62. data/spec/dummy/delorean/table_report.dl +1 -0
  63. data/spec/features/data_blame_report_spec.rb +66 -0
  64. data/spec/features/data_grid_spec.rb +1 -1
  65. data/spec/features/enum_values_report_spec.rb +76 -0
  66. data/spec/features/inline_editing_spec.rb +33 -0
  67. data/spec/features/rule_spec.rb +1 -1
  68. data/spec/features/schedule_jobs_dashboard_spec.rb +1 -1
  69. data/spec/features/scripting_spec.rb +1 -1
  70. data/spec/features/user_list_report_spec.rb +74 -0
  71. data/spec/fixtures/misc/struct_compare_tests.txt +15 -5
  72. data/spec/job_helper.rb +39 -0
  73. data/spec/jobs/cron_job_spec.rb +91 -0
  74. data/spec/lib/mcfly_model_spec.rb +9 -0
  75. data/spec/models/promise_spec.rb +168 -1
  76. data/spec/other/diagnostic/delayed_job_workers_spec.rb +1 -1
  77. data/spec/performance/caching_spec.rb +99 -0
  78. data/spec/services/background_job/fetch_missing_in_schedule_cron_jobs_spec.rb +34 -0
  79. data/spec/support/delayed_job_helpers.rb +3 -3
  80. data/spec/support/shared_connection.rb +9 -1
  81. data/spec/support/structure_compare.rb +19 -3
  82. metadata +39 -6
  83. data/app/components/marty/event_view.rb +0 -129
  84. data/app/models/marty/event.rb +0 -317
  85. data/spec/dummy/db/migrate/20160923183516_add_bulk_pricing_event_ops.rb +0 -8
  86. data/spec/dummy/delorean/blame_report.dl +0 -268
  87. data/spec/dummy/delorean/marty_fields.dl +0 -63
  88. data/spec/dummy/delorean/table_report.dl +0 -34
  89. data/spec/models/event_spec.rb +0 -272
data/lib/marty/monkey.rb CHANGED
@@ -70,14 +70,10 @@ class Delorean::BaseModule::NodeCall
70
70
  end
71
71
 
72
72
  class Delorean::Engine
73
- def background_eval(node, params, attrs, event = {})
73
+ # FIXME: Marty::Event no longer exists, we should remove event at some point
74
+ def background_eval(node, params, attrs, _event = {})
74
75
  raise 'background_eval bad params' unless params.is_a?(Hash)
75
76
 
76
- unless event.empty?
77
- params['p_event'] = event.each_with_object({}) do |(k, v), h|
78
- h[k.to_s] = v
79
- end
80
- end
81
77
  nc = Delorean::BaseModule::NodeCall.new({}, self, node, params)
82
78
  # start the background promise
83
79
  nc | attrs
@@ -112,6 +108,25 @@ class Netzke::Base
112
108
  end
113
109
  end
114
110
 
111
+ # FIXME: Move to Netzke
112
+ module Netzke
113
+ module Basepack
114
+ class ColumnConfig < AttrConfig
115
+ alias old_set_defaults set_defaults
116
+
117
+ def set_defaults
118
+ old_set_defaults
119
+
120
+ return unless xtype == :checkcolumn
121
+ # Use default value only if there is a boolean attribute with that name
122
+ return unless @model_adapter.attr_type(name) == :boolean
123
+ return if key?(:default_value)
124
+
125
+ self.default_value = false
126
+ end
127
+ end
128
+ end
129
+ end
115
130
  ######################################################################
116
131
 
117
132
  # The following is a hack to get around ActiveRecord's broken handling
@@ -356,3 +371,8 @@ module Netzke
356
371
  end
357
372
  end
358
373
  end
374
+
375
+ require 'delayed_cron_job'
376
+ require_relative './delayed_job/scheduled_job_plugin.rb'
377
+
378
+ Delayed::Worker.plugins << Marty::DelayedJob::ScheduledJobPlugin
@@ -23,6 +23,7 @@ class Marty::PromiseRubyJob < Struct.new(:promise,
23
23
  begin
24
24
  Mcfly.whodunnit = promise.user
25
25
 
26
+ ENV['__promise_id'] = promise.id.to_s
26
27
  mod = module_name.constantize
27
28
  res = { 'result' => mod.send(method_name, *method_args) }
28
29
  rescue StandardError => e
@@ -31,6 +32,7 @@ class Marty::PromiseRubyJob < Struct.new(:promise,
31
32
 
32
33
  promise.set_result(res)
33
34
  process_hook(res)
35
+ ENV.delete('__promise_id')
34
36
  end
35
37
 
36
38
  def process_hook(res)
@@ -0,0 +1,29 @@
1
+ module Marty
2
+ module RailsApp
3
+ class << self
4
+ def application_name
5
+ if Gem::Version.new(Rails.version) >= Gem::Version.new('6.0.0')
6
+ Rails.application.class.module_parent_name
7
+ else
8
+ Rails.application.class.parent_name
9
+ end
10
+ end
11
+
12
+ def needs_migration?
13
+ if Gem::Version.new(Rails.version) >= Gem::Version.new('5.2.0')
14
+ ActiveRecord::Base.connection.migration_context.needs_migration?
15
+ else
16
+ ActiveRecord::Migrator.needs_migration?
17
+ end
18
+ end
19
+
20
+ def parameter_filter_class
21
+ if Gem::Version.new(Rails.version) >= Gem::Version.new('6.0.0')
22
+ ActiveSupport::ParameterFilter
23
+ else
24
+ ActionDispatch::Http::ParameterFilter
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
data/lib/marty/railtie.rb CHANGED
@@ -4,5 +4,6 @@ module Marty
4
4
  config.marty.default_posting_type = 'BASE'
5
5
  config.marty.extjs_theme = 'classic'
6
6
  config.marty.promise_job_enqueue_hooks = []
7
+ config.marty.redis_url = nil
7
8
  end
8
9
  end
data/lib/marty/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Marty
4
- VERSION = '6.1.0'
4
+ VERSION = '8.0.0'
5
5
  end
data/marty.gemspec CHANGED
@@ -41,6 +41,7 @@ Gem::Specification.new do |s|
41
41
  s.add_dependency 'net-ldap'
42
42
  s.add_dependency 'netzke'
43
43
  s.add_dependency 'pg'
44
+ s.add_dependency 'redis'
44
45
  s.add_dependency 'rubyzip'
45
46
  s.add_dependency 'zip-zip'
46
47
  end
@@ -208,13 +208,13 @@ describe Marty::JobController, slow: true do
208
208
 
209
209
  expect_csv = "a,b\r\n1,1\r\n2,4\r\n3,9\r\n"
210
210
  expect(response.body).to eq expect_csv
211
- expect(response.content_type).to eq 'text/csv'
211
+ expect(response.media_type).to eq 'text/csv'
212
212
 
213
213
  get 'download', params: {
214
214
  job_id: promise.parent_id,
215
215
  }
216
216
 
217
- expect(response.content_type).to eq 'application/zip'
217
+ expect(response.media_type).to eq 'application/zip'
218
218
 
219
219
  Zip::InputStream.open(StringIO.new(response.body)) do |io|
220
220
  count = 0
@@ -13,6 +13,7 @@ class Gemini::CmAuthApp < Marty::MainAuthApp
13
13
  :loan_program_view,
14
14
  :my_rule_view,
15
15
  :xyz_rule_view,
16
+ :simple_view,
16
17
  ],
17
18
  }
18
19
  ]
@@ -33,13 +34,24 @@ class Gemini::CmAuthApp < Marty::MainAuthApp
33
34
  a.handler = :netzke_load_component_by_action
34
35
  end
35
36
 
37
+ action :simple_view do |a|
38
+ a.text = a.tooltip = 'Gemini Simple'
39
+ a.handler = :netzke_load_component_by_action
40
+ end
41
+
36
42
  component :loan_program_view do |c|
37
43
  c.klass = Gemini::LoanProgramView
38
44
  end
45
+
39
46
  component :my_rule_view do |c|
40
47
  c.klass = Gemini::MyRuleView
41
48
  end
49
+
42
50
  component :xyz_rule_view do |c|
43
51
  c.klass = Gemini::XyzRuleView
44
52
  end
53
+
54
+ component :simple_view do |c|
55
+ c.klass = Gemini::SimpleView
56
+ end
45
57
  end
@@ -0,0 +1,17 @@
1
+ class Gemini::SimpleView < Marty::McflyGridPanel
2
+ has_marty_permissions create: :dev,
3
+ read: :dev,
4
+ update: :dev,
5
+ delete: :dev
6
+
7
+ def configure(c)
8
+ super
9
+ c.title = 'Gemini Simple'
10
+ c.model = Gemini::Simple
11
+ c.attributes = [
12
+ :user_id,
13
+ :some_name,
14
+ :active,
15
+ ]
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ class TestFailingJob < Marty::CronJob
2
+ def perform
3
+ self.class.trigger_destroy
4
+ self.class.trigger_failure
5
+ end
6
+
7
+ def self.trigger_destroy
8
+ delayed_job.destroy!
9
+ end
10
+
11
+ def self.trigger_failure
12
+ raise 'failure triggered'
13
+ end
14
+ end
@@ -5,8 +5,33 @@ class Gemini::Helper
5
5
 
6
6
  # Just for testing
7
7
  delorean_fn :sleep, sig: 1 do
8
- |seconds|
8
+ |seconds, label = ''|
9
+
10
+ Marty::Logger.info('sleeping', {label: label}) if label.present?
11
+
9
12
  Kernel.sleep seconds
13
+ seconds.to_s
14
+ end
15
+
16
+ def self.pr_wait(ids)
17
+ idh = ids.each_with_object({}) do |id, h|
18
+ h[id] = false
19
+ end
20
+ timeout = 60
21
+ all_done = false
22
+ loop do
23
+ idh.each do |id, v|
24
+ next if v
25
+ p = Marty::Promise.uncached { Marty::Promise.find_by(id: id) }
26
+ idh[id] = p.result if p.status
27
+ end
28
+ all_done = idh.values.all? { |v| v }
29
+ break if all_done || timeout == 0
30
+ timeout -= 1
31
+ sleep 1
32
+ end
33
+ raise "DID NOT FINISH" unless all_done
34
+ idh.values.map { |h| h['result'] }
10
35
  end
11
36
 
12
37
  # Just for testing
@@ -57,4 +82,68 @@ class Gemini::Helper
57
82
  end
58
83
  true
59
84
  end
85
+
86
+ delorean_fn :get_inds do |cnt|
87
+ (0..cnt-1).to_a
88
+ end
89
+
90
+ delorean_fn :cached_factorial, cache: true do |number|
91
+ Kernel.sleep 0.001
92
+ Math.gamma(number + 1)
93
+ end
94
+
95
+ delorean_fn :priority_tester do |reverse, job_cnt|
96
+ blockers = 8.times.map do |idx|
97
+ title = "Blocker #{idx}"
98
+ Marty::Promises::Ruby::Create.call(
99
+ module_name: 'Gemini::Helper',
100
+ method_name: 'sleep',
101
+ method_args: [5, title],
102
+ params: {
103
+ p_title: title,
104
+ _user_id: 1,
105
+ p_priority: 0,
106
+ }.compact
107
+ ).as_json.values.first.first
108
+ end
109
+ prioritized = job_cnt.times.map do |idx|
110
+ pri = reverse ? job_cnt - idx : idx
111
+ title = "Prioritized #{idx} pri=#{pri}"
112
+ Marty::Promises::Ruby::Create.call(
113
+ module_name: 'Gemini::Helper',
114
+ method_name: 'sleep',
115
+ method_args: [2, title],
116
+ params: {
117
+ p_title: title,
118
+ _user_id: 1,
119
+ p_priority: pri
120
+ }.compact
121
+ ).as_json.values.first.first
122
+ end
123
+ pr_wait(blockers) + pr_wait(prioritized)
124
+ end
125
+
126
+ delorean_fn :priority_inh_tester do |title|
127
+ [Marty::Promises::Ruby::Create.call(
128
+ module_name: 'Gemini::Helper',
129
+ method_name: 'sleep',
130
+ method_args: [0, 'child1'],
131
+ params: {
132
+ p_title: title + ' child1',
133
+ _user_id: 1,
134
+ _parent_id: ENV['__promise_id']&.to_i
135
+ }.compact
136
+ ).as_json.values.first.first,
137
+ Marty::Promises::Ruby::Create.call(
138
+ module_name: 'Gemini::Helper',
139
+ method_name: 'sleep',
140
+ method_args: [0, 'child2'],
141
+ params: {
142
+ p_title: title + ' child2',
143
+ _user_id: 1,
144
+ p_priority: 10,
145
+ _parent_id: ENV['__promise_id']&.to_i
146
+ }.compact
147
+ ).as_json.values.first.first]
148
+ end
60
149
  end
@@ -83,6 +83,7 @@ module Dummy
83
83
  config.marty.extjs_theme = 'crisp'
84
84
 
85
85
  config.active_job.queue_adapter = :delayed_job
86
+ config.marty.redis_url = ENV['MARTY_REDIS_URL']
86
87
  end
87
88
  end
88
89
  require "marty/permissions"
@@ -0,0 +1,10 @@
1
+ class AddActivityFlagToSimple < ActiveRecord::Migration[5.2]
2
+ def up
3
+ add_column :gemini_simples, :active, :boolean, null: false, default: false
4
+ change_column_default :gemini_simples, :active, nil
5
+ end
6
+
7
+ def down
8
+ remove_column :gemini_simples, :active
9
+ end
10
+ end
@@ -0,0 +1 @@
1
+ ../../../delorean/blame_report.dl
@@ -0,0 +1 @@
1
+ ../../../delorean/enum_report.dl
@@ -0,0 +1 @@
1
+ ../../../delorean/marty_fields.dl
@@ -0,0 +1 @@
1
+ ../../../delorean/table_report.dl
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Data Blame report', js: true do
4
+ before(:each) do
5
+ populate_test_users
6
+ Marty::Script.load_scripts(nil)
7
+ end
8
+
9
+ def go_to_reporting
10
+ press('Applications')
11
+ press('Reports')
12
+ end
13
+
14
+ def with_user(uname)
15
+ u = Marty::User.find_by_login(uname)
16
+ begin
17
+ old_u, Mcfly.whodunnit = Mcfly.whodunnit, u
18
+ yield(u)
19
+ ensure
20
+ Mcfly.whodunnit = old_u
21
+ end
22
+ end
23
+
24
+ def select_node node_name
25
+ wait_for_ajax
26
+ # hacky: assumes only 1 combobox without label
27
+ within(:gridpanel, 'report_select', match: :first) do
28
+ # hacky, hardcoding netzkecombobox dropdown arrow name
29
+ arrow = find(:input, 'nodename')['data-componentid'] + '-trigger-picker'
30
+ find(:xpath, ".//div[@id='#{arrow}']").click
31
+ find(:xpath, "//li[text()='#{node_name}']").click
32
+ end
33
+ end
34
+
35
+ it 'run_script displays proper data' do
36
+ log_in_as('admin1')
37
+ go_to_reporting
38
+
39
+ tag_grid = netzke_find('tag_grid')
40
+ script_grid = netzke_find('script_grid')
41
+ posting_combo = netzke_find('Posting', 'combobox')
42
+ script_combo = netzke_find('', 'combobox')
43
+
44
+ by 'select tag' do
45
+ wait_for_ajax
46
+ tag_grid.select_row(1)
47
+ end
48
+
49
+ and_by 'select TableReport script & UserList node' do
50
+ find('table', text: 'BlameReport').click
51
+ select_node('Data Blame Report (xlsx)')
52
+ end
53
+
54
+ and_by 'generate report' do
55
+ wait_for_ajax
56
+ press('Generate Report')
57
+ wait_for_ajax
58
+ expect(page).to have_content('Generate: Data Blame Report')
59
+ end
60
+
61
+ path = Rails.root.join('spec/tmp/downloads/Data Blame Report.xlsx')
62
+ file_exists = File.file?(path)
63
+
64
+ expect(file_exists).to be true
65
+ end
66
+ end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- feature 'data grid view', js: true do
3
+ feature 'data grid view', js: true, speed: :super_slow do
4
4
  before(:all) do
5
5
  self.use_transactional_tests = true
6
6
  end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'Enum Values List report', js: true do
4
+ before(:each) do
5
+ populate_test_users
6
+ Marty::Script.load_scripts(nil)
7
+ end
8
+
9
+ def go_to_reporting
10
+ press('Applications')
11
+ press('Reports')
12
+ end
13
+
14
+ def with_user(uname)
15
+ u = Marty::User.find_by_login(uname)
16
+ begin
17
+ old_u, Mcfly.whodunnit = Mcfly.whodunnit, u
18
+ yield(u)
19
+ ensure
20
+ Mcfly.whodunnit = old_u
21
+ end
22
+ end
23
+
24
+ def select_node node_name
25
+ wait_for_ajax
26
+ # hacky: assumes only 1 combobox without label
27
+ within(:gridpanel, 'report_select', match: :first) do
28
+ # hacky, hardcoding netzkecombobox dropdown arrow name
29
+ arrow = find(:input, 'nodename')['data-componentid'] + '-trigger-picker'
30
+ find(:xpath, ".//div[@id='#{arrow}']").click
31
+ find(:xpath, "//li[text()='#{node_name}']").click
32
+ end
33
+ end
34
+
35
+ it 'run_script displays proper data' do
36
+ log_in_as('admin1')
37
+ go_to_reporting
38
+
39
+ tag_grid = netzke_find('tag_grid')
40
+ script_grid = netzke_find('script_grid')
41
+ posting_combo = netzke_find('Posting', 'combobox')
42
+ script_combo = netzke_find('', 'combobox')
43
+
44
+ by 'select tag' do
45
+ wait_for_ajax
46
+ tag_grid.select_row(1)
47
+ end
48
+
49
+ and_by 'select TableReport script & UserList node' do
50
+ find('table', text: 'EnumReport').click
51
+ select_node('Enum Values List (csv)')
52
+ end
53
+
54
+ and_by 'generate report' do
55
+ wait_for_ajax
56
+ press('Generate Report')
57
+ wait_for_ajax
58
+ expect(page).to have_content('Generate: Enum Values List')
59
+ end
60
+
61
+ path = Rails.root.join('spec/tmp/downloads/Enum Values List.csv')
62
+ csv_content = CSV.parse File.read(path)
63
+
64
+ expect(csv_content.size > 1).to be true
65
+ expect(csv_content.first).to eq ['enum_name', 'value']
66
+
67
+ expected_rows = [
68
+ ['marty_promise_types', 'delorean'],
69
+ ['marty_promise_types', 'ruby']
70
+ ]
71
+
72
+ promise_types = csv_content.select { |v| v[0] == 'marty_promise_types' }
73
+
74
+ expect(promise_types).to eq expected_rows
75
+ end
76
+ end