marty 5.2.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +1 -1
  3. data/.rubocop_todo.yml +62 -77
  4. data/Dockerfile.dummy +1 -1
  5. data/app/assets/javascripts/marty/extjs/extensions/marty.js +11 -0
  6. data/app/components/marty/log_view.rb +1 -1
  7. data/app/components/marty/promise_view.rb +0 -3
  8. data/app/jobs/marty/remove_old_promises_job.rb +7 -0
  9. data/app/models/marty/data_grid.rb +0 -163
  10. data/app/models/marty/event.rb +2 -2
  11. data/app/models/marty/posting.rb +0 -7
  12. data/app/models/marty/promise.rb +1 -1
  13. data/app/services/marty/data_grid_view/save_grid.rb +2 -2
  14. data/app/services/marty/jobs/schedule.rb +5 -0
  15. data/db/migrate/510_schedule_job_to_remove_old_promises.rb +19 -0
  16. data/lib/marty.rb +8 -0
  17. data/{other → lib}/marty/api/base.rb +14 -17
  18. data/{other → lib}/marty/diagnostic/aws/ec2_instance.rb +2 -2
  19. data/{other → lib}/marty/diagnostic/aws/error.rb +0 -0
  20. data/{other → lib}/marty/diagnostic/base.rb +0 -0
  21. data/{other → lib}/marty/diagnostic/collection.rb +0 -0
  22. data/{other → lib}/marty/diagnostic/connections.rb +0 -0
  23. data/{other → lib}/marty/diagnostic/database.rb +0 -0
  24. data/{other → lib}/marty/diagnostic/delayed_job_version.rb +0 -0
  25. data/{other → lib}/marty/diagnostic/delayed_job_workers.rb +0 -0
  26. data/{other → lib}/marty/diagnostic/environment_variables.rb +0 -0
  27. data/{other → lib}/marty/diagnostic/fatal.rb +0 -0
  28. data/{other → lib}/marty/diagnostic/node.rb +0 -0
  29. data/{other → lib}/marty/diagnostic/nodes.rb +1 -2
  30. data/{other → lib}/marty/diagnostic/packer.rb +0 -0
  31. data/{other → lib}/marty/diagnostic/reporter.rb +2 -2
  32. data/{other → lib}/marty/diagnostic/request.rb +0 -0
  33. data/{other → lib}/marty/diagnostic/scheduled_jobs.rb +0 -0
  34. data/{other → lib}/marty/diagnostic/version.rb +0 -0
  35. data/lib/marty/engine.rb +5 -0
  36. data/lib/marty/version.rb +3 -1
  37. data/lib/marty/xl.rb +5 -5
  38. data/spec/dummy/app/assets/config/manifest.js +0 -0
  39. data/spec/dummy/app/jobs/test2_job.rb +4 -0
  40. data/spec/dummy/config/application.rb +1 -4
  41. data/spec/dummy/config/initializers/assets.rb +1 -1
  42. data/spec/features/extjs_spec.rb +58 -0
  43. data/spec/features/schedule_jobs_dashboard_spec.rb +9 -9
  44. data/spec/lib/logger_spec.rb +2 -2
  45. data/spec/models/posting_spec.rb +0 -13
  46. data/spec/services/jobs/schedule_spec.rb +40 -0
  47. data/spec/support/chromedriver.rb +2 -0
  48. metadata +27 -22
  49. data/spec/dummy/app/jobs/test_job2.rb +0 -4
@@ -1,6 +1,6 @@
1
1
  # ONLY FOR DEVELOPMENT PURPOSES
2
2
  # dockerfile is not suitable to produce a production-grade docker image
3
- FROM ruby:2.5.1
3
+ FROM ruby:2.6.3
4
4
 
5
5
  WORKDIR /opt/app
6
6
 
@@ -897,3 +897,14 @@ Ext.define('Marty.layout.container.Auto', {
897
897
  },
898
898
 
899
899
  });
900
+
901
+ Ext.define('overrides.grid.column.Column', {
902
+ override: 'Ext.grid.column.Column',
903
+
904
+ initConfig: function(config) {
905
+ if (!config.renderer && !this.updater) {
906
+ config.formatter = 'htmlEncode'
907
+ }
908
+ return this.callParent(arguments)
909
+ }
910
+ })
@@ -59,7 +59,7 @@ class Marty::LogView < Marty::Grid
59
59
  c.read_only = true
60
60
  c.filterable = true
61
61
  c.xtype = :datecolumn
