marty 2.9.3 → 3.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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +6 -5
  3. data/Gemfile.lock +1 -1
  4. data/app/components/marty/data_grid_view.rb +64 -1
  5. data/app/components/marty/data_grid_view/client/data_grid_edit.js +550 -0
  6. data/app/components/marty/import_type_view.rb +11 -2
  7. data/app/components/marty/main_auth_app.rb +23 -23
  8. data/app/components/marty/promise_view.rb +1 -1
  9. data/app/components/marty/report_select.rb +1 -0
  10. data/app/components/marty/script_form.rb +1 -1
  11. data/app/components/marty/user_view.rb +30 -18
  12. data/app/models/marty/data_grid.rb +34 -8
  13. data/app/models/marty/import_type.rb +2 -4
  14. data/app/models/marty/role_type.rb +10 -0
  15. data/app/models/marty/user.rb +9 -4
  16. data/app/models/marty/user_role.rb +2 -3
  17. data/app/models/marty/vw_promise.rb +2 -2
  18. data/app/services/marty/data_grid/constraint.rb +73 -0
  19. data/app/services/marty/data_grid_view/save_grid.rb +63 -0
  20. data/config/locales/en.yml +1 -0
  21. data/db/migrate/107_add_data_grid_constraint.rb +8 -0
  22. data/db/migrate/507_migrate_marty_roles_to_enum.rb +72 -0
  23. data/db/seeds.rb +1 -6
  24. data/lib/marty/permissions.rb +6 -16
  25. data/lib/marty/version.rb +1 -1
  26. data/spec/dummy/config/locales/en.yml +1 -0
  27. data/spec/features/data_grid_spec.rb +499 -0
  28. data/spec/features/data_import_spec.rb +11 -8
  29. data/spec/features/user_view_spec.rb +1 -1
  30. data/spec/fixtures/json/data_grid.json +210 -0
  31. data/spec/fixtures/misc/data_grid_1.txt +15 -0
  32. data/spec/fixtures/misc/data_grid_2.txt +17 -0
  33. data/spec/fixtures/misc/data_grid_3.txt +9 -0
  34. data/spec/fixtures/misc/data_grid_4.txt +5 -0
  35. data/spec/fixtures/misc/data_grid_5.txt +19 -0
  36. data/spec/fixtures/misc/grid1_final_data.json +23 -0
  37. data/spec/fixtures/misc/grid1_final_meta.json +182 -0
  38. data/spec/fixtures/misc/grid2_final_data.json +11 -0
  39. data/spec/fixtures/misc/grid2_final_meta.json +181 -0
  40. data/spec/fixtures/misc/grid5_final_data.json +142 -0
  41. data/spec/fixtures/misc/grid5_final_meta.json +152 -0
  42. data/spec/fixtures/misc/grid_log_errs.json +418 -0
  43. data/spec/models/data_grid_spec.rb +689 -626
  44. data/spec/models/import_type_spec.rb +5 -5
  45. data/spec/spec_helper.rb +9 -7
  46. data/spec/support/users.rb +1 -1
  47. metadata +22 -3
  48. data/app/models/marty/role.rb +0 -6
@@ -13,7 +13,7 @@ class Marty::ImportTypeView < Marty::Grid
13
13
  c.attributes =
