marty 8.0.0 → 8.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|