62
- c.format = 'Y-m-d h:i:s.u'
62
+ c.format = 'Y-m-d H:i:s.u'
63
63
  c.field_config = {
64
64
  xtype: :displayfield,
65
65
  }
@@ -46,9 +46,6 @@ class Marty::PromiseView < Netzke::Tree::Base
46
46
  update: class_can?(:update),
47
47
  delete: class_can?(:delete)
48
48
  }
49
-
50
- # garbage collect old promises (hacky to do this here)
51
- Marty::Promise.cleanup(false)
52
49
  end
53
50
 
54
51
  def bbar
@@ -0,0 +1,7 @@
1
+ module Marty
2
+ class RemoveOldPromisesJob < ::Marty::CronJob
3
+ def perform
4
+ Marty::Promise.cleanup(false)
5
+ end
6
+ end
7
+ end
@@ -226,12 +226,6 @@ class Marty::DataGrid < Marty::Base
226
226
  "ri: #{row_info}" if res['error']
227
227
  end
228
228
 
229
- if ret_grid_data
230
- dg = find(dgh['id'])
231
- md, mmd = modify_grid(h_passed, dg.metadata, dg.data)
232
- res['data'] = md
233
- res['metadata'] = mmd
234
- end
235
229
  res
236
230
  end
237
231
 
@@ -683,142 +677,8 @@ class Marty::DataGrid < Marty::Base
683
677
  end
684
678
  end
685
679
 
686
- def self.modify_grid(params, metadata, data)
687
- removes = ['h', 'v'].each_with_object({}) { |dir, hash| hash[dir] = Set.new }
688
-
689
- metadata_copy, data_copy = metadata.deep_dup, data.deep_dup
690
-
691
- metadata_copy.each do |meta|
692
- dir, keys, type, rs_keep = meta.values_at(
693
- 'dir', 'keys', 'type', 'rs_keep')
694
- next unless rs_keep
695
-
696
- if type == 'numrange' || type == 'int4range'
697
- modop, modvalparm = parse_bounds(rs_keep)
698
- modval = params[modvalparm]
699
- if modval
700
- prune_a, rewrite_a = compute_numeric_mods(keys, modop, modval)
701
- removes[dir].merge(prune_a)
702
- rewrite_a.each { |(ind, value)| keys[ind] = value }
703
- end
704
- else
705
- modval = params[rs_keep]
706
- if modval
707
- prune_a, rewrite_a = compute_set_mods(keys, modval)
708
- removes[dir].merge(prune_a)
709
- rewrite_a.each { |(ind, value)| keys[ind] = value }
710
- end
711
- end
712
- end
713
-
714
- removes.reject! { |_dir, set| set.empty? }
715
-
716
- removes.each do |dir, _set|
717
- metadata_copy.select { |m| m['dir'] == dir }.each do |meta|
718
- meta['keys'] = remove_indices(meta['keys'], removes[dir])
719
- end
720
- end
721
-
722
- data_copy = remove_indices(data_copy, removes['v']) if removes['v']
723
-
724
- data_copy.each_index do |index|
725
- data_copy[index] = remove_indices(data_copy[index], removes['h'])
726
- end if removes['h']
727
-
728
- [data_copy, metadata_copy]
729
- end
730
-
731
680
  private
732
681
 
