marty 8.0.0 → 8.2.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +7 -0
  3. data/app/assets/javascripts/marty/cable.js +12 -0
  4. data/app/assets/stylesheets/marty/codemirror/notifications.css +4 -0
  5. data/app/channels/application_cable/channel.rb +4 -0
  6. data/app/channels/application_cable/connection.rb +17 -0
  7. data/app/channels/marty/notification_channel.rb +8 -0
  8. data/app/components/marty/auth_app.rb +54 -6
  9. data/app/components/marty/auth_app/client/auth_app.js +53 -0
  10. data/app/components/marty/main_auth_app.rb +47 -1
  11. data/app/components/marty/notifications/config_view.rb +56 -0
  12. data/app/components/marty/notifications/deliveries_view.rb +50 -0
  13. data/app/components/marty/notifications/grid_view.rb +56 -0
  14. data/app/components/marty/notifications/window.rb +23 -0
  15. data/app/components/marty/promise_view.rb +13 -1
  16. data/app/components/marty/promise_view/client/promise_view.js +18 -0
  17. data/app/components/marty/{schedule_jobs_dashboard/client/schedule_jobs_dashboard.js → schedule_jobs_grid/client/schedule_jobs_grid.js} +0 -0
  18. data/app/components/marty/simple_app/client/simple_app.js +5 -1
  19. data/app/components/marty/users/user_view.rb +177 -0
  20. data/app/controllers/marty/application_controller.rb +13 -0
  21. data/app/models/marty/data_grid.rb +1 -1
  22. data/app/models/marty/notifications.rb +4 -0
  23. data/app/models/marty/notifications/config.rb +25 -0
  24. data/app/models/marty/notifications/delivery.rb +56 -0
  25. data/app/models/marty/notifications/event_type.rb +11 -0
  26. data/app/models/marty/notifications/notification.rb +25 -0
  27. data/app/models/marty/promise.rb +1 -0
  28. data/app/models/marty/user.rb +14 -0
  29. data/app/services/marty/notifications/create.rb +39 -0
  30. data/app/services/marty/notifications/create_deliveries.rb +25 -0
  31. data/app/services/marty/notifications/process_delivery.rb +11 -0
  32. data/app/services/marty/notifications/processors/email.rb +13 -0
  33. data/app/services/marty/notifications/processors/sms.rb +13 -0
  34. data/app/services/marty/notifications/processors/web.rb +27 -0
  35. data/app/services/marty/promises/cancel.rb +37 -0
  36. data/app/services/marty/promises/delorean/create.rb +4 -0
  37. data/app/services/marty/promises/ruby/create.rb +4 -1
  38. data/app/views/marty/diagnostic/op.html.erb +4 -0
  39. data/db/migrate/514_remove_marty_events.rb +1 -1
  40. data/db/migrate/519_create_marty_notifications_event_types.rb +16 -0
  41. data/db/migrate/520_create_marty_notifications.rb +11 -0
  42. data/db/migrate/521_create_marty_notifications_deliveries.rb +22 -0
  43. data/db/migrate/522_create_marty_notifications_config.rb +21 -0
  44. data/db/sql/lookup_grid_distinct_v1.sql +6 -6
  45. data/lib/marty.rb +2 -0
  46. data/lib/marty/diagnostic/version.rb +36 -26
  47. data/lib/marty/engine.rb +7 -1
  48. data/lib/marty/mcfly_model.rb +27 -6
  49. data/lib/marty/railtie.rb +1 -0
  50. data/lib/marty/version.rb +1 -1
  51. data/marty.gemspec +3 -0
  52. data/spec/controllers/diagnostic/controller_spec.rb +1 -1
  53. data/spec/dummy/app/models/gemini/fannie_bup.rb +27 -13
  54. data/spec/dummy/app/models/gemini/helper.rb +62 -0
  55. data/spec/features/notifications_spec.rb +224 -0
  56. data/spec/job_helper.rb +14 -1
  57. data/spec/lib/mcfly_model_spec.rb +14 -7
  58. data/spec/models/promise_spec.rb +121 -0
  59. data/spec/services/notifications/create_spec.rb +82 -0
  60. metadata +73 -3
