motor-admin 0.1.28 → 0.1.34
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/wrap_io_params.rb +25 -0
- data/app/controllers/motor/active_storage_attachments_controller.rb +12 -5
- data/app/controllers/motor/data_controller.rb +18 -13
- data/app/models/motor/query.rb +8 -0
- data/app/views/motor/alerts_mailer/alert_email.html.erb +1 -1
- data/config/routes.rb +1 -12
- data/lib/generators/motor/install_generator.rb +2 -0
- data/lib/motor.rb +0 -1
- data/lib/motor/admin.rb +26 -0
- data/lib/motor/api_query/filter.rb +16 -7
- data/lib/motor/build_schema.rb +2 -2
- data/lib/motor/build_schema/load_from_rails.rb +26 -15
- data/lib/motor/build_schema/persist_resource_configs.rb +3 -3
- data/lib/motor/tags.rb +2 -1
- data/lib/motor/version.rb +1 -1
- data/ui/dist/{main-4da1a5102d7bc66aefd0.css.gz → main-052729fa924c6434623f.css.gz} +0 -0
- data/ui/dist/main-052729fa924c6434623f.js.gz +0 -0
- data/ui/dist/manifest.json +5 -5
- metadata +5 -5
- data/lib/motor/api.rb +0 -6
- data/ui/dist/main-4da1a5102d7bc66aefd0.js.gz +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6d39e56e7ea8a0b52d05594f2b1fb6f12860d9fd5771fbcf9bec758ce5752f08
|
4
|
+
data.tar.gz: 5cd61073f5a310dd5562225476134358cec33ab1df16983b0faca249a434c91e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bcb86c3d249b14ae24d8a37fc5418ab41e789602ede38c5ab31dbda19d82100ac1f1321ff868fef6c7e892fdde2559905afbcfe22e85c65b63ce0c826092bfc1
|
7
|
+
data.tar.gz: 051cfe7a8bdcee2ca9f173954de10108d5682c5f59c2a4291443f32552f31020baeaec78a2f7806d78be13df73f5a9a73742b77c9c4943eee1a60db81ec18100
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
module WrapIoParams
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
before_action :wrap_io_params, only: %i[update create]
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def wrap_io_params(hash = params)
|
14
|
+
hash.each do |key, value|
|
15
|
+
if key == 'io'
|
16
|
+
hash[key] = StringIO.new(value.encode('ISO-8859-1'))
|
17
|
+
elsif value.is_a?(ActionController::Parameters)
|
18
|
+
wrap_io_params(value)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
hash
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -2,16 +2,15 @@
|
|
2
2
|
|
3
3
|
module Motor
|
4
4
|
class ActiveStorageAttachmentsController < ApiBaseController
|
5
|
+
include Motor::WrapIoParams
|
6
|
+
|
5
7
|
wrap_parameters :data, except: %i[include fields]
|
6
8
|
|
7
9
|
load_and_authorize_resource :attachment, class: 'ActiveStorage::Attachment', parent: false
|
8
10
|
|
9
11
|
def create
|
10
12
|
if attachable?(@attachment.record)
|
11
|
-
@attachment.record.public_send(@attachment.name).attach(
|
12
|
-
io: StringIO.new(params.dig(:data, :file, :io).to_s.encode('ISO-8859-1')),
|
13
|
-
filename: params.dig(:data, :file, :filename)
|
14
|
-
)
|
13
|
+
@attachment.record.public_send(@attachment.name).attach(file_params)
|
15
14
|
|
16
15
|
head :ok
|
17
16
|
else
|
@@ -26,8 +25,16 @@ module Motor
|
|
26
25
|
record.respond_to?("#{@attachment.name}_attachments=")
|
27
26
|
end
|
28
27
|
|
28
|
+
def file_params
|
29
|
+
params.require(:data).require(:file).permit(:io, :filename).to_h
|
30
|
+
end
|
31
|
+
|
29
32
|
def attachment_params
|
30
|
-
params
|
33
|
+
if params[:data].present?
|
34
|
+
params.require(:data).except(:file).permit!
|
35
|
+
else
|
36
|
+
{}
|
37
|
+
end
|
31
38
|
end
|
32
39
|
end
|
33
40
|
end
|
@@ -2,13 +2,14 @@
|
|
2
2
|
|
3
3
|
module Motor
|
4
4
|
class DataController < ApiBaseController
|
5
|
+
include Motor::WrapIoParams
|
6
|
+
|
5
7
|
INSTANCE_VARIABLE_NAME = 'resource'
|
6
8
|
|
7
9
|
wrap_parameters :data, except: %i[include fields]
|
8
10
|
|
9
11
|
before_action :load_and_authorize_resource
|
10
12
|
before_action :load_and_authorize_association
|
11
|
-
before_action :wrap_io_params
|
12
13
|
|
13
14
|
def index
|
14
15
|
@resources = Motor::ApiQuery.call(@resources, params)
|
@@ -27,12 +28,20 @@ module Motor
|
|
27
28
|
@resource.save!
|
28
29
|
|
29
30
|
render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params) }
|
31
|
+
rescue ActiveRecord::RecordInvalid
|
32
|
+
render json: { errors: @resource.errors }, status: :unprocessable_entity
|
33
|
+
rescue StandardError => e
|
34
|
+
render json: { errors: [e.message] }, status: :unprocessable_entity
|
30
35
|
end
|
31
36
|
|
32
37
|
def update
|
33
38
|
@resource.update!(resource_params)
|
34
39
|
|
35
40
|
render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params) }
|
41
|
+
rescue ActiveRecord::RecordInvalid
|
42
|
+
render json: { errors: @resource.errors }, status: :unprocessable_entity
|
43
|
+
rescue StandardError => e
|
44
|
+
render json: { errors: [e.message] }, status: :unprocessable_entity
|
36
45
|
end
|
37
46
|
|
38
47
|
def destroy
|
@@ -77,6 +86,10 @@ module Motor
|
|
77
86
|
self,
|
78
87
|
options
|
79
88
|
).load_and_authorize_resource
|
89
|
+
rescue ActiveRecord::RecordNotFound
|
90
|
+
head :not_found
|
91
|
+
rescue StandardError => e
|
92
|
+
render json: { errors: [e.message] }, status: :unprocessable_entity
|
80
93
|
end
|
81
94
|
|
82
95
|
def load_and_authorize_association
|
@@ -96,6 +109,10 @@ module Motor
|
|
96
109
|
else
|
97
110
|
render json: { message: 'Unknown association' }, status: :not_found
|
98
111
|
end
|
112
|
+
rescue ActiveRecord::RecordNotFound
|
113
|
+
head :not_found
|
114
|
+
rescue StandardError => e
|
115
|
+
render json: { errors: [e.message] }, status: :unprocessable_entity
|
99
116
|
end
|
100
117
|
|
101
118
|
def resource_params
|
@@ -105,17 +122,5 @@ module Motor
|
|
105
122
|
{}
|
106
123
|
end
|
107
124
|
end
|
108
|
-
|
109
|
-
def wrap_io_params(hash = params)
|
110
|
-
hash.each do |key, value|
|
111
|
-
if key == 'io'
|
112
|
-
hash[key] = StringIO.new(value.encode('ISO-8859-1'))
|
113
|
-
elsif value.is_a?(ActionController::Parameters)
|
114
|
-
wrap_io_params(value)
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
hash
|
119
|
-
end
|
120
125
|
end
|
121
126
|
end
|
data/app/models/motor/query.rb
CHANGED
@@ -10,5 +10,13 @@ module Motor
|
|
10
10
|
serialize :preferences, HashSerializer
|
11
11
|
|
12
12
|
scope :active, -> { where(deleted_at: nil) }
|
13
|
+
|
14
|
+
def result(variables_hash = {})
|
15
|
+
result = Motor::Queries::RunQuery.call(self, variables_hash: variables_hash)
|
16
|
+
column_names = result.columns.pluck(:name)
|
17
|
+
|
18
|
+
result.data.map { |row| column_names.zip(row).to_h }
|
19
|
+
end
|
20
|
+
alias run result
|
13
21
|
end
|
14
22
|
end
|
@@ -77,7 +77,7 @@
|
|
77
77
|
<% if @query_result.data.length > 1 %>
|
78
78
|
<p style="margin: 7px 0; float: left">Showing <%= @query_result.data.first(40).size %> of <%= @query_result.data.size %> rows</p>
|
79
79
|
<% end %>
|
80
|
-
<a href="<%= Motor::Admin.routes.url_helpers.motor_ui_query_url(@alert.query, { host: 'localhost:3000'}.merge(Rails.application.config.action_mailer.default_url_options || {})) %>" target="blank" style="margin: 7px 0; float: right">Open query </a>
|
80
|
+
<a href="<%= Motor::Admin.routes.url_helpers.motor_ui_query_url(@alert.query, { host: 'localhost:3000' }.merge(Rails.application.config.action_mailer.default_url_options || {})) %>" target="blank" style="margin: 7px 0; float: right">Open query </a>
|
81
81
|
<table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 3px;">
|
82
82
|
<tr>
|
83
83
|
<td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px; max-width: 0px; overflow: scroll">
|
data/config/routes.rb
CHANGED
@@ -47,15 +47,4 @@ Motor::Admin.routes.draw do
|
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
|
51
|
-
namespace :motor, path: '' do
|
52
|
-
resources :resources, path: '/:resource',
|
53
|
-
only: %i[index show update create destroy],
|
54
|
-
controller: 'data' do
|
55
|
-
put '/:method', to: 'data#execute'
|
56
|
-
resources :association, path: '/:association',
|
57
|
-
only: %i[index create],
|
58
|
-
controller: 'data'
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
50
|
+
ActiveSupport::Notifications.instrument('motor.routes.loaded')
|
data/lib/motor.rb
CHANGED
data/lib/motor/admin.rb
CHANGED
@@ -2,6 +2,32 @@
|
|
2
2
|
|
3
3
|
module Motor
|
4
4
|
class Admin < ::Rails::Engine
|
5
|
+
initializer 'motor.startup_message' do
|
6
|
+
ActiveSupport::Notifications.subscribe('motor.routes.loaded') do
|
7
|
+
next unless Motor.server?
|
8
|
+
|
9
|
+
if Rails.application.routes.url_helpers.respond_to?(:motor_admin_path)
|
10
|
+
url =
|
11
|
+
begin
|
12
|
+
Rails.application.routes.url_helpers.motor_admin_url
|
13
|
+
rescue ArgumentError
|
14
|
+
Rails.application.routes.url_helpers.motor_admin_path
|
15
|
+
end
|
16
|
+
|
17
|
+
puts
|
18
|
+
puts "⚡ Motor::Admin is starting under #{url}"
|
19
|
+
else
|
20
|
+
puts
|
21
|
+
puts '⚠️ Motor::Admin is not mounted.'
|
22
|
+
puts 'Add the following line to your config/routes.rb:'
|
23
|
+
puts
|
24
|
+
puts " mount Motor::Admin => '/admin'"
|
25
|
+
end
|
26
|
+
|
27
|
+
puts
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
5
31
|
initializer 'motor.filter_params' do
|
6
32
|
Rails.application.config.filter_parameters += %i[io]
|
7
33
|
end
|
@@ -34,20 +34,29 @@ module Motor
|
|
34
34
|
|
35
35
|
def normalize_filter_hash(hash)
|
36
36
|
hash.each_with_object({}) do |(action, value), acc|
|
37
|
-
|
37
|
+
new_action, new_value =
|
38
38
|
if value.is_a?(Hash)
|
39
|
-
normalize_filter_hash(value)
|
39
|
+
[action, normalize_filter_hash(value)]
|
40
40
|
else
|
41
|
-
|
41
|
+
normalize_action(action, value)
|
42
42
|
end
|
43
|
+
|
44
|
+
acc[new_action] = new_value
|
45
|
+
|
46
|
+
acc
|
43
47
|
end
|
44
48
|
end
|
45
49
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
50
|
+
def normalize_action(action, value)
|
51
|
+
case action
|
52
|
+
when 'contains'
|
53
|
+
['ilike', value.sub(LIKE_FILTER_VALUE_REGEXP, '%\1%')]
|
54
|
+
when 'starts_with'
|
55
|
+
['ilike', value.sub(LIKE_FILTER_VALUE_REGEXP, '\1%')]
|
56
|
+
when 'ends_with'
|
57
|
+
['ilike', value.sub(LIKE_FILTER_VALUE_REGEXP, '%\1')]
|
49
58
|
else
|
50
|
-
value
|
59
|
+
[action, value]
|
51
60
|
end
|
52
61
|
end
|
53
62
|
end
|
data/lib/motor/build_schema.rb
CHANGED
@@ -133,7 +133,7 @@ module Motor
|
|
133
133
|
column_type: is_attachment ? 'file' : 'integer',
|
134
134
|
access_type: access_type,
|
135
135
|
default_value: default_attrs[column_name],
|
136
|
-
validators: fetch_validators(model, column_name),
|
136
|
+
validators: fetch_validators(model, column_name, ref),
|
137
137
|
format: {},
|
138
138
|
reference: {
|
139
139
|
name: name,
|
@@ -172,23 +172,34 @@ module Motor
|
|
172
172
|
end.compact
|
173
173
|
end
|
174
174
|
|
175
|
-
def fetch_validators(model, column_name)
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
{ includes: validator.send(:delimiter) }
|
180
|
-
when ActiveRecord::Validations::PresenceValidator
|
181
|
-
{ required: true }
|
182
|
-
when ActiveModel::Validations::FormatValidator
|
183
|
-
{ format: JsRegex.new(validator.options[:with]).to_h.slice(:source, :options) }
|
184
|
-
when ActiveRecord::Validations::LengthValidator
|
185
|
-
{ length: validator.options }
|
186
|
-
when ActiveModel::Validations::NumericalityValidator
|
187
|
-
{ numeric: validator.options }
|
175
|
+
def fetch_validators(model, column_name, reflection = nil)
|
176
|
+
validators =
|
177
|
+
if reflection&.belongs_to? && !reflection.options[:optional]
|
178
|
+
[{ required: true }]
|
188
179
|
else
|
189
|
-
|
180
|
+
[]
|
190
181
|
end
|
182
|
+
|
183
|
+
validators += model.validators_on(column_name).map do |validator|
|
184
|
+
build_validator_hash(validator)
|
191
185
|
end.compact
|
186
|
+
|
187
|
+
validators.uniq
|
188
|
+
end
|
189
|
+
|
190
|
+
def build_validator_hash(validator)
|
191
|
+
case validator
|
192
|
+
when ActiveModel::Validations::InclusionValidator
|
193
|
+
{ includes: validator.send(:delimiter) }
|
194
|
+
when ActiveRecord::Validations::PresenceValidator
|
195
|
+
{ required: true }
|
196
|
+
when ActiveModel::Validations::FormatValidator
|
197
|
+
{ format: JsRegex.new(validator.options[:with]).to_h.slice(:source, :options) }
|
198
|
+
when ActiveRecord::Validations::LengthValidator
|
199
|
+
{ length: validator.options }
|
200
|
+
when ActiveModel::Validations::NumericalityValidator
|
201
|
+
{ numeric: validator.options }
|
202
|
+
end
|
192
203
|
end
|
193
204
|
|
194
205
|
def eager_load_models!
|
@@ -90,9 +90,9 @@ module Motor
|
|
90
90
|
return preferences if new_prefs[configs_name].blank?
|
91
91
|
|
92
92
|
normalized_configs = public_send("normalize_#{configs_name}",
|
93
|
-
default_prefs[
|
94
|
-
existing_prefs.fetch(
|
95
|
-
new_prefs.fetch(
|
93
|
+
default_prefs[configs_name],
|
94
|
+
existing_prefs.fetch(configs_name, []),
|
95
|
+
new_prefs.fetch(configs_name, []))
|
96
96
|
|
97
97
|
preferences[configs_name] = normalized_configs
|
98
98
|
|
data/lib/motor/tags.rb
CHANGED
@@ -21,7 +21,8 @@ module Motor
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def remove_missing_tags(taggable, tags)
|
24
|
-
|
24
|
+
downcase_tags = tags.map(&:downcase)
|
25
|
+
tags_to_remove = taggable.tags.reject { |tt| tt.name.downcase.in?(downcase_tags) }
|
25
26
|
|
26
27
|
taggable.tags -= tags_to_remove
|
27
28
|
|
data/lib/motor/version.rb
CHANGED
Binary file
|
Binary file
|
data/ui/dist/manifest.json
CHANGED
@@ -5,9 +5,9 @@
|
|
5
5
|
"fonts/ionicons.ttf?v=3.0.0-alpha.3": "fonts/ionicons.ttf",
|
6
6
|
"fonts/ionicons.woff2?v=3.0.0-alpha.3": "fonts/ionicons.woff2",
|
7
7
|
"fonts/ionicons.woff?v=3.0.0-alpha.3": "fonts/ionicons.woff",
|
8
|
-
"main-
|
9
|
-
"main-
|
10
|
-
"main-
|
11
|
-
"main.css": "main-
|
12
|
-
"main.js": "main-
|
8
|
+
"main-052729fa924c6434623f.css.gz": "main-052729fa924c6434623f.css.gz",
|
9
|
+
"main-052729fa924c6434623f.js.LICENSE.txt": "main-052729fa924c6434623f.js.LICENSE.txt",
|
10
|
+
"main-052729fa924c6434623f.js.gz": "main-052729fa924c6434623f.js.gz",
|
11
|
+
"main.css": "main-052729fa924c6434623f.css",
|
12
|
+
"main.js": "main-052729fa924c6434623f.js"
|
13
13
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motor-admin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.34
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pete Matsyburka
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-05-
|
11
|
+
date: 2021-05-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord-filter
|
@@ -132,6 +132,7 @@ files:
|
|
132
132
|
- LICENSE
|
133
133
|
- README.md
|
134
134
|
- Rakefile
|
135
|
+
- app/controllers/concerns/motor/wrap_io_params.rb
|
135
136
|
- app/controllers/motor/active_storage_attachments_controller.rb
|
136
137
|
- app/controllers/motor/alerts_controller.rb
|
137
138
|
- app/controllers/motor/api_base_controller.rb
|
@@ -182,7 +183,6 @@ files:
|
|
182
183
|
- lib/motor/alerts/persistance.rb
|
183
184
|
- lib/motor/alerts/scheduled_alerts_cache.rb
|
184
185
|
- lib/motor/alerts/scheduler.rb
|
185
|
-
- lib/motor/api.rb
|
186
186
|
- lib/motor/api_query.rb
|
187
187
|
- lib/motor/api_query/apply_scope.rb
|
188
188
|
- lib/motor/api_query/build_json.rb
|
@@ -214,8 +214,8 @@ files:
|
|
214
214
|
- lib/motor/ui_configs.rb
|
215
215
|
- lib/motor/version.rb
|
216
216
|
- ui/dist/fonts/ionicons.woff2
|
217
|
-
- ui/dist/main-
|
218
|
-
- ui/dist/main-
|
217
|
+
- ui/dist/main-052729fa924c6434623f.css.gz
|
218
|
+
- ui/dist/main-052729fa924c6434623f.js.gz
|
219
219
|
- ui/dist/manifest.json
|
220
220
|
homepage:
|
221
221
|
licenses:
|
data/lib/motor/api.rb
DELETED
Binary file
|