733
- def self.remove_indices(orig_array, inds)
734
- orig_array.each_with_object([]).with_index do |(item, new_array), index|
735
- new_array.push(item) unless inds.include?(index)
736
- end
737
- end
738
-
739
- def self.opposite_sign(op) # toggle sign and inclusivity
740
- {
741
- :< => :>=,
742
- :<= => :>,
743
- :> => :<=,
744
- :>= => :<,
745
- }[op]
746
- end
747
-
748
- def self.compute_numeric_mods(keys, op, val)
749
- @keyhash ||= {}
750
- prune_a, rewrite_a = [], []
751
-
752
- # features allow multiple values, but for constraint on a grid range
753
- # only a scalar is meaningful. so if there are multiple values we
754
- # take the first value to use
755
- value = val.is_a?(Array) ? val[0] : val
756
- keys.each_with_index do |key, index|
757
- lhop, orig_lhv, orig_rhv, rhop = @keyhash[key] ||= parse_range(key)
758
-
759
- lhv, rhv = orig_lhv || -Float::INFINITY, orig_rhv || Float::INFINITY
760
-
761
- case op
762
- when :>=, :>
763
- next if value > rhv
764
-
765
- if value == rhv
766
- if rhop == :<= && op == :>=
767
- rewrite_a.push(
768
- [index, rewrite_range(lhop, orig_lhv, orig_rhv, :<)])
769
- end
770
- elsif value > lhv
771
- rewrite_a.push(
772
- [index, rewrite_range(lhop, orig_lhv, value, opposite_sign(op))])
773
- elsif value == lhv && lhop == :>= && op == :>
774
- rewrite_a.push([index, rewrite_range(:>=, value, value, :<=)])
775
- elsif value <= lhv
776
- prune_a.push(index)
777
- end
778
- when :<=, :<
779
- next if value < lhv
780
-
781
- if value == lhv
782
- if lhop == :>= && op == :<=
783
- rewrite_a.push(
784
- [index, rewrite_range(:>, orig_lhv, orig_rhv, rhop)])
785
- end
786
- elsif value < rhv
787
- rewrite_a.push(
788
- [index, rewrite_range(opposite_sign(op), value, orig_rhv, rhop)])
789
- elsif value == rhv && rhop == :<= && op == :<
790
- rewrite_a.push([index, rewrite_range(:>=, value, value, :<=)])
791
- elsif value >= rhv
792
- prune_a.push(index)
793
- end
794
- end
795
- end
796
- [prune_a, rewrite_a]
797
- end
798
-
799
- # value is a list of what to keep
800
- def self.compute_set_mods(keys, val)
801
- prune_a, rewrite_a, value = [], [], Array(val)
802
-
803
- keys.each_with_index do |key, index|
804
- # rewrite any nil (wildcard) keys in the dimension
805
- # to be our 'to-keep' val(s)
806
- if key.nil?
807
- rewrite_a.push([index, value])
808
- next
809
- end
810
-
811
- remove = key - value
812
- if remove == key
813
- prune_a.push(index)
814
- next
815
- end
816
-
817
- rewrite_a.push([index, key - remove]) if remove != []
818
- end
819
- [prune_a, rewrite_a]
820
- end
821
-
822
682
  def self.parse_range(key)
823
683
  match = key.match(/\A(\[|\()([0-9\.-]*),([0-9\.-]*)(\]|\))\z/)
824
684
  raise "unrecognized pattern #{key}" unless match
@@ -831,29 +691,6 @@ class Marty::DataGrid < Marty::Base
831
691
  [lboundary == '(' ? :> : :>=, lhv, rhv, rboundary == ')' ? :< : :<=]
832
692
  end
833
693
 
834
- def self.rewrite_range(lb, lhv, rhv, rb)
835
- lboundary = lb == :> ? '(' : '['
836
-
837
- # even though numranges are float type, we don't want to output ".0"
838
- # for integer values. So for values like that we convert to int
839
- # first before conversion to string
840
- lvalue = (lhv.to_i == lhv ? lhv.to_i : lhv).to_s
841
- rvalue = (rhv.to_i == rhv ? rhv.to_i : rhv).to_s
842
- rboundary = rb == :< ? ')' : ']'
843
- lboundary + lvalue + ',' + rvalue + rboundary
844
- end
845
-
846
- def self.parse_bounds(key)
847
- match = key.match(/\A *(<|>|<=|>=)? *([a-z_]+) *\z/)
848
- raise "unrecognized pattern #{key}" unless match
849
-
850
- opstr, ident = match[1..2]
851
-
852
- # data grid value is expressed as what to keep
853
- # we convert to the opposite (what to prune)
854
- [opposite_sign(opstr.to_sym), ident]
855
- end
856
-
857
694
  def self.remove_not(string)
858
695
  return string unless string.starts_with?(NOT_STRING_START)
859
696
  return string unless string.ends_with?(NOT_STRING_END)
@@ -119,10 +119,10 @@ SQL
119
119
  end
120
120
 
121
121
  def self.last_event(klass, subject_id, operation = nil)
122
- hash = all_running.select do |pm|
122
+ hash = all_running.reverse.find do |pm|
123
123
  pm['klass'] == klass && pm['subject_id'] == subject_id.to_i &&
124
124
  (operation.nil? || pm['enum_event_operation'] == operation)
125
- end.last
125
+ end
126
126
 
127
127
  return hash if hash
128
128
 
@@ -67,13 +67,6 @@ class Marty::Posting < Marty::Base
67
67
  q.order('created_dt DESC').first.attributes
68
68
  end
69
69
 
70
- def self.get_latest(limit, _is_test = nil)
71
- # IMPORTANT: is_test arg is ignored (KEEP for backward compat.)
72
-
73
- q = where("created_dt <> 'infinity'").
74
- order('created_dt DESC').limit(limit)
75
- end
76
-
77
70
  delorean_fn :get_latest_by_type, sig: [1, 2] do |limit, posting_types = []|
