motor-admin 0.1.35 → 0.1.41
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/app/controllers/concerns/motor/load_and_authorize_dynamic_resource.rb +70 -0
- data/app/controllers/motor/alerts_controller.rb +12 -6
- data/app/controllers/motor/audits_controller.rb +16 -0
- data/app/controllers/motor/configs_controller.rb +1 -0
- data/app/controllers/motor/dashboards_controller.rb +4 -0
- data/app/controllers/motor/data_controller.rb +2 -57
- data/app/controllers/motor/forms_controller.rb +4 -0
- data/app/controllers/motor/queries_controller.rb +4 -0
- data/app/controllers/motor/resources_controller.rb +1 -0
- data/app/controllers/motor/ui_controller.rb +4 -0
- data/app/models/motor/alert.rb +4 -2
- data/app/models/motor/application_record.rb +10 -0
- data/app/models/motor/audit.rb +9 -0
- data/app/models/motor/config.rb +2 -0
- data/app/models/motor/dashboard.rb +3 -1
- data/app/models/motor/form.rb +3 -1
- data/app/models/motor/query.rb +4 -1
- data/app/models/motor/resource.rb +2 -0
- data/app/views/motor/ui/show.html.erb +1 -1
- data/config/routes.rb +1 -0
- data/lib/generators/motor/templates/install.rb +40 -16
- data/lib/motor.rb +12 -3
- data/lib/motor/active_record_utils/defined_scopes_extension.rb +1 -1
- data/lib/motor/admin.rb +8 -0
- data/lib/motor/alerts/persistance.rb +17 -3
- data/lib/motor/alerts/scheduler.rb +1 -1
- data/lib/motor/api_query/apply_scope.rb +12 -5
- data/lib/motor/api_query/sort.rb +1 -1
- data/lib/motor/build_schema.rb +3 -3
- data/lib/motor/build_schema/find_display_column.rb +4 -4
- data/lib/motor/build_schema/load_from_rails.rb +20 -10
- data/lib/motor/build_schema/merge_schema_configs.rb +8 -4
- data/lib/motor/build_schema/reorder_schema.rb +10 -4
- data/lib/motor/configs.rb +17 -0
- data/lib/motor/configs/build_configs_hash.rb +83 -0
- data/lib/motor/configs/build_ui_app_tag.rb +71 -0
- data/lib/motor/configs/load_from_cache.rb +81 -0
- data/lib/motor/configs/sync_from_file.rb +36 -0
- data/lib/motor/configs/sync_from_hash.rb +126 -0
- data/lib/motor/configs/sync_middleware.rb +72 -0
- data/lib/motor/configs/sync_with_remote.rb +47 -0
- data/lib/motor/configs/write_to_file.rb +36 -0
- data/lib/motor/dashboards/persistance.rb +15 -5
- data/lib/motor/forms/persistance.rb +15 -5
- data/lib/motor/net_http_utils.rb +38 -0
- data/lib/motor/queries/persistance.rb +13 -3
- data/lib/motor/railtie.rb +11 -0
- data/lib/motor/tasks/motor.rake +37 -0
- data/lib/motor/version.rb +1 -1
- data/ui/dist/{main-03c3b1d3390877206e02.css.gz → main-0ef3be65da8d3b0dbabb.css.gz} +0 -0
- data/ui/dist/main-0ef3be65da8d3b0dbabb.js.gz +0 -0
- data/ui/dist/manifest.json +5 -5
- metadata +33 -5
- data/lib/motor/ui_configs.rb +0 -82
- data/ui/dist/main-03c3b1d3390877206e02.js.gz +0 -0
@@ -13,9 +13,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
|
|
13
13
|
|
14
14
|
t.index :updated_at
|
15
15
|
t.index 'lower(name)',
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
name: 'motor_queries_lower_name_unique_index',
|
17
|
+
unique: true,
|
18
|
+
where: 'deleted_at IS NULL'
|
19
19
|
end
|
20
20
|
|
21
21
|
create_table :motor_dashboards, force: true do |t|
|
@@ -30,9 +30,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
|
|
30
30
|
|
31
31
|
t.index :updated_at
|
32
32
|
t.index 'lower(title)',
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
name: 'motor_dashboards_lower_title_unique_index',
|
34
|
+
unique: true,
|
35
|
+
where: 'deleted_at IS NULL'
|
36
36
|
end
|
37
37
|
|
38
38
|
create_table :motor_forms, force: true do |t|
|
@@ -49,9 +49,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
|
|
49
49
|
|
50
50
|
t.index :updated_at
|
51
51
|
t.index 'lower(name)',
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
name: 'motor_forms_lower_name_unique_index',
|
53
|
+
unique: true,
|
54
|
+
where: 'deleted_at IS NULL'
|
55
55
|
end
|
56
56
|
|
57
57
|
create_table :motor_resources, force: true do |t|
|
@@ -87,9 +87,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
|
|
87
87
|
|
88
88
|
t.index :updated_at
|
89
89
|
t.index 'lower(name)',
|
90
|
-
|
91
|
-
|
92
|
-
|
90
|
+
name: 'motor_alerts_lower_name_unique_index',
|
91
|
+
unique: true,
|
92
|
+
where: 'deleted_at IS NULL'
|
93
93
|
end
|
94
94
|
|
95
95
|
create_table :motor_alert_locks, force: true do |t|
|
@@ -107,8 +107,8 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
|
|
107
107
|
t.timestamps
|
108
108
|
|
109
109
|
t.index 'lower(name)',
|
110
|
-
|
111
|
-
|
110
|
+
name: 'motor_tags_lower_name_unique_index',
|
111
|
+
unique: true
|
112
112
|
end
|
113
113
|
|
114
114
|
create_table :motor_taggable_tags, force: true do |t|
|
@@ -117,12 +117,36 @@ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Mi
|
|
117
117
|
t.column :taggable_type, :string, null: false
|
118
118
|
|
119
119
|
t.index %i[taggable_id taggable_type tag_id],
|
120
|
-
|
121
|
-
|
120
|
+
name: 'motor_polymorphic_association_tag_index',
|
121
|
+
unique: true
|
122
122
|
end
|
123
|
+
|
124
|
+
create_table :motor_audits, force: true do |t|
|
125
|
+
t.column :auditable_id, :integer
|
126
|
+
t.column :auditable_type, :string
|
127
|
+
t.column :associated_id, :integer
|
128
|
+
t.column :associated_type, :string
|
129
|
+
t.column :user_id, :integer
|
130
|
+
t.column :user_type, :string
|
131
|
+
t.column :username, :string
|
132
|
+
t.column :action, :string
|
133
|
+
t.column :audited_changes, :text
|
134
|
+
t.column :version, :integer, default: 0
|
135
|
+
t.column :comment, :string
|
136
|
+
t.column :remote_address, :string
|
137
|
+
t.column :request_uuid, :string
|
138
|
+
t.column :created_at, :datetime
|
139
|
+
end
|
140
|
+
|
141
|
+
add_index :motor_audits, %i[auditable_type auditable_id version], name: 'motor_auditable_index'
|
142
|
+
add_index :motor_audits, %i[associated_type associated_id], name: 'motor_auditable_associated_index'
|
143
|
+
add_index :motor_audits, %i[user_id user_type], name: 'motor_auditable_user_index'
|
144
|
+
add_index :motor_audits, :request_uuid
|
145
|
+
add_index :motor_audits, :created_at
|
123
146
|
end
|
124
147
|
|
125
148
|
def self.down
|
149
|
+
drop_table :motor_audits
|
126
150
|
drop_table :motor_alert_locks
|
127
151
|
drop_table :motor_alerts
|
128
152
|
drop_table :motor_taggable_tags
|
data/lib/motor.rb
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'concurrent/executor/fixed_thread_pool'
|
4
|
+
require 'concurrent/timer_task'
|
3
5
|
require 'cancancan'
|
4
6
|
require 'ar_lazy_preload'
|
5
7
|
require 'js_regex'
|
6
8
|
require 'fugit'
|
7
9
|
require 'csv'
|
8
10
|
require 'active_record/filter'
|
11
|
+
require 'audited'
|
12
|
+
require 'uri'
|
13
|
+
require 'net/http'
|
14
|
+
require 'net/https'
|
9
15
|
|
10
16
|
module Motor
|
11
17
|
PATH = Pathname.new(__dir__)
|
@@ -17,7 +23,8 @@ module Motor
|
|
17
23
|
Dir[PATH.join('./motor/**/*.rb')].each do |f|
|
18
24
|
next if f.ends_with?('alerts/scheduler.rb')
|
19
25
|
next if f.ends_with?('alerts/scheduled_alerts_cache.rb')
|
20
|
-
next if f.ends_with?('
|
26
|
+
next if f.ends_with?('configs/load_from_cache.rb')
|
27
|
+
next if f.ends_with?('configs/sync_from_file.rb')
|
21
28
|
|
22
29
|
load f
|
23
30
|
end
|
@@ -46,13 +53,15 @@ end
|
|
46
53
|
require 'motor/version'
|
47
54
|
require 'motor/admin'
|
48
55
|
require 'motor/assets'
|
56
|
+
require 'motor/active_record_utils'
|
49
57
|
require 'motor/build_schema'
|
50
58
|
require 'motor/api_query'
|
51
59
|
require 'motor/tags'
|
52
|
-
require 'motor/
|
60
|
+
require 'motor/configs'
|
53
61
|
require 'motor/queries'
|
54
62
|
require 'motor/dashboards'
|
55
63
|
require 'motor/forms'
|
56
64
|
require 'motor/alerts'
|
57
65
|
require 'motor/hash_serializer'
|
58
|
-
require 'motor/
|
66
|
+
require 'motor/net_http_utils'
|
67
|
+
require 'motor/railtie'
|
data/lib/motor/admin.rb
CHANGED
@@ -28,6 +28,14 @@ module Motor
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
initializer 'motor.configs.sync_middleware' do
|
32
|
+
next if Motor::Configs::SYNC_ACCESS_KEY.blank?
|
33
|
+
|
34
|
+
require 'motor/configs/sync_middleware'
|
35
|
+
|
36
|
+
Rails.application.config.middleware.insert_after(Rails::Rack::Logger, Motor::Configs::SyncMiddleware)
|
37
|
+
end
|
38
|
+
|
31
39
|
initializer 'motor.filter_params' do
|
32
40
|
Rails.application.config.filter_parameters += %i[io]
|
33
41
|
end
|
@@ -43,30 +43,44 @@ module Motor
|
|
43
43
|
retry
|
44
44
|
end
|
45
45
|
|
46
|
-
def update_from_params!(alert, params)
|
46
|
+
def update_from_params!(alert, params, force_replace: false)
|
47
|
+
tag_ids = alert.tags.ids
|
48
|
+
|
47
49
|
alert = assign_attributes(alert, params)
|
48
50
|
|
49
|
-
raise NameAlreadyExists if name_already_exists?(alert)
|
51
|
+
raise NameAlreadyExists if !force_replace && name_already_exists?(alert)
|
50
52
|
raise InvalidInterval unless alert.cron
|
51
53
|
|
52
54
|
ApplicationRecord.transaction do
|
55
|
+
archive_with_existing_name(alert) if force_replace
|
56
|
+
|
53
57
|
alert.save!
|
54
58
|
end
|
55
59
|
|
56
|
-
alert.
|
60
|
+
alert.touch if tags_changed?(tag_ids, alert) && params[:updated_at].blank?
|
57
61
|
|
58
62
|
alert
|
59
63
|
rescue ActiveRecord::RecordNotUnique
|
60
64
|
retry
|
61
65
|
end
|
62
66
|
|
67
|
+
def tags_changed?(previous_ids, alert)
|
68
|
+
previous_ids.sort != alert.tags.reload.ids.sort
|
69
|
+
end
|
70
|
+
|
63
71
|
def assign_attributes(alert, params)
|
64
72
|
alert.assign_attributes(params.slice(*ALERT_ATTRIBUTES))
|
65
73
|
alert.preferences[:interval] = normalize_interval(alert.preferences[:interval])
|
74
|
+
alert.updated_at = [params[:updated_at], Time.current].min if params[:updated_at].present?
|
66
75
|
|
67
76
|
Motor::Tags.assign_tags(alert, params[:tags])
|
68
77
|
end
|
69
78
|
|
79
|
+
def archive_with_existing_name(alert)
|
80
|
+
Motor::Alert.where(['lower(name) = ? AND id != ?', alert.name.to_s.downcase, alert.id])
|
81
|
+
.update_all(deleted_at: Time.current)
|
82
|
+
end
|
83
|
+
|
70
84
|
def normalize_interval(interval)
|
71
85
|
interval.to_s.gsub(NORMALIZE_INTERVAL_REGEXP, 'every ')
|
72
86
|
end
|
@@ -13,13 +13,20 @@ module Motor
|
|
13
13
|
if rel.klass.defined_scopes.include?(scope_symbol)
|
14
14
|
rel.public_send(scope_symbol)
|
15
15
|
else
|
16
|
-
|
17
|
-
|
16
|
+
apply_filter_scope(rel, scope)
|
17
|
+
end
|
18
|
+
end
|
18
19
|
|
19
|
-
|
20
|
+
def apply_filter_scope(rel, scope)
|
21
|
+
configs = Motor::Resource.find_by_name(rel.klass.name.underscore)
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
+
return rel unless configs
|
24
|
+
|
25
|
+
scope_configs = configs.preferences[:scopes].find { |s| s[:name] == scope }
|
26
|
+
|
27
|
+
return rel unless scope_configs
|
28
|
+
|
29
|
+
ApiQuery::Filter.call(rel, scope_configs[:preferences][:filter])
|
23
30
|
end
|
24
31
|
end
|
25
32
|
end
|
data/lib/motor/api_query/sort.rb
CHANGED
@@ -13,7 +13,7 @@ module Motor
|
|
13
13
|
arel_order = build_arel_order(rel.klass, param)
|
14
14
|
join_params = build_join_params(rel.klass, param)
|
15
15
|
|
16
|
-
rel.
|
16
|
+
rel.reorder(arel_order).left_joins(join_params)
|
17
17
|
end
|
18
18
|
|
19
19
|
def build_join_params(_model, param)
|
data/lib/motor/build_schema.rb
CHANGED
@@ -58,11 +58,11 @@ module Motor
|
|
58
58
|
|
59
59
|
module_function
|
60
60
|
|
61
|
-
def call
|
61
|
+
def call(cache_keys = {})
|
62
62
|
schema = LoadFromRails.call
|
63
|
-
schema = MergeSchemaConfigs.call(schema)
|
63
|
+
schema = MergeSchemaConfigs.call(schema, cache_keys)
|
64
64
|
|
65
|
-
ReorderSchema.call(schema)
|
65
|
+
ReorderSchema.call(schema, cache_keys)
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
@@ -14,15 +14,15 @@ module Motor
|
|
14
14
|
fname
|
15
15
|
lname
|
16
16
|
sname
|
17
|
+
company
|
18
|
+
domain
|
19
|
+
title
|
17
20
|
phone
|
18
21
|
phone_number
|
19
22
|
email
|
20
|
-
domain
|
21
23
|
phone
|
22
|
-
company
|
23
24
|
filename
|
24
25
|
file_name
|
25
|
-
title
|
26
26
|
url
|
27
27
|
make
|
28
28
|
brand
|
@@ -42,7 +42,7 @@ module Motor
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def select_column_name(column_names)
|
45
|
-
name =
|
45
|
+
name = DISPLAY_NAMES.find { |column_name| column_name.in?(column_names) }
|
46
46
|
name ||= column_names.find { |column_name| column_name.match?(DISPLAY_NAME_REGEXP) }
|
47
47
|
|
48
48
|
name
|
@@ -4,6 +4,7 @@ module Motor
|
|
4
4
|
module BuildSchema
|
5
5
|
module LoadFromRails
|
6
6
|
MUTEX = Mutex.new
|
7
|
+
UNIFIED_TYPES = ActiveRecordUtils::Types::UNIFIED_TYPES
|
7
8
|
|
8
9
|
module_function
|
9
10
|
|
@@ -11,7 +12,7 @@ module Motor
|
|
11
12
|
models.map do |model|
|
12
13
|
build_model_schema(model)
|
13
14
|
rescue StandardError, NotImplementedError => e
|
14
|
-
Rails.logger.error(e)
|
15
|
+
Rails.logger.error(e) if model.name != 'Audited::Audit'
|
15
16
|
|
16
17
|
next
|
17
18
|
end.compact
|
@@ -20,10 +21,10 @@ module Motor
|
|
20
21
|
def models
|
21
22
|
eager_load_models!
|
22
23
|
|
23
|
-
models =
|
24
|
-
models = models.reject(&:abstract_class)
|
24
|
+
models = ActiveRecord::Base.descendants.reject(&:abstract_class)
|
25
25
|
|
26
26
|
models -= Motor::ApplicationRecord.descendants
|
27
|
+
models -= [Motor::Audit]
|
27
28
|
models -= [ActiveRecord::SchemaMigration] if defined?(ActiveRecord::SchemaMigration)
|
28
29
|
models -= [ActiveStorage::Blob] if defined?(ActiveStorage::Blob)
|
29
30
|
models -= [ActiveStorage::VariantRecord] if defined?(ActiveStorage::VariantRecord)
|
@@ -31,12 +32,6 @@ module Motor
|
|
31
32
|
models
|
32
33
|
end
|
33
34
|
|
34
|
-
def load_descendants(model)
|
35
|
-
model.descendants + model.descendants.flat_map do |klass|
|
36
|
-
load_descendants(klass)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
35
|
def build_model_schema(model)
|
41
36
|
model_name = model.name
|
42
37
|
|
@@ -91,10 +86,13 @@ module Motor
|
|
91
86
|
end
|
92
87
|
|
93
88
|
def build_table_column(column, model, default_attrs)
|
89
|
+
is_enum = model.defined_enums[column.name]
|
90
|
+
|
94
91
|
{
|
95
92
|
name: column.name,
|
96
93
|
display_name: column.name.humanize,
|
97
|
-
column_type:
|
94
|
+
column_type: is_enum ? 'string' : UNIFIED_TYPES[column.type.to_s] || column.type.to_s,
|
95
|
+
is_array: column.array?,
|
98
96
|
access_type: COLUMN_NAME_ACCESS_TYPES.fetch(column.name, ColumnAccessTypes::READ_WRITE),
|
99
97
|
default_value: default_attrs[column.name],
|
100
98
|
validators: fetch_validators(model, column.name),
|
@@ -180,6 +178,10 @@ module Motor
|
|
180
178
|
[]
|
181
179
|
end
|
182
180
|
|
181
|
+
enum = model.defined_enums[column_name]
|
182
|
+
|
183
|
+
validators += [{ includes: enum.keys }] if enum
|
184
|
+
|
183
185
|
validators += model.validators_on(column_name).map do |validator|
|
184
186
|
build_validator_hash(validator)
|
185
187
|
end.compact
|
@@ -209,6 +211,14 @@ module Motor
|
|
209
211
|
else
|
210
212
|
Rails.application.eager_load!
|
211
213
|
end
|
214
|
+
|
215
|
+
ActiveRecord::Base.descendants.each do |model|
|
216
|
+
model.reflections.each do |_, ref|
|
217
|
+
ref.klass
|
218
|
+
rescue StandardError
|
219
|
+
next
|
220
|
+
end
|
221
|
+
end
|
212
222
|
end
|
213
223
|
end
|
214
224
|
end
|
@@ -12,9 +12,10 @@ module Motor
|
|
12
12
|
module_function
|
13
13
|
|
14
14
|
# @param schema [Array<HashWithIndifferentAccess>]
|
15
|
+
# @param cache_keys [Hash]
|
15
16
|
# @return [Array<HashWithIndifferentAccess>]
|
16
|
-
def call(schema)
|
17
|
-
configs = load_configs
|
17
|
+
def call(schema, cache_keys = {})
|
18
|
+
configs = load_configs(cache_keys)
|
18
19
|
|
19
20
|
schema.map do |model|
|
20
21
|
merge_model(model, configs.fetch(model[:name], {}))
|
@@ -120,9 +121,12 @@ module Motor
|
|
120
121
|
end.compact
|
121
122
|
end
|
122
123
|
|
124
|
+
# @param cache_keys [Hash]
|
123
125
|
# @return [HashWithIndifferentAccess<String, HashWithIndifferentAccess>]
|
124
|
-
def load_configs
|
125
|
-
Motor::
|
126
|
+
def load_configs(cache_keys)
|
127
|
+
resources = Motor::Configs::LoadFromCache.load_resources(cache_key: cache_keys[:resources])
|
128
|
+
|
129
|
+
resources.each_with_object(HashWithIndifferentAccess.new) do |resource, acc|
|
126
130
|
acc[resource.name] = resource.preferences
|
127
131
|
end
|
128
132
|
end
|
@@ -16,10 +16,11 @@ module Motor
|
|
16
16
|
|
17
17
|
module_function
|
18
18
|
|
19
|
+
# @param cache_keys [Hash]
|
19
20
|
# @param schema [Array<HashWithIndifferentAccess>]
|
20
21
|
# @return [Array<HashWithIndifferentAccess>]
|
21
|
-
def call(schema)
|
22
|
-
configs = load_configs
|
22
|
+
def call(schema, cache_keys = {})
|
23
|
+
configs = load_configs(cache_keys)
|
23
24
|
|
24
25
|
schema = sort_by_name(schema, configs['resources.order'])
|
25
26
|
|
@@ -64,6 +65,8 @@ module Motor
|
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
68
|
+
# @param columns [Array<HashWithIndifferentAccess>]
|
69
|
+
# @return [Array<HashWithIndifferentAccess>]
|
67
70
|
def sort_columns(columns)
|
68
71
|
columns.each_with_object([]) do |column, acc|
|
69
72
|
weight = COLUMNS_DEFAULT_ORDER_WEIGHTS.fetch(column[:name], COLUMNS_DEFAULT_ORDER_WEIGHT)
|
@@ -72,9 +75,12 @@ module Motor
|
|
72
75
|
end.flatten.compact
|
73
76
|
end
|
74
77
|
|
78
|
+
# @param cache_keys [Hash]
|
75
79
|
# @return [Hash<String, HashWithIndifferentAccess>]
|
76
|
-
def load_configs
|
77
|
-
Motor::
|
80
|
+
def load_configs(cache_keys = {})
|
81
|
+
configs = Motor::Configs::LoadFromCache.load_configs(cache_key: cache_keys[:configs])
|
82
|
+
|
83
|
+
configs.each_with_object({}) do |config, acc|
|
78
84
|
acc[config.key] = config.value
|
79
85
|
end
|
80
86
|
end
|