marty 5.2.0 → 6.1.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 (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