14
14
  [
15
15
  :name,
16
- :role__name,
16
+ :role,
17
17
  :db_model_name,
18
18
  :cleaner_function,
19
19
  :validation_function,
@@ -26,8 +26,17 @@ class Marty::ImportTypeView < Marty::Grid
26
26
  c.flex = 1
27
27
  end
28
28
 
29
- attribute :role__name do |c|
29
+ attribute :role do |c|
30
30
  c.width = 150
31
+
32
+ store = ::Marty::RoleType.get_all.sort
33
+
34
+ c.editor_config = {
35
+ multi_select: false,
36
+ store: store,
37
+ type: :string,
38
+ xtype: :combo,
39
+ }
31
40
  end
32
41
 
33
42
  attribute :db_model_name do |c|
@@ -27,7 +27,7 @@ class Marty::MainAuthApp < Marty::AuthApp
27
27
  end
28
28
 
29
29
  def self.has_scripting_perm?
30
- has_admin_perm?
30
+ has_perm?(:admin)
31
31
  end
32
32
 
33
33
  def posting_menu
@@ -50,7 +50,7 @@ class Marty::MainAuthApp < Marty::AuthApp
50
50
  {
51
51
  text: 'Log Maintenance',
52
52
  icon_cls: 'fa fa-wrench glyph',
53
- disabled: !self.class.has_admin_perm?,
53
+ disabled: !self.class.has_perm?(:admin),
54
54
  menu: [
55
55
  :log_view,
56
56
  :log_cleanup,
@@ -64,7 +64,7 @@ class Marty::MainAuthApp < Marty::AuthApp
64
64
  {
65
65
  text: 'API Management',
66
66
  icon_cls: 'fa fa-fighter-jet glyph',
67
- disabled: !self.class.has_admin_perm?,
67
+ disabled: !self.class.has_perm?(:admin),
68
68
  menu: [
69
69
  :api_auth_view,
70
70
  :api_config_view,
@@ -108,7 +108,7 @@ class Marty::MainAuthApp < Marty::AuthApp
108
108
  {
109
109
  text: 'Background Jobs',
110
110
  icon_cls: 'fa fa-user-clock glyph',
111
- disabled: !self.class.has_admin_perm?,
111
+ disabled: !self.class.has_perm?(:admin),
112
112
  menu: [
113
113
  :bg_status,
114
114
  :bg_stop,
@@ -151,8 +151,8 @@ class Marty::MainAuthApp < Marty::AuthApp
151
151
  return super unless self.class.has_any_perm?
152
152
 
153
153
  [ident_menu, sep] +
154
- (self.class.has_admin_perm? ||
155
- self.class.has_user_manager_perm? ? [system_menu, sep] : []) +
154
+ (self.class.has_perm?(:admin) ||
155
+ self.class.has_perm?(:user_manager) ? [system_menu, sep] : []) +
156
156
  data_menus +
157
157
  [
158
158
  applications_menu, sep,
@@ -165,7 +165,7 @@ class Marty::MainAuthApp < Marty::AuthApp
165
165
  action :import_type_view do |a|
166
166
  a.text = I18n.t('import_type')
167
167
  a.handler = :netzke_load_component_by_action
168
- a.disabled = !self.class.has_admin_perm?
168
+ a.disabled = !self.class.has_perm?(:admin)
169
169
  a.icon_cls = 'fa fa-file-import glyph'
170
170
  end
171
171
 
@@ -194,30 +194,30 @@ class Marty::MainAuthApp < Marty::AuthApp
194
194
  a.text = I18n.t('user_view')
195
195
  a.handler = :netzke_load_component_by_action
196
196
  a.icon_cls = 'fa fa-users glyph'
197
- a.disabled = !self.class.has_admin_perm? &&
198
- !self.class.has_user_manager_perm?
197
+ a.disabled = !self.class.has_perm?(:admin) &&
198
+ !self.class.has_perm?(:user_manager)
199
199
  end
200
200
 
201
201
  action :event_view do |a|
202
202
  a.text = I18n.t('event_view')
203
203
  a.handler = :netzke_load_component_by_action
204
204
  a.icon_cls = 'fa fa-bolt glyph'
205
- a.disabled = !self.class.has_admin_perm?
205
+ a.disabled = !self.class.has_perm?(:admin)
206
206
  end
207
207
 
208
208
  action :config_view do |a|
209
209
  a.text = I18n.t('config_view')
210
210
  a.handler = :netzke_load_component_by_action
211
211
  a.icon_cls = 'fa fa-cog glyph'
212
- a.disabled = !self.class.has_admin_perm? &&
213
- !self.class.has_user_manager_perm?
212
+ a.disabled = !self.class.has_perm?(:admin) &&
213
+ !self.class.has_perm?(:user_manager)
214
214
  end
215
215
 
216
216
  action :api_auth_view do |a|
217
217
  a.text = 'API Auth Management'
218
218
  a.handler = :netzke_load_component_by_action
219
219
  a.icon_cls = 'fa fa-key glyph'
220
- a.disabled = !self.class.has_admin_perm?
220
+ a.disabled = !self.class.has_perm?(:admin)
221
221
  end
222
222
 
223
223
  action :api_config_view do |a|
@@ -225,7 +225,7 @@ class Marty::MainAuthApp < Marty::AuthApp
225
225
  a.tooltip = 'Manage API behavior and settings'
226
226
  a.handler = :netzke_load_component_by_action
227
227
  a.icon_cls = 'fa fa-sliders-h glyph'
228
- a.disabled = !self.class.has_admin_perm?
228
+ a.disabled = !self.class.has_perm?(:admin)
229
229
  end
230
230
 
231
231
  action :api_log_view do |a|
@@ -233,7 +233,7 @@ class Marty::MainAuthApp < Marty::AuthApp
233
233
  a.tooltip = 'View API logs'
234
234
  a.handler = :netzke_load_component_by_action
235
235
  a.icon_cls = 'fa fa-pencil-alt glyph'
236
- a.disabled = !self.class.has_admin_perm?
236
+ a.disabled = !self.class.has_perm?(:admin)
237
237
  end
238
238
 
239
239
  action :data_grid_view do |a|
@@ -247,42 +247,42 @@ class Marty::MainAuthApp < Marty::AuthApp
247
247
  a.text = 'Reload Scripts'
248
248
  a.tooltip = 'Reload and tag Delorean scripts'
249
249
  a.icon_cls = 'fa fa-sync-alt glyph'
250
- a.disabled = !self.class.has_admin_perm?
250
+ a.disabled = !self.class.has_perm?(:admin)
251
251
  end
252
252
 
253
253
  action :load_seed do |a|
254
254
  a.text = 'Load Seeds'
255
255
  a.tooltip = 'Load Seeds'
256
256
  a.icon_cls = 'fa fa-retweet glyph'
257
- a.disabled = !self.class.has_admin_perm?
257
+ a.disabled = !self.class.has_perm?(:admin)
258
258
  end
259
259
 
260
260
  action :bg_status do |a|
261
261
  a.text = 'Show Delayed Jobs Status'
262
262
  a.tooltip = 'Run delayed_job status script'
263
263
  a.icon_cls = 'fa fa-desktop glyph'
264
- a.disabled = !self.class.has_admin_perm?
264
+ a.disabled = !self.class.has_perm?(:admin)
265
265
  end
266
266
 
267
267
  action :bg_stop do |a|
268
268
  a.text = 'Stop Delayed Jobs'
269
269
  a.tooltip = 'Run delayed_job stop script'
270
270
  a.icon_cls = 'fa fa-skull glyph'
271
- a.disabled = !self.class.has_admin_perm?
271
+ a.disabled = !self.class.has_perm?(:admin)
272
272
  end
273
273
 
274
274
  action :bg_restart do |a|
275
275
  a.text = 'Restart Delayed Jobs'
276
276
  a.tooltip = 'Run delayed_job restart script using DELAYED_JOB_PARAMS'
277
277
  a.icon_cls = 'fa fa-power-off glyph'
278
- a.disabled = !self.class.has_admin_perm?
278
+ a.disabled = !self.class.has_perm?(:admin)
279
279
  end
280
280
 
281
281
  action :schedule_jobs_dashboard do |a|
282
282
  a.text = 'Schedule Jobs Dashboard'
283
283
  a.tooltip = 'Edit Delayed Jobs Cron schedules'
284
284
  a.icon_cls = 'fa fa-cog glyph'
285
- a.disabled = !self.class.has_admin_perm?
285
+ a.disabled = !self.class.has_perm?(:admin)
286
286
  a.handler = :netzke_load_component_by_action
287
287
  end
288
288
 
@@ -291,14 +291,14 @@ class Marty::MainAuthApp < Marty::AuthApp
291
291
  a.tooltip = 'View Log'
292
292
  a.handler = :netzke_load_component_by_action
293
293
  a.icon_cls = 'fa fa-cog glyph'
294
- a.disabled = !self.class.has_admin_perm?
294
+ a.disabled = !self.class.has_perm?(:admin)
295
295
  end
296
296
 
297
297
  action :log_cleanup do |a|
298
298
  a.text = 'Cleanup Log Table'
299
299
  a.tooltip = 'Delete old log records'
300
300
  a.icon_cls = 'fa fa-cog glyph'
301
- a.disabled = !self.class.has_admin_perm?
301
+ a.disabled = !self.class.has_perm?(:admin)
302
302
  end
303
303
 
304
304
  ######################################################################
@@ -59,7 +59,7 @@ class Marty::PromiseView < Netzke::Tree::Base
59
59
  a.text = a.tooltip = 'Clear'
60
60
  a.disabled = false
61
61
  a.icon_cls = 'fa fa-minus glyph'
62
- a.hidden = !self.class.has_admin_perm?
62
+ a.hidden = !self.class.has_perm?(:admin)
63
63
  end
64
64
 
65
65
  action :download do |a|
@@ -66,6 +66,7 @@ class Marty::ReportSelect < Marty::Form
66
66
 
67
67
  nodes.map do |node|
68
68
  roles = engine.evaluate(node, 'roles') rescue nil
69
+
69
70
  next if roles && !roles.any? { |r| Marty::User.has_role(r) }
70
71
 
71
72
  begin
@@ -39,7 +39,7 @@ class Marty::ScriptForm < Marty::Form
39
39
  end
40
40
 
41
41
  def can_save?(script)
42
- script && self.class.has_dev_perm? && Mcfly.is_infinity(script.obsoleted_dt)
42
+ script && self.class.has_perm?(:dev) && Mcfly.is_infinity(script.obsoleted_dt)
43
43
  end
44
44
 
45
45
  ######################################################################
@@ -11,7 +11,7 @@ module Marty; class UserView < Marty::Grid
11
11
  :firstname,
12
12
  :lastname,
13
13
  :active,
14
- :roles,
14
+ :user_roles,
15
15
  ]
16
16
  end
17
17
 
@@ -27,21 +27,27 @@ module Marty; class UserView < Marty::Grid
27
27
  c.store_config.merge!(sorters: [{ property: :login,
28
28
  direction: 'ASC',
29
29
  }]) if c.attributes.include?(:login)
30
- c.scope = ->(arel) { arel.includes(:roles) }
30
+ c.scope = ->(arel) { arel.includes(:user_roles) }
31
31
  end
32
32
 
33
33
  def self.set_roles(roles, user)
34
- roles ||= []
34
+ roles = [] unless roles.present?
35
35
 
36
- # Destroy old roles (must call destroy for auditing to work properly)
37
- user.user_roles.each do |ur|
38
- ur.destroy unless roles.include?(I18n.t("roles.#{ur.role.name}"))
36
+ roles = Marty::RoleType.get_all.select do |role|
37
+ roles.include?(I18n.t("roles.#{role}", default: role))
39
38
  end
40
39
 
41
- # set new roles
42
- user.roles = Role.select { |r|
43
- roles.include? I18n.t("roles.#{r.name}")
44
- }
40
+ roles_in_user = user.user_roles.map(&:role)
41
+ roles_to_delete = roles_in_user - roles
42
+ roles_to_add = roles - roles_in_user
43
+
44
+ Marty::User.transaction do
45
+ user.user_roles.where(role: roles_to_delete).map(&:destroy!)
46
+
47
+ roles_to_add.each do |role|
48
+ user.user_roles.create!(role: role)
49
+ end
50
+ end
45
51
  end
46
52
 
47
53
  def self.create_edit_user(data)
@@ -49,12 +55,12 @@ module Marty; class UserView < Marty::Grid
49
55
  user = data['id'].nil? ? User.new : User.find(data['id'])
50
56
 
51
57
  user_columns.each do |c|
52
- user.send("#{c}=", data[c.to_s]) unless c == :roles
58
+ user.send("#{c}=", data[c.to_s]) unless c == :user_roles
53
59
  end
54
60
 
55
61
  if user.valid?
56
62
  user.save
57
- set_roles(data['roles'], user)
63
+ set_roles(data['user_roles'], user)
58
64
  end
59
65
 
60
66
  user
@@ -139,20 +145,26 @@ module Marty; class UserView < Marty::Grid
139
145
  c.label = I18n.t('user_grid.active')
140
146
  end
141
147
 
142
- attribute :roles do |c|
148
+ attribute :user_roles do |c|
143
149
  c.width = 100
144
150
  c.flex = 1
145
151
  c.label = I18n.t('user_grid.roles')
146
- c.type = :string,
152
+ c.type = :string
147
153
 
148
- c.getter = lambda do |r|
149
- r.roles.map { |ur| I18n.t("roles.#{ur.name}") }.sort
150
- end
154
+ c.getter = lambda do |r|
155
+ r.user_roles.map do |ur|
156
+ I18n.t("roles.#{ur.role}", default: ur.role)
157
+ end
158
+ end
159
+
160
+ store = ::Marty::RoleType.get_all.sort.map do |role|
161
+ I18n.t("roles.#{role}", default: role)
162
+ end
151
163
 
152
164
  c.editor_config = {
153
165
  multi_select: true,
154
166
  empty_text: I18n.t('user_grid.select_roles'),
155
- store: Role.pluck(:name).map { |n| I18n.t("roles.#{n}") }.sort,
167
+ store: store,
156
168
  type: :string,
157
169
  xtype: :combo,
158
170
  }
@@ -70,6 +70,23 @@ class Marty::DataGrid < Marty::Base
70
70
 
71
71
  dg.errors.add(:base, 'duplicate vertical key combination') unless
72
72
  v_zip_keys.uniq.length == v_zip_keys.length
73
+
74
+ con_chk = []
75
+ begin
76
+ con_chk = Marty::DataGrid::Constraint.parse(dg.data_type, dg.constraint)
77
+ rescue StandardError => e
78
+ dg.errors.add(:base, "Error in constraint: #{e.message}")
79
+ end
80
+ data_check = Marty::DataGrid::Constraint.check_data(dg.data_type,
81
+ dg.data, con_chk)
82
+ return unless data_check.present?
83
+
84
+ data_check.each do |(err, x, y)|
85
+ dg.errors.add(:base, "cell #{x}, #{y} fails constraint check") if
86
+ err == :constraint
87
+ dg.errors.add(:base, "cell #{x}, #{y} incorrect type") if
88
+ err == :type
89
+ end
73
90
  end
74
91
  end
75
92
 
@@ -342,10 +359,14 @@ class Marty::DataGrid < Marty::Base
342
359
 
343
360
  def export_array
344
361
  # add data type metadata row if not default
345
- dt_row = lenient ? ['lenient'] : []
346
- dt_row << data_type unless [nil, DEFAULT_DATA_TYPE].member?(data_type)
362
+ lenstr = 'lenient' if lenient
363
+ typestr = data_type unless [nil, DEFAULT_DATA_TYPE].member?(data_type) &&
364
+ !constraint.present?
365
+
366
+ len_dt = [lenstr, typestr].compact.join(' ')
347
367
 
348
- meta_rows = dt_row.empty? ? [] : [[dt_row.join(' ')]]
368
+ meta_rows = len_dt.present? || constraint.present? ?
369
+ [[len_dt, constraint]] : []
349
370
 
350
371
  meta_rows += metadata.map do |inf|
351
372
  [inf['attr'], inf['type'], inf['dir'], inf['rs_keep'] || '']
@@ -466,7 +487,7 @@ class Marty::DataGrid < Marty::Base
466
487
  data_type, lenient = nil, false
467
488
 
468
489
  # check if there's a data_type definition
469
- dt, *x = rows[0]
490
+ dt, constraint, *x = rows[0]
470
491
  if dt && x.all?(&:nil?)
471
492
  dts = dt.split
472
493
  raise "bad data type '#{dt}'" if dts.count > 2
@@ -474,8 +495,10 @@ class Marty::DataGrid < Marty::Base
474
495
  lenient = dts.delete 'lenient'
475
496
  data_type = dts.first
476
497
  end
498
+ constraint = nil if x.first.in?(['v', 'h'])
477
499
 
478
- rows_for_metadata = rows[(data_type || lenient ? 1 : 0)...blank_index]
500
+ start_md = constraint || data_type || lenient ? 1 : 0
501
+ rows_for_metadata = rows[start_md...blank_index]
479
502
  metadata = rows_for_metadata.map do |attr, type, dir, rs_keep, key|
480
503
  raise 'metadata elements must include attr/type/dir' unless
481
504
  attr && type && dir
@@ -549,11 +572,12 @@ class Marty::DataGrid < Marty::Base
549
572
  end
550
573
  end
551
574
 
552
- [metadata, data, data_type, lenient]
575
+ [metadata, data, data_type, lenient, constraint]
553
576
  end
554
577
 
555
578
  def self.create_from_import(name, import_text, created_dt = nil)
556
- metadata, data, data_type, lenient = parse(created_dt, import_text, {})
579
+ metadata, data, data_type, lenient, constraint = parse(created_dt,
580
+ import_text, {})
557
581
  dg = new
558
582
  dg.name = name
559
583
  dg.data = data
@@ -561,12 +585,13 @@ class Marty::DataGrid < Marty::Base
561
585
  dg.lenient = !!lenient
562
586
  dg.metadata = metadata
563
587
  dg.created_dt = created_dt if created_dt
588
+ dg.constraint = constraint
564
589
  dg.save!
565
590
  dg
566
591
  end
567
592
 
568
593
  def update_from_import(name, import_text, created_dt = nil)
569
- new_metadata, data, data_type, lenient =
594
+ new_metadata, data, data_type, lenient, constraint =
570
595
  self.class.parse(created_dt, import_text, {})
571
596
 
572
597
  self.name = name
@@ -575,6 +600,7 @@ class Marty::DataGrid < Marty::Base
575
600
  self.lenient = !!lenient
576
601
  # Otherwise changed will depend on order in hashes
577
602
  self.metadata = new_metadata unless metadata == new_metadata
603
+ self.constraint = constraint
578
604
  self.created_dt = created_dt if created_dt
579
605
  save!
580
606
  end
@@ -27,9 +27,7 @@ class Marty::ImportType < Marty::Base
27
27
  self.preprocess_function = nil if preprocess_function.blank?
28
28
  end
29
29
 
30
- belongs_to :role
31
-
32
- validates_presence_of :name, :db_model_name, :role_id
30
+ validates_presence_of :name, :db_model_name, :role
33
31
  validates_uniqueness_of :name
34
32
  validates_with ImportTypeValidator
35
33
 
@@ -38,6 +36,6 @@ class Marty::ImportType < Marty::Base
38
36
  end
39
37
 
40
38
  def allow_import?
41
- Mcfly.whodunnit && Mcfly.whodunnit.roles.pluck(:id).include?(role_id)
39
+ Mcfly.whodunnit && Mcfly.whodunnit.roles.include?(role)
42
40
  end
43
41
  end
@@ -0,0 +1,10 @@
1
+ class Marty::RoleType < Marty::Base
2
+ extend Marty::PgEnum
3
+
4
+ VALUES = [
5
+ 'admin',
6
+ 'user_manager',
7
+ 'dev',
8
+ 'viewer'
9
+ ]
10
+ end