78
71
  raise 'missing posting types list' unless posting_types
79
72
  raise 'bad posting types list' unless posting_types.is_a?(Array)
@@ -213,7 +213,7 @@ class Marty::Promise < Marty::Base
213
213
  def cleanup(all = false)
214
214
  where(
215
215
  'start_dt < ? AND parent_id IS NULL',
216
- DateTime.now - (all ? 0.hours : 4.hours)
216
+ all ? Time.zone.now : 4.hours.ago
217
217
  ).destroy_all
218
218
  rescue StandardError => e
219
219
  Marty::Util.logger.error("promise GC error: #{e}")
@@ -25,8 +25,8 @@ module Marty
25
25
  data_as_array = data.map do |row|
26
26
  row.keys.map { |key| row[key] }
27
27
  end
28
- vcnt = dg.metadata.select { |md| md['dir'] == 'v' }.count
29
- hcnt = dg.metadata.select { |md| md['dir'] == 'h' }.count
28
+ vcnt = dg.metadata.count { |md| md['dir'] == 'v' }
29
+ hcnt = dg.metadata.count { |md| md['dir'] == 'h' }
30
30
  cur_data_dim = [dg.data.length, dg.data[0].length]
31
31
  exported = dg.export.lines
32
32
  sep = exported.each_with_index.detect { |l, _i| /^\s*$/.match(l) }.last
@@ -7,6 +7,11 @@ module Marty
7
7
  glob = Rails.root.join('app', 'jobs', '**', '*_job.rb')
8
8
  Dir.glob(glob).each { |f| require f }
9
9
 
10
+ glob2 = Marty.root.join('app', 'jobs', '**', '*_job.rb')
11
+ Dir.glob(glob2).each { |f| require f }
12
+
13
+ Delayed::Job.where.not(cron: nil).each(&:destroy!)
14
+
10
15
  Marty::CronJob.subclasses.map do |klass|
11
16
  klass.schedule
12
17
  [klass.name, klass.cron_expression]
@@ -0,0 +1,19 @@
1
+ class ScheduleJobToRemoveOldPromises < ActiveRecord::Migration[4.2]
2
+ def up
3
+ cron_every_hour = '0 * * * *'
4
+
5
+ Marty::BackgroundJob::Schedule.create!(
6
+ job_class: 'Marty::RemoveOldPromisesJob',
7
+ cron: cron_every_hour,
8
+ state: 'on'
9
+ )
10
+
11
+ ::Marty::RemoveOldPromisesJob.schedule
12
+ end
13
+
14
+ def down
15
+ Marty::BackgroundJob::Schedule.find_by(
16
+ job_class: 'Marty::RemoveOldPromisesJob'
17
+ )&.destroy
18
+ end
19
+ end
@@ -21,3 +21,11 @@ require 'marty/json_schema'
21
21
  # to the Gemfile
22
22
  require 'net-ldap'
23
23
  require 'delayed_cron_job'
24
+
25
+ require 'pathname'
26
+
27
+ module Marty
28
+ def self.root
29
+ Pathname.new(File.expand_path('..', __dir__))
30
+ end
31
+ end
@@ -28,11 +28,9 @@ class Marty::Api::Base
28
28
  params
29
29
  end
30
30
 
31
- def self.before_evaluate api_params
32
- end
31
+ def self.before_evaluate api_params; end
33
32
 
34
- def self.after_evaluate api_params, result
35
- end
33
+ def self.after_evaluate api_params, result; end
36
34
 
37
35
  @@numbers = {}
38
36
  @@schemas = {}
@@ -60,7 +58,7 @@ class Marty::Api::Base
60
58
  # get_schema will either return a hash with the schema,
61
59
  # or a string with the error
62
60
  input_schema = @@schemas[schema_key] ||=
63
- Marty::JsonSchema.get_schema(*schema_key)
61
+ Marty::JsonSchema.get_schema(*schema_key)
64
62
  rescue StandardError => e
65
63
  return { error: e.message }
66
64
  end
@@ -69,7 +67,7 @@ class Marty::Api::Base
69
67
  if input_schema.is_a?(Hash)
70
68
  # fix numbers types
71
69
  numbers = @@numbers[schema_key] ||=
72
- Marty::JsonSchema.get_numbers(input_schema)
70
+ Marty::JsonSchema.get_numbers(input_schema)
73
71
 
74
72
  # modify params in place
75
73
  Marty::JsonSchema.fix_numbers(params[:params], numbers)
@@ -85,14 +83,14 @@ class Marty::Api::Base
85
83
  return { "error": input_schema } if input_schema.is_a?(String)
86
84
 
87
85
  begin
88
- res = SchemaValidator::validate_schema(input_schema, params[:params])
86
+ res = SchemaValidator.validate_schema(input_schema, params[:params])
89
87
  rescue NameError
90
88
  return { error: "Unrecognized PgEnum for attribute #{params[:attr]}" }
91
89
  rescue StandardError => e
92
90
  return { error: "#{params[:attr]}: #{e.message}" }
93
91
  end
94
92
 
95
- schema_errors = SchemaValidator::get_errors(res) unless res.empty?
93
+ schema_errors = SchemaValidator.get_errors(res) unless res.empty?
96
94
  return { error: "Error(s) validating: #{schema_errors}" } if
97
95
  schema_errors
98
96
  end
@@ -101,8 +99,8 @@ class Marty::Api::Base
101
99
  begin
102
100
  engine = Marty::ScriptSet.new(params[:tag]).get_engine(params[:script])
103
101
  rescue StandardError => e
104
- error = "Can't get engine: #{params[:script] || 'nil'} with tag: " +
105
- "#{params[:tag] || 'nil'}; message: #{e.message}"
102
+ error = "Can't get engine: #{params[:script] || 'nil'} with tag: " \
103
+ "#{params[:tag] || 'nil'}; message: #{e.message}"
106
104
  Marty::Logger.info error
107
105
  return { error: error }
108
106
  end
@@ -127,13 +125,13 @@ class Marty::Api::Base
127
125
  if config[:output_validated] && !(res.is_a?(Hash) && res['error'])
128
126
  begin
129
127
  output_schema_params = params + { attr: params[:attr] + '_' }
130
- schema = SchemaValidator::get_schema(output_schema_params)
128
+ schema = SchemaValidator.get_schema(output_schema_params)
131
129
  rescue StandardError => e
132
130
  return { error: e.message }
133
131
  end
134
132
 
135
133
  begin
136
- schema_errors = SchemaValidator::validate_schema(schema, res)
134
+ schema_errors = SchemaValidator.validate_schema(schema, res)
137
135
  rescue NameError
138
136
  return { error: "Unrecognized PgEnum for attribute #{attr}" }
139
137
  rescue StandardError => e
@@ -160,7 +158,7 @@ class Marty::Api::Base
160
158
  Marty::Logger.info "Evaluation error: #{msg}"
161
159
  return retval = msg
162
160
  ensure
163
- error = Hash === retval ? retval[:error] : nil
161
+ error = retval.is_a?(Hash) ? retval[:error] : nil
164
162
  end
165
163
  end
166
164
 
@@ -189,8 +187,7 @@ class Marty::Api::Base
189
187
  end_time: Time.zone.now,
190
188
  error: error,
191
189
  remote_ip: request.remote_ip,
192
- auth_name: params[:auth]
193
- }
190
+ auth_name: params[:auth] }
194
191
  end
195
192
 
196
193
  def self.log result, params, request
@@ -221,7 +218,7 @@ class Marty::Api::Base
221
218
  end
222
219
 
223
220
  def self.massage_message(msg)
224
- m = %r|'#/([^']+)' of type ([^ ]+) matched the disallowed schema|.
221
+ m = %r{'#/([^']+)' of type ([^ ]+) matched the disallowed schema}.
225
222
  match(msg)
226
223
 
227
224
  return msg unless m
@@ -239,7 +236,7 @@ class Marty::Api::Base
239
236
  fa, fragment, message, errors = errs.values_at(:failed_attribute,
240
237
  :fragment,
241
238
  :message, :errors)
242
- ((['AllOf', 'AnyOf', 'Not'].include?(fa) && fragment == '#/') ?
239
+ (['AllOf', 'AnyOf', 'Not'].include?(fa) && fragment == '#/' ?
243
240
  [] : [massage_message(message)]) + _get_errors(errors || {})
244
241
  end
245
242
  end
@@ -70,13 +70,13 @@ class Marty::Diagnostic::Aws::Ec2Instance < Marty::Aws::Request
70
70
  instances = ensure_resp(
71
71
  ['reservationSet', 'item', 'instancesSet', 'item'],
72
72
  resp
73
- ).map do |i|
73
+ ).flat_map do |i|
74
74
  {
75
75
  'id' => i['instanceId'],
76
76
  'ip' => i['privateIpAddress'],
77
77
  'state' => i['instanceState'],
78
78
  }
79
- end.flatten(1)
79
+ end
80
80
  end
81
81
 
82
82
  def get_private_ips