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.
- checksums.yaml +4 -4
- data/.gitlab-ci.yml +7 -0
- data/app/assets/javascripts/marty/cable.js +12 -0
- data/app/assets/stylesheets/marty/codemirror/notifications.css +4 -0
- data/app/channels/application_cable/channel.rb +4 -0
- data/app/channels/application_cable/connection.rb +17 -0
- data/app/channels/marty/notification_channel.rb +8 -0
- data/app/components/marty/auth_app.rb +54 -6
- data/app/components/marty/auth_app/client/auth_app.js +53 -0
- data/app/components/marty/main_auth_app.rb +47 -1
- data/app/components/marty/notifications/config_view.rb +56 -0
- data/app/components/marty/notifications/deliveries_view.rb +50 -0
- data/app/components/marty/notifications/grid_view.rb +56 -0
- data/app/components/marty/notifications/window.rb +23 -0
- data/app/components/marty/promise_view.rb +13 -1
- data/app/components/marty/promise_view/client/promise_view.js +18 -0
- data/app/components/marty/{schedule_jobs_dashboard/client/schedule_jobs_dashboard.js → schedule_jobs_grid/client/schedule_jobs_grid.js} +0 -0
- data/app/components/marty/simple_app/client/simple_app.js +5 -1
- data/app/components/marty/users/user_view.rb +177 -0
- data/app/controllers/marty/application_controller.rb +13 -0
- data/app/models/marty/data_grid.rb +1 -1
- data/app/models/marty/notifications.rb +4 -0
- data/app/models/marty/notifications/config.rb +25 -0
- data/app/models/marty/notifications/delivery.rb +56 -0
- data/app/models/marty/notifications/event_type.rb +11 -0
- data/app/models/marty/notifications/notification.rb +25 -0
- data/app/models/marty/promise.rb +1 -0
- data/app/models/marty/user.rb +14 -0
- data/app/services/marty/notifications/create.rb +39 -0
- data/app/services/marty/notifications/create_deliveries.rb +25 -0
- data/app/services/marty/notifications/process_delivery.rb +11 -0
- data/app/services/marty/notifications/processors/email.rb +13 -0
- data/app/services/marty/notifications/processors/sms.rb +13 -0
- data/app/services/marty/notifications/processors/web.rb +27 -0
- data/app/services/marty/promises/cancel.rb +37 -0
- data/app/services/marty/promises/delorean/create.rb +4 -0
- data/app/services/marty/promises/ruby/create.rb +4 -1
- data/app/views/marty/diagnostic/op.html.erb +4 -0
- data/db/migrate/514_remove_marty_events.rb +1 -1
- data/db/migrate/519_create_marty_notifications_event_types.rb +16 -0
- data/db/migrate/520_create_marty_notifications.rb +11 -0
- data/db/migrate/521_create_marty_notifications_deliveries.rb +22 -0
- data/db/migrate/522_create_marty_notifications_config.rb +21 -0
- data/db/sql/lookup_grid_distinct_v1.sql +6 -6
- data/lib/marty.rb +2 -0
- data/lib/marty/diagnostic/version.rb +36 -26
- data/lib/marty/engine.rb +7 -1
- data/lib/marty/mcfly_model.rb +27 -6
- data/lib/marty/railtie.rb +1 -0
- data/lib/marty/version.rb +1 -1
- data/marty.gemspec +3 -0
- data/spec/controllers/diagnostic/controller_spec.rb +1 -1
- data/spec/dummy/app/models/gemini/fannie_bup.rb +27 -13
- data/spec/dummy/app/models/gemini/helper.rb +62 -0
- data/spec/features/notifications_spec.rb +224 -0
- data/spec/job_helper.rb +14 -1
- data/spec/lib/mcfly_model_spec.rb +14 -7
- data/spec/models/promise_spec.rb +121 -0
- data/spec/services/notifications/create_spec.rb +82 -0
- metadata +73 -3
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'marty/notifications/grid_view'
|
2
|
+
|
3
|
+
module Marty
|
4
|
+
module Notifications
|
5
|
+
class Window < Netzke::Window::Base
|
6
|
+
def configure(c)
|
7
|
+
super
|
8
|
+
|
9
|
+
c.title = 'Notifications'
|
10
|
+
c.modal = true
|
11
|
+
c.items = [:grid_view]
|
12
|
+
c.lazy_loading = true
|
13
|
+
c.width = 800
|
14
|
+
c.height = 550
|
15
|
+
end
|
16
|
+
|
17
|
+
component :grid_view do |c|
|
18
|
+
c.klass = Marty::Notifications::GridView
|
19
|
+
c.rows_per_page = 30
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -50,7 +50,7 @@ class Marty::PromiseView < Netzke::Tree::Base
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def bbar
|
53
|
-
[:clear, '->', :refresh, :download]
|
53
|
+
[:clear, :cancel_job, '->', :refresh, :download]
|
54
54
|
end
|
55
55
|
|
56
56
|
action :clear do |a|
|
@@ -60,6 +60,13 @@ class Marty::PromiseView < Netzke::Tree::Base
|
|
60
60
|
a.hidden = !self.class.has_perm?(:admin)
|
61
61
|
end
|
62
62
|
|
63
|
+
action :cancel_job do |a|
|
64
|
+
a.text = a.tooltip = 'Cancel Job'
|
65
|
+
a.disabled = false
|
66
|
+
a.icon_cls = 'fa fa-minus glyph'
|
67
|
+
a.hidden = !self.class.has_perm?(:admin)
|
68
|
+
end
|
69
|
+
|
63
70
|
action :download do |a|
|
64
71
|
a.text = a.tooltip = 'Download'
|
65
72
|
a.disabled = true
|
@@ -77,6 +84,11 @@ class Marty::PromiseView < Netzke::Tree::Base
|
|
77
84
|
client.netzke_on_refresh
|
78
85
|
end
|
79
86
|
|
87
|
+
endpoint :cancel_job do |id|
|
88
|
+
Marty::Promises::Cancel.call(id)
|
89
|
+
client.netzke_on_refresh
|
90
|
+
end
|
91
|
+
|
80
92
|
def get_records params
|
81
93
|
search_scope = config[:live_search_scope] || :live_search
|
82
94
|
Marty::VwPromise.children_for_id(params[:id], params[search_scope])
|
@@ -64,5 +64,23 @@
|
|
64
64
|
// extra request for every node expand event.
|
65
65
|
netzkeOnNodeStateChange: function() {
|
66
66
|
return;
|
67
|
+
},
|
68
|
+
|
69
|
+
netzkeOnCancelJob: function (params) {
|
70
|
+
var me = this;
|
71
|
+
var sel = this.getSelectionModel().getSelection();
|
72
|
+
if (sel.length != 1) {
|
73
|
+
return this.netzkeNotify('select one job to cancel')
|
74
|
+
}
|
75
|
+
Ext.Msg.show({
|
76
|
+
title: 'Cancel this Job?',
|
77
|
+
msg: 'Enter CANCEL and press OK to cancel the job',
|
78
|
+
width: 375,
|
79
|
+
buttons: Ext.Msg.OKCANCEL,
|
80
|
+
prompt: true,
|
81
|
+
fn: function (btn, value) {
|
82
|
+
(btn == "ok" && value == "CANCEL") && me.server.cancelJob(sel[0].getId());
|
83
|
+
}
|
84
|
+
});
|
67
85
|
}
|
68
86
|
}
|
File without changes
|
@@ -18,6 +18,7 @@
|
|
18
18
|
});
|
19
19
|
|
20
20
|
this.setRouting();
|
21
|
+
this.netzkeInitComponentCallback();
|
21
22
|
},
|
22
23
|
|
23
24
|
setRouting: function () {
|
@@ -54,5 +55,8 @@
|
|
54
55
|
|
55
56
|
onToggleConfigMode: function (params) {
|
56
57
|
this.toggleConfigMode();
|
57
|
-
}
|
58
|
+
},
|
59
|
+
|
60
|
+
netzkeInitComponentCallback: function() {
|
61
|
+
},
|
58
62
|
}
|
@@ -0,0 +1,177 @@
|
|
1
|
+
module Marty
|
2
|
+
class Users
|
3
|
+
class UserView < Marty::Grid
|
4
|
+
has_marty_permissions create: [:admin, :user_manager],
|
5
|
+
read: :any,
|
6
|
+
update: [:admin, :user_manager],
|
7
|
+
delete: [:admin, :user_manager]
|
8
|
+
|
9
|
+
# list of columns to be displayed in the grid view
|
10
|
+
def self.user_columns
|
11
|
+
[
|
12
|
+
:login,
|
13
|
+
:firstname,
|
14
|
+
:lastname,
|
15
|
+
:active,
|
16
|
+
:user_roles,
|
17
|
+
]
|
18
|
+
end
|
19
|
+
|
20
|
+
def configure(c)
|
21
|
+
super
|
22
|
+
|
23
|
+
c.attributes ||= self.class.user_columns
|
24
|
+
c.title ||= I18n.t('users', default: 'Users')
|
25
|
+
c.model = 'Marty::User'
|
26
|
+
c.editing = :in_form
|
27
|
+
c.paging = :pagination
|
28
|
+
c.multi_select = false
|
29
|
+
if c.attributes.include?(:login)
|
30
|
+
c.store_config[:sorters] = [{ property: :login,
|
31
|
+
direction: 'ASC', }]
|
32
|
+
end
|
33
|
+
c.scope = ->(arel) { arel.includes(:user_roles) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.set_roles(roles, user)
|
37
|
+
roles = [] unless roles.present?
|
38
|
+
|
39
|
+
roles = ::Marty::RoleType.from_nice_names(roles)
|
40
|
+
|
41
|
+
roles_in_user = user.user_roles.map(&:role)
|
42
|
+
roles_to_delete = roles_in_user - roles
|
43
|
+
roles_to_add = roles - roles_in_user
|
44
|
+
|
45
|
+
Marty::User.transaction do
|
46
|
+
user.user_roles.where(role: roles_to_delete).map(&:destroy!)
|
47
|
+
|
48
|
+
roles_to_add.each do |role|
|
49
|
+
user.user_roles.create!(role: role)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.create_edit_user(data)
|
55
|
+
# Creates initial place-holder user object and validate
|
56
|
+
user = data['id'].nil? ? User.new : User.find(data['id'])
|
57
|
+
|
58
|
+
user_columns.each do |c|
|
59
|
+
user.send("#{c}=", data[c.to_s]) unless c == :user_roles
|
60
|
+
end
|
61
|
+
|
62
|
+
if user.valid?
|
63
|
+
user.save
|
64
|
+
set_roles(data['user_roles'], user)
|
65
|
+
end
|
66
|
+
|
67
|
+
user
|
68
|
+
end
|
69
|
+
|
70
|
+
# override the add_in_form and edit_in_form endpoint. User creation/update
|
71
|
+
# needs to use the create_edit_user method.
|
72
|
+
|
73
|
+
endpoint :add_window__add_form__submit do |params|
|
74
|
+
data = ActiveSupport::JSON.decode(params[:data])
|
75
|
+
|
76
|
+
data['id'] = nil
|
77
|
+
|
78
|
+
unless self.class.can_perform_action?(:create)
|
79
|
+
client.netzke_notify 'Permission Denied'
|
80
|
+
return
|
81
|
+
end
|
82
|
+
|
83
|
+
user = self.class.create_edit_user(data)
|
84
|
+
if user.valid?
|
85
|
+
client.success = true
|
86
|
+
client.netzke_on_submit_success
|
87
|
+
else
|
88
|
+
client.netzke_notify(model_adapter.errors_array(user).join("\n"))
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
endpoint :edit_window__edit_form__submit do |params|
|
93
|
+
data = ActiveSupport::JSON.decode(params[:data])
|
94
|
+
unless self.class.can_perform_action?(:update)
|
95
|
+
client.netzke_notify 'Permission Denied'
|
96
|
+
return
|
97
|
+
end
|
98
|
+
|
99
|
+
user = self.class.create_edit_user(data)
|
100
|
+
if user.valid?
|
101
|
+
client.success = true
|
102
|
+
client.netzke_on_submit_success
|
103
|
+
else
|
104
|
+
client.netzke_notify(model_adapter.errors_array(user).join("\n"))
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
action :add do |a|
|
109
|
+
super(a)
|
110
|
+
a.text = I18n.t('user_grid.new')
|
111
|
+
a.tooltip = I18n.t('user_grid.new')
|
112
|
+
a.icon_cls = 'fa fa-user-plus glyph'
|
113
|
+
end
|
114
|
+
|
115
|
+
action :edit do |a|
|
116
|
+
super(a)
|
117
|
+
a.icon_cls = 'fa fa-user-cog glyph'
|
118
|
+
end
|
119
|
+
|
120
|
+
action :delete do |a|
|
121
|
+
super(a)
|
122
|
+
a.icon_cls = 'fa fa-user-minus glyph'
|
123
|
+
end
|
124
|
+
|
125
|
+
def default_context_menu
|
126
|
+
[]
|
127
|
+
end
|
128
|
+
|
129
|
+
attribute :login do |c|
|
130
|
+
c.width = 100
|
131
|
+
c.label = I18n.t('user_grid.login')
|
132
|
+
end
|
133
|
+
|
134
|
+
attribute :firstname do |c|
|
135
|
+
c.width = 100
|
136
|
+
c.label = I18n.t('user_grid.firstname')
|
137
|
+
end
|
138
|
+
|
139
|
+
attribute :lastname do |c|
|
140
|
+
c.width = 100
|
141
|
+
c.label = I18n.t('user_grid.lastname')
|
142
|
+
end
|
143
|
+
|
144
|
+
attribute :active do |c|
|
145
|
+
c.width = 60
|
146
|
+
c.label = I18n.t('user_grid.active')
|
147
|
+
end
|
148
|
+
|
149
|
+
attribute :user_roles do |c|
|
150
|
+
c.width = 100
|
151
|
+
c.flex = 1
|
152
|
+
c.label = I18n.t('user_grid.roles')
|
153
|
+
c.type = :string
|
154
|
+
|
155
|
+
c.getter = lambda do |r|
|
156
|
+
Marty::RoleType.to_nice_names(r.user_roles.map(&:role))
|
157
|
+
end
|
158
|
+
|
159
|
+
store = ::Marty::RoleType.to_nice_names(::Marty::RoleType::VALUES.sort)
|
160
|
+
|
161
|
+
c.editor_config = {
|
162
|
+
multi_select: true,
|
163
|
+
empty_text: I18n.t('user_grid.select_roles'),
|
164
|
+
store: store,
|
165
|
+
type: :string,
|
166
|
+
xtype: :combo,
|
167
|
+
}
|
168
|
+
end
|
169
|
+
|
170
|
+
attribute :created_dt do |c|
|
171
|
+
c.label = I18n.t('user_grid.created_dt')
|
172
|
+
c.format = 'Y-m-d H:i'
|
173
|
+
c.read_only = true
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -22,6 +22,7 @@ class Marty::ApplicationController < ActionController::Base
|
|
22
22
|
if session[:user_id]
|
23
23
|
if session_expired? && !try_to_autologin
|
24
24
|
reset_session
|
25
|
+
reset_signed_cookies
|
25
26
|
else
|
26
27
|
session[:atime] = Time.now.utc.to_i
|
27
28
|
end
|
@@ -50,6 +51,8 @@ class Marty::ApplicationController < ActionController::Base
|
|
50
51
|
session[:user_id] = user.id
|
51
52
|
session[:ctime] = Time.now.utc.to_i
|
52
53
|
session[:atime] = Time.now.utc.to_i
|
54
|
+
|
55
|
+
set_signed_cookies
|
53
56
|
end
|
54
57
|
|
55
58
|
def user_setup
|
@@ -78,6 +81,7 @@ class Marty::ApplicationController < ActionController::Base
|
|
78
81
|
user = Marty::User.try_to_autologin(cookies[:autologin])
|
79
82
|
if user
|
80
83
|
reset_session
|
84
|
+
reset_signed_cookies
|
81
85
|
start_user_session(user)
|
82
86
|
end
|
83
87
|
user
|
@@ -87,6 +91,7 @@ class Marty::ApplicationController < ActionController::Base
|
|
87
91
|
# Sets the logged in user
|
88
92
|
def set_user(user)
|
89
93
|
reset_session
|
94
|
+
reset_signed_cookies
|
90
95
|
if user && user.is_a?(Marty::User)
|
91
96
|
Marty::User.current = user
|
92
97
|
start_user_session(user)
|
@@ -123,6 +128,14 @@ class Marty::ApplicationController < ActionController::Base
|
|
123
128
|
set_user(user)
|
124
129
|
end
|
125
130
|
|
131
|
+
def set_signed_cookies
|
132
|
+
cookies.signed[:user_id] = session[:user_id]
|
133
|
+
end
|
134
|
+
|
135
|
+
def reset_signed_cookies
|
136
|
+
cookies.signed[:user_id] = nil
|
137
|
+
end
|
138
|
+
|
126
139
|
def toggle_dark_mode
|
127
140
|
cookies[:dark_mode] = cookies[:dark_mode] != 'true'
|
128
141
|
end
|
@@ -249,7 +249,7 @@ class Marty::DataGrid < Marty::Base
|
|
249
249
|
|
250
250
|
# private method used to cache lookup_grid_distinct_entry_h result
|
251
251
|
delorean_fn :lookup_grid_h_priv,
|
252
|
-
private: true, cache: true, sig: 4 do |pt, dgh, h, distinct|
|
252
|
+
to_hash: false, private: true, cache: true, sig: 4 do |pt, dgh, h, distinct|
|
253
253
|
lookup_grid_distinct_entry_h(
|
254
254
|
pt, h, dgh, nil, true, false, distinct)['result']
|
255
255
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Marty
|
2
|
+
module Notifications
|
3
|
+
class Config < Marty::Base
|
4
|
+
self.table_name = 'marty_notifications_configs'
|
5
|
+
|
6
|
+
AVAILABLE_TYPES = ::Marty::Notifications::Delivery.
|
7
|
+
state_machines[:delivery_type].states.map(&:value)
|
8
|
+
|
9
|
+
belongs_to :recipient, class_name: '::Marty::User'
|
10
|
+
|
11
|
+
validates :recipient, presence: true
|
12
|
+
|
13
|
+
validates :delivery_type, presence: true, inclusion: { in: AVAILABLE_TYPES }
|
14
|
+
|
15
|
+
validates :delivery_type,
|
16
|
+
presence: true,
|
17
|
+
uniqueness: { scope: [:event_type, :recipient_id] }
|
18
|
+
|
19
|
+
state_machine :state, initial: :on do
|
20
|
+
state :off
|
21
|
+
state :on
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Marty
|
2
|
+
module Notifications
|
3
|
+
class Delivery < Marty::Base
|
4
|
+
self.table_name = 'marty_notifications_deliveries'
|
5
|
+
|
6
|
+
belongs_to :notification, class_name: '::Marty::Notifications::Notification'
|
7
|
+
belongs_to :recipient, class_name: '::Marty::User'
|
8
|
+
|
9
|
+
validates :state, presence: true
|
10
|
+
|
11
|
+
# One delivery per type per notification for user
|
12
|
+
validates :delivery_type,
|
13
|
+
presence: true,
|
14
|
+
uniqueness: { scope: [:notification_id, :recipient_id] }
|
15
|
+
|
16
|
+
state_machine :delivery_type, initial: :web do
|
17
|
+
state :web do
|
18
|
+
def processor
|
19
|
+
Marty::Notifications::Processors::Web
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# state :email do
|
24
|
+
# def processor
|
25
|
+
# Marty::Notifications::Processors::Email
|
26
|
+
# end
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# state :sms do
|
30
|
+
# def processor
|
31
|
+
# Marty::Notifications::Processors::Sms
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
end
|
35
|
+
|
36
|
+
state_machine :state, initial: :pending do
|
37
|
+
state :pending
|
38
|
+
state :sent
|
39
|
+
state :delivered
|
40
|
+
state :failed
|
41
|
+
|
42
|
+
event :set_sent do
|
43
|
+
transition [:pending, :failed] => :sent, sent: same
|
44
|
+
end
|
45
|
+
|
46
|
+
event :set_failed do
|
47
|
+
transition [:pending, :sent] => :failed, failed: same
|
48
|
+
end
|
49
|
+
|
50
|
+
event :set_delivered do
|
51
|
+
transition all => :delivered
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Marty
|
2
|
+
module Notifications
|
3
|
+
class Notification < Marty::Base
|
4
|
+
self.table_name = 'marty_notifications'
|
5
|
+
|
6
|
+
validates :state, :event_type, presence: true
|
7
|
+
|
8
|
+
has_many(
|
9
|
+
:deliveries,
|
10
|
+
class_name: '::Marty::Notifications::Delivery',
|
11
|
+
dependent: :destroy,
|
12
|
+
foreign_key: :notification_id
|
13
|
+
)
|
14
|
+
|
15
|
+
state_machine :state, initial: :pending do
|
16
|
+
state :pending
|
17
|
+
state :processed
|
18
|
+
|
19
|
+
event :set_processed do
|
20
|
+
transition processed: same, pending: :processed
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|