@@ -42,6 +42,7 @@ class Marty::Promise < Marty::Base
42
42
  def set_result(res)
43
43
  # log "SETRES #{Process.pid} #{self}"
44
44
 
45
+ reload
45
46
  # promise must have been started and not yet ended
46
47
  if !start_dt || end_dt || result != {}
47
48
  # log "SETERR #{Process.pid} #{self}"
@@ -9,6 +9,13 @@ class Marty::User < Marty::Base
9
9
 
10
10
  has_many :user_roles, dependent: :destroy
11
11
 
12
+ has_many(
13
+ :notification_deliveries,
14
+ class_name: '::Marty::Notifications::Delivery',
15
+ dependent: :destroy,
16
+ foreign_key: :recipient_id
17
+ )
18
+
12
19
  scope :active, -> { where(active: true) }
13
20
 
14
21
  validate :verify_changes
@@ -114,6 +121,13 @@ class Marty::User < Marty::Base
114
121
  end
115
122
  end
116
123
 
124
+ def unread_web_notifications_count
125
+ notification_deliveries.where(
126
+ delivery_type: :web,
127
+ state: [:sent]
128
+ ).count
129
+ end
130
+
117
131
  private
118
132
 
119
133
  def verify_changes
@@ -0,0 +1,39 @@
1
+ module Marty
2
+ module Notifications
3
+ module Create
4
+ extend Delorean::Functions
5
+
6
+ delorean_fn :call do |event_type:, text:|
7
+ notification, deliveries = ActiveRecord::Base.transaction do
8
+ notification = ::Marty::Notifications::Notification.create!(
9
+ event_type: event_type,
10
+ text: text,
11
+ state: :pending
12
+ )
13
+
14
+ # FIXME: We should consider processing deliveries in the background
15
+ deliveries = ::Marty::Notifications::CreateDeliveries.call(
16
+ notification: notification
17
+ )
18
+
19
+ [notification, deliveries]
20
+ end
21
+
22
+ deliveries.each do |delivery|
23
+ ::Marty::Notifications::ProcessDelivery.call(
24
+ delivery: delivery
25
+ )
26
+ end
27
+
28
+ notification.set_processed!
29
+
30
+ {
31
+ id: notification.id,
32
+ event_type: event_type,
33
+ text: text,
34
+ state: notification.state,
35
+ }
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,25 @@
1
+ module Marty
2
+ module Notifications
3
+ module CreateDeliveries
4
+ class << self
5
+ def call(notification:)
6
+ configs(notification).map do |config|
7
+ notification.deliveries.create!(
8
+ state: :pending,
9
+ delivery_type: config.delivery_type,
10
+ recipient: config.recipient,
11
+ text: config.text
12
+ )
13
+ end
14
+ end
15
+
16
+ def configs(notification)
17
+ Marty::Notifications::Config.where(
18
+ state: :on,
19
+ event_type: notification.event_type
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,11 @@
1
+ module Marty
2
+ module Notifications
3
+ module ProcessDelivery
4
+ class << self
5
+ def call(delivery:)
6
+ delivery.processor.call(delivery: delivery)
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module Marty
2
+ module Notifications
3
+ module Processors
4
+ module Email
5
+ class << self
6
+ def call(delivery:)
7
+ raise 'Not implemented!'
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Marty
2
+ module Notifications
3
+ module Processors
4
+ module Sms
5
+ class << self
6
+ def call(delivery:)
7
+ raise 'Not implemented!'
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ module Marty
2
+ module Notifications
3
+ module Processors
4
+ module Web
5
+ class << self
6
+ def call(delivery:)
7
+ delivery.set_sent!
8
+ notify_websocket(delivery: delivery)
9
+ end
10
+
11
+ private
12
+
13
+ def notify_websocket(delivery:)
14
+ return unless Rails.application.config.marty.enable_action_cable
15
+
16
+ unread_notifications_count = delivery.recipient&.unread_web_notifications_count
17
+
18
+ ActionCable.server.broadcast(
19
+ "marty_notifications_#{delivery.recipient_id}",
20
+ unread_notifications_count: unread_notifications_count
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,37 @@
1
+ module Marty::Promises
2
+ module Cancel
3
+ class << self
4
+ def call(id)
5
+ ids = get_all_ids(id)
6
+ promises = Marty::Promise.where(id: ids)
7
+ jobids = promises.map(&:job_id).compact.sort
8
+ Delayed::Job.where(id: jobids).destroy_all
9
+ promises.each do |p|
10
+ p.update!(status: false,
11
+ end_dt: p.end_dt || Time.zone.now,
12
+ result: p.result + { error: 'Cancelled' })
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ # Promises are nodes/leaves on a tree. Given a promise id
19
+ # from anywhere on the tree, find the ids of all promises
20
+ # on that tree.
21
+ def get_all_ids(id)
22
+ get_base = lambda do |pid|
23
+ p = Marty::Promise.find_by(id: pid)
24
+ p.parent_id ? get_base.call(p.parent_id) : pid
25
+ end
26
+ base_id = get_base.call(id)
27
+ get_children = lambda do |pid|
28
+ children = Marty::Promise.where(parent_id: pid).pluck(:id)
29
+ children.present? ?
30
+ (children + children.map { |cid| get_children.call(cid) }).flatten :
31
+ children
32
+ end
33
+ [base_id] + get_children.call(base_id)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -15,9 +15,13 @@ module Marty
15
15
  pid = promise_params[:_parent_id]
16
16
  if pid
17
17
  ppr = Marty::Promise.find_by(id: pid)
18
+ # make sure parent isn't cancelled
19
+ return if ppr&.result&.[]('error') == 'Cancelled'
20
+
18
21
  default_priority = ppr.priority if ppr
19
22
  end
20
23
  priority = promise_params['p_priority'] || default_priority
24
+
21
25
  promise = Marty::Promise.create(
22
26
  title: title,
23
27
  user_id: promise_params[:_user_id],
@@ -15,10 +15,13 @@ module Marty
15
15
  pid = promise_params[:_parent_id]
16
16
  if pid
17
17
  ppr = Marty::Promise.find_by(id: pid)
18
+ # make sure parent isn't cancelled
19
+ return if ppr&.result&.[]('error') == 'Cancelled'
20
+
18
21
  default_priority = ppr.priority if ppr
19
22
  end
20
-
21
23
  priority = promise_params['p_priority'] || default_priority
24
+
22
25
  promise = Marty::Promise.create(
23
26
  title: title,
24
27
  user_id: promise_params[:_user_id],
@@ -1,5 +1,9 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
+ <% if cookies[:dark_mode] == 'true' %>
4
+ <%= stylesheet_link_tag 'marty/dark_mode', media: 'all', 'data-turbolinks-track': 'reload' %>
5
+ <% end %>
6
+
3
7
  <head>
4
8
  <title><%=Rails.application.class.parent_name%> Diagnostic</title>
5
9
  <style type="text/css">
@@ -3,7 +3,7 @@ class RemoveMartyEvents < ActiveRecord::Migration[4.2]
3
3
  drop_table :marty_events
4
4
 
5
5
  execute <<-SQL
6
- DROP TYPE enum_event_operations;
6
+ DROP TYPE IF EXISTS enum_event_operations;
7
7
  SQL
8
8
  end
9
9
 
@@ -0,0 +1,16 @@
1
+ class CreateMartyNotificationsEventTypes < ActiveRecord::Migration[4.2]
2
+ def up
3
+ values = ::Marty::Notifications::EventType::VALUES
4
+ str_values = values.map {|v| ActiveRecord::Base.connection.quote v}.join ','
5
+
6
+ execute <<-SQL
7
+ CREATE TYPE marty_notifications_event_types AS ENUM (#{str_values});
8
+ SQL
9
+ end
10
+
11
+ def down
12
+ execute <<-SQL
13
+ DROP TYPE marty_notifications_event_types;
14
+ SQL
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ class CreateMartyNotifications < ActiveRecord::Migration[4.2]
2
+ def change
3
+ create_table :marty_notifications do |t|
4
+ t.pg_enum :event_type, enum: :marty_notifications_event_types, null: false
5
+ t.string :state, null: false
6
+ t.text :text, null: false
7
+
8
+ t.timestamps null: false
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ class CreateMartyNotificationsDeliveries < ActiveRecord::Migration[4.2]
2
+ def change
3
+ create_table :marty_notifications_deliveries do |t|
4
+ t.integer :notification_id, null: false
5
+ t.integer :recipient_id
6
+ t.string :delivery_type, null: false
7
+ t.string :state, null: false
8
+ t.text :text, null: false, default: ''
9
+ t.string :error_text, null: false, default: ''
10
+
11
+ t.timestamps null: false
12
+ end
13
+
14
+ add_foreign_key :marty_notifications_deliveries, :marty_notifications,
15
+ column: :notification_id, on_delete: :cascade
16
+ add_foreign_key :marty_notifications_deliveries, :marty_users,
17
+ column: :recipient_id, on_delete: :nullify
18
+
19
+ add_index :marty_notifications_deliveries, [:notification_id, :recipient_id, :delivery_type],
20
+ unique: true, name: :index_marty_notifications_deliveries_on_n_id_r_id_and_type
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ class CreateMartyNotificationsConfig < ActiveRecord::Migration[4.2]
2
+ def change
3
+ create_table :marty_notifications_configs do |t|
4
+ t.pg_enum :event_type, enum: :marty_notifications_event_types, null: false
5
+ t.integer :recipient_id
6
+ t.string :delivery_type, null: false
7
+ t.string :state, null: false
8
+ t.text :text, null: false, default: ''
9
+
10
+ t.timestamps null: false
11
+ end
12
+
13
+ add_foreign_key :marty_notifications_configs, :marty_users,
14
+ column: :recipient_id, on_delete: :nullify
15
+
16
+ add_index :marty_notifications_configs,
17
+ [:event_type, :recipient_id, :delivery_type],
18
+ unique: true,
19
+ name: :index_marty_notifications_configs_on_event_recipient_delivery
20
+ end
21
+ end
@@ -37,7 +37,7 @@ DECLARE
37
37
 
38
38
  result JSONB;
39
39
  return_json JSONB;
40
-
40
+
41
41
  error_extra JSONB;
42
42
 
43
43
  target RECORD;
@@ -58,7 +58,7 @@ BEGIN
58
58
  END IF;
59
59
  END LOOP;
60
60
 
61
- FOREACH direction IN ARRAY directions LOOP
61
+ FOREACH direction IN ARRAY directions LOOP
62
62
 
63
63
  IF direction = 'h' THEN
64
64
  data_grid_metadata_current := data_grid_metadata_h;
@@ -89,7 +89,7 @@ BEGIN
89
89
 
90
90
  query_index_result := empty_jsonb_array;
91
91
 
92
- -- execute the SQL query that has been received before and
92
+ -- execute the SQL query that has been received before and
93
93
  -- add it's (possibly multiplt) results to query_index_result variable
94
94
  FOR target IN EXECUTE query_dir_result ->> 0 USING query_dir_result -> 1 LOOP
95
95
  query_index_result := query_index_result || to_jsonb(target.index);
@@ -105,7 +105,7 @@ BEGIN
105
105
  END IF;
106
106
 
107
107
 
108
- IF dis AND jsonb_array_length(query_index_result) > 1 THEN
108
+ IF dis AND jsonb_array_length(query_index_result) > 1 THEN
109
109
  RAISE EXCEPTION 'matches > 1';
110
110
  END IF;
111
111
 
@@ -114,7 +114,7 @@ BEGIN
114
114
  vertical_indexes := COALESCE(vertical_indexes, empty_jsonb_array);
115
115
  horizontal_indexes := COALESCE(horizontal_indexes, empty_jsonb_array);
116
116
 
117
- IF ((jsonb_array_length(vertical_indexes)) = 0
117
+ IF ((jsonb_array_length(vertical_indexes)) = 0
118
118
  OR (jsonb_array_length(horizontal_indexes)) = 0)
119
119
  AND NOT data_grid_lenient
120
120
  AND NOT return_grid_data THEN
@@ -140,7 +140,7 @@ BEGIN
140
140
  result := data_grid_data -> vertical_index -> horizontal_index;
141
141
  END IF;
142
142
 
143
- IF NOT return_grid_data THEN
143
+ IF NOT return_grid_data THEN
144
144
  data_grid_data := NULL;
145
145
  data_grid_metadata := NULL;
146
146
  END IF;
@@ -7,6 +7,7 @@
7
7
 
8
8
  # Also note that anything required here will need to require in any
9
9
  # classes that they might be overriding methods in
10
+ require 'action_cable/engine'
10
11
 
11
12
  require 'marty/engine'
12
13
  require 'marty/railtie'
@@ -22,6 +23,7 @@ require 'marty/rails_app'
22
23
  # to the Gemfile
23
24
  require 'net-ldap'
24
25
  require 'delayed_cron_job'
26
+ require 'state_machines-activerecord'
25
27
 
26
28
  require 'pathname'
27
29
 
@@ -1,30 +1,40 @@
1
- module Marty::Diagnostic; class Version < Base
2
- diagnostic_fn do
3
- begin
4
- message = `cd #{Rails.root}; git describe --tags --always;`.strip
5
- rescue StandardError
6
- message = error('Failed accessing git')
1
+ module Marty::Diagnostic
2
+ class Version < Base
3
+ diagnostic_fn do
4
+ begin
5
+ submodules = `cd #{Rails.root}; git submodule`.split("\n").map do |s|
6
+ sha, path, tag = s.split
7
+ name = File.basename(path)
8
+ {
9
+ "#{name}_sha".titleize => sha[0..7],
10
+ "#{name}_tag".titleize => tag,
11
+ }
12
+ end.reduce(&:merge) || {}
13
+
14
+ git_tag = `cd #{Rails.root}; git describe --tags --always;`.strip
15
+ git = { 'Root Git' => git_tag }.merge(submodules)
16
+ rescue StandardError
17
+ git = { 'Root Git' => error('Failed accessing git') }
18
+ end
19
+ rbv = "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})"
20
+ {
21
+ 'Marty' => Marty::VERSION,
22
+ 'Delorean' => Delorean::VERSION,
23
+ 'Mcfly' => Mcfly::VERSION,
24
+ 'Rails' => Rails.version,
25
+ 'Netzke Core' => Netzke::Core::VERSION,
26
+ 'Netzke Basepack' => Netzke::Basepack::VERSION,
27
+ 'Ruby' => rbv,
28
+ 'RubyGems' => Gem::VERSION,
29
+ 'Database Schema Version' => db_schema,
30
+ 'Environment' => Rails.env,
31
+ }.merge(git)
7
32
  end
8
- rbv = "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL} (#{RUBY_PLATFORM})"
9
- {
10
- 'Marty' => Marty::VERSION,
11
- 'Delorean' => Delorean::VERSION,
12
- 'Mcfly' => Mcfly::VERSION,
13
- 'Git' => message,
14
- 'Rails' => Rails.version,
15
- 'Netzke Core' => Netzke::Core::VERSION,
16
- 'Netzke Basepack' => Netzke::Basepack::VERSION,
17
- 'Ruby' => rbv,
18
- 'RubyGems' => Gem::VERSION,
19
- 'Database Schema Version' => db_schema,
20
- 'Environment' => Rails.env,
21
- }
22
- end
23
33
 
24
- def self.db_schema
25
- Database.db_schema
26
- rescue StandardError => e
27
- error(e.message)
34
+ def self.db_schema
35
+ Database.db_schema
36
+ rescue StandardError => e
37
+ error(e.message)
38
+ end
28
39
  end
29
40
  end
30
- end