motor-admin 0.1.32 → 0.1.37

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9db6c3263064796fde708305f78fbc33db762534890eeeb79ec13e2954b8b54b
4
- data.tar.gz: 462878ba2c5c21c77af910b27be67a1a8b7518ba73fd2b239c3309f88e9ec49a
3
+ metadata.gz: c003af4059ae5bd3a60d30031526c1ed2aefd267c7e1f93f33e5bac94eaff5c7
4
+ data.tar.gz: aa7403bca65f0d936b86bcc58f1fe88b4081439d74843f1ca1c3d25fd4da8a81
5
5
  SHA512:
6
- metadata.gz: 457cfc674fa8845a9a1e21e6177a38a9159c92ad021ffb6af2504eb1df326cf5325efad85efed341082e9c3e3a863aba408d456626f5da167671e60af93f3416
7
- data.tar.gz: e13a4a74752db108786a9625d3ea3e851e6bdcc4a7b6ac55f041d3cd753b5993f61e8d825038846603718483733888258e734d2fbc204b27b0149b2c751d0b46
6
+ metadata.gz: a195dd04aceadc74e1d7fa89dc0100c15fc045c0fad238230567069801698d341d6a9e598b861ae9e802e61dd5a48915d15c74b88a1228deae5a985fad283762
7
+ data.tar.gz: 7d6dc23a2750dc941b20d83170dc3f2eb3018d02897b2500b99831285ac139fcde46ebfa7ae044be856504bac6f09e1d11e70c02a443eba6d70278b959784f74
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module LoadAndAuthorizeDynamicResource
5
+ extend ActiveSupport::Concern
6
+
7
+ INSTANCE_VARIABLE_NAME = 'resource'
8
+
9
+ included do
10
+ before_action :load_and_authorize_resource
11
+ before_action :load_and_authorize_association
12
+ end
13
+
14
+ def resource_class
15
+ @resource_class ||=
16
+ Motor::BuildSchema::Utils.classify_slug(resource_name_prefix + params[:resource])
17
+ end
18
+
19
+ def resource_name_prefix
20
+ ''
21
+ end
22
+
23
+ def load_and_authorize_resource
24
+ options = {
25
+ class: resource_class,
26
+ parent: false,
27
+ instance_name: INSTANCE_VARIABLE_NAME
28
+ }
29
+
30
+ if params[:resource_id].present?
31
+ options = options.merge(
32
+ parent: true,
33
+ id_param: :resource_id
34
+ )
35
+ end
36
+
37
+ CanCan::ControllerResource.new(
38
+ self,
39
+ options
40
+ ).load_and_authorize_resource
41
+ rescue ActiveRecord::RecordNotFound
42
+ head :not_found
43
+ rescue StandardError => e
44
+ render json: { errors: [e.message] }, status: :unprocessable_entity
45
+ end
46
+
47
+ def load_and_authorize_association
48
+ return if params[:association].blank?
49
+
50
+ association = resource_class.reflections[params[:association]]
51
+
52
+ if association
53
+ CanCan::ControllerResource.new(
54
+ self,
55
+ class: association.klass,
56
+ parent: false,
57
+ through: :resource,
58
+ through_association: params[:association].to_sym,
59
+ instance_name: INSTANCE_VARIABLE_NAME
60
+ ).load_and_authorize_resource
61
+ else
62
+ render json: { message: 'Unknown association' }, status: :not_found
63
+ end
64
+ rescue ActiveRecord::RecordNotFound
65
+ head :not_found
66
+ rescue StandardError => e
67
+ render json: { errors: [e.message] }, status: :unprocessable_entity
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class AuditsController < ApiBaseController
5
+ load_and_authorize_resource :audit
6
+
7
+ def index
8
+ audits = Motor::ApiQuery.call(@audits, params)
9
+
10
+ render json: {
11
+ data: Motor::ApiQuery::BuildJson.call(audits, params),
12
+ meta: Motor::ApiQuery::BuildMeta.call(audits, params)
13
+ }
14
+ end
15
+ end
16
+ end
@@ -2,14 +2,10 @@
2
2
 
3
3
  module Motor
4
4
  class DataController < ApiBaseController
5
- include Motor::WrapIoParams
6
-
7
- INSTANCE_VARIABLE_NAME = 'resource'
8
-
9
5
  wrap_parameters :data, except: %i[include fields]
10
6
 
11
- before_action :load_and_authorize_resource
12
- before_action :load_and_authorize_association
7
+ include Motor::WrapIoParams
8
+ include Motor::LoadAndAuthorizeDynamicResource
13
9
 
14
10
  def index
15
11
  @resources = Motor::ApiQuery.call(@resources, params)
@@ -64,53 +60,6 @@ module Motor
64
60
 
65
61
  private
66
62
 
67
- def resource_class
68
- @resource_class ||= Motor::BuildSchema::Utils.classify_slug(params[:resource])
69
- end
70
-
71
- def load_and_authorize_resource
72
- options = {
73
- class: resource_class,
74
- parent: false,
75
- instance_name: INSTANCE_VARIABLE_NAME
76
- }
77
-
78
- if params[:resource_id].present?
79
- options = options.merge(
80
- parent: true,
81
- id_param: :resource_id
82
- )
83
- end
84
-
85
- CanCan::ControllerResource.new(
86
- self,
87
- options
88
- ).load_and_authorize_resource
89
- rescue StandardError => e
90
- render json: { errors: [e.message] }, status: :unprocessable_entity
91
- end
92
-
93
- def load_and_authorize_association
94
- return if params[:association].blank?
95
-
96
- association = resource_class.reflections[params[:association]]
97
-
98
- if association
99
- CanCan::ControllerResource.new(
100
- self,
101
- class: association.klass,
102
- parent: false,
103
- through: :resource,
104
- through_association: params[:association].to_sym,
105
- instance_name: INSTANCE_VARIABLE_NAME
106
- ).load_and_authorize_resource
107
- else
108
- render json: { message: 'Unknown association' }, status: :not_found
109
- end
110
- rescue StandardError => e
111
- render json: { errors: [e.message] }, status: :unprocessable_entity
112
- end
113
-
114
63
  def resource_params
115
64
  if params[:data].present?
116
65
  params.require(:data).except(resource_class.primary_key).permit!
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class Alert < ::Motor::ApplicationRecord
5
+ audited
6
+
5
7
  belongs_to :query
6
8
  belongs_to :author, polymorphic: true, optional: true
7
9
 
@@ -4,5 +4,15 @@ module Motor
4
4
  class ApplicationRecord < ActiveRecord::Base
5
5
  self.abstract_class = true
6
6
  self.table_name_prefix = 'motor_'
7
+
8
+ def self.audited(*args)
9
+ default_class = Audited.audit_class
10
+
11
+ Audited.audit_class = Motor::Audit
12
+
13
+ super
14
+ ensure
15
+ Audited.audit_class = default_class
16
+ end
7
17
  end
8
18
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class Audit < Audited::Audit
5
+ self.table_name = 'motor_audits'
6
+
7
+ serialize :audited_changes, HashSerializer
8
+ end
9
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class Config < ::Motor::ApplicationRecord
5
+ audited
6
+
5
7
  serialize :value, HashSerializer
6
8
  end
7
9
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class Dashboard < ::Motor::ApplicationRecord
5
+ audited
6
+
5
7
  belongs_to :author, polymorphic: true, optional: true
6
8
 
7
9
  has_many :taggable_tags, as: :taggable
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class Form < ::Motor::ApplicationRecord
5
+ audited
6
+
5
7
  belongs_to :author, polymorphic: true, optional: true
6
8
 
7
9
  has_many :taggable_tags, as: :taggable
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class Query < ::Motor::ApplicationRecord
5
+ audited
6
+
5
7
  belongs_to :author, polymorphic: true, optional: true
6
8
 
7
9
  has_many :taggable_tags, as: :taggable
@@ -10,5 +12,13 @@ module Motor
10
12
  serialize :preferences, HashSerializer
11
13
 
12
14
  scope :active, -> { where(deleted_at: nil) }
15
+
16
+ def result(variables_hash = {})
17
+ result = Motor::Queries::RunQuery.call(self, variables_hash: variables_hash)
18
+ column_names = result.columns.pluck(:name)
19
+
20
+ result.data.map { |row| column_names.zip(row).to_h }
21
+ end
22
+ alias run result
13
23
  end
14
24
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class Resource < ::Motor::ApplicationRecord
5
+ audited
6
+
5
7
  serialize :preferences, HashSerializer
6
8
  end
7
9
  end
data/config/routes.rb CHANGED
@@ -15,6 +15,7 @@ Motor::Admin.routes.draw do
15
15
  resources :alerts, only: %i[index show create update destroy]
16
16
  resources :active_storage_attachments, only: %i[create], path: 'data/active_storage__attachments'
17
17
  resource :schema, only: %i[show update]
18
+ resources :audits, only: %i[index]
18
19
  resources :resources, path: '/data/:resource',
19
20
  only: %i[index show update create destroy],
20
21
  controller: 'data' do
@@ -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
- name: 'motor_queries_lower_name_unique_index',
17
- unique: true,
18
- where: 'deleted_at IS NULL'
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
- name: 'motor_dashboards_lower_title_unique_index',
34
- unique: true,
35
- where: 'deleted_at IS NULL'
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
- name: 'motor_forms_lower_name_unique_index',
53
- unique: true,
54
- where: 'deleted_at IS NULL'
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
- name: 'motor_alerts_lower_name_unique_index',
91
- unique: true,
92
- where: 'deleted_at IS NULL'
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
- name: 'motor_tags_lower_name_unique_index',
111
- unique: true
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
- name: 'motor_polymorphic_association_tag_index',
121
- unique: true
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
@@ -6,6 +6,7 @@ require 'js_regex'
6
6
  require 'fugit'
7
7
  require 'csv'
8
8
  require 'active_record/filter'
9
+ require 'audited'
9
10
 
10
11
  module Motor
11
12
  PATH = Pathname.new(__dir__)
@@ -45,7 +46,6 @@ end
45
46
 
46
47
  require 'motor/version'
47
48
  require 'motor/admin'
48
- # require 'motor/api'
49
49
  require 'motor/assets'
50
50
  require 'motor/build_schema'
51
51
  require 'motor/api_query'
data/lib/motor/admin.rb CHANGED
@@ -15,9 +15,16 @@ module Motor
15
15
  end
16
16
 
17
17
  puts
18
- puts "⚡ Motor Admin is starting under #{url}"
18
+ puts "⚡ Motor::Admin is starting under #{url}"
19
+ else
19
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'"
20
25
  end
26
+
27
+ puts
21
28
  end
22
29
  end
23
30
 
@@ -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.order(arel_order).left_joins(join_params)
16
+ rel.reorder(arel_order).left_joins(join_params)
17
17
  end
18
18
 
19
19
  def build_join_params(_model, param)
@@ -48,8 +48,8 @@ module Motor
48
48
 
49
49
  DEFAULT_TABS = [
50
50
  {
51
- name: 'summary',
52
- display_name: 'Summary',
51
+ name: 'details',
52
+ display_name: 'Details',
53
53
  tab_type: 'default',
54
54
  preferences: {},
55
55
  visible: true
@@ -11,7 +11,7 @@ module Motor
11
11
  models.map do |model|
12
12
  build_model_schema(model)
13
13
  rescue StandardError, NotImplementedError => e
14
- Rails.logger.error(e)
14
+ Rails.logger.error(e) if model.name != 'Audited::Audit'
15
15
 
16
16
  next
17
17
  end.compact
@@ -24,6 +24,7 @@ module Motor
24
24
  models = models.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)
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
- tags_to_remove = taggable.tags.reject { |tt| tt.name.downcase.in?(tags) }
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Motor
4
- VERSION = '0.1.32'
4
+ VERSION = '0.1.37'
5
5
  end
@@ -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-eea896c6f0ede15495f0.css.gz": "main-eea896c6f0ede15495f0.css.gz",
9
- "main-eea896c6f0ede15495f0.js.LICENSE.txt": "main-eea896c6f0ede15495f0.js.LICENSE.txt",
10
- "main-eea896c6f0ede15495f0.js.gz": "main-eea896c6f0ede15495f0.js.gz",
11
- "main.css": "main-eea896c6f0ede15495f0.css",
12
- "main.js": "main-eea896c6f0ede15495f0.js"
8
+ "main-358ea31cd7020f915067.css.gz": "main-358ea31cd7020f915067.css.gz",
9
+ "main-358ea31cd7020f915067.js.LICENSE.txt": "main-358ea31cd7020f915067.js.LICENSE.txt",
10
+ "main-358ea31cd7020f915067.js.gz": "main-358ea31cd7020f915067.js.gz",
11
+ "main.css": "main-358ea31cd7020f915067.css",
12
+ "main.js": "main-358ea31cd7020f915067.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.32
4
+ version: 0.1.37
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 00:00:00.000000000 Z
11
+ date: 2021-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord-filter
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: 0.6.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: audited
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.9'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.9'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: cancancan
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -132,12 +146,14 @@ files:
132
146
  - LICENSE
133
147
  - README.md
134
148
  - Rakefile
149
+ - app/controllers/concerns/motor/load_and_authorize_dynamic_resource.rb
135
150
  - app/controllers/concerns/motor/wrap_io_params.rb
136
151
  - app/controllers/motor/active_storage_attachments_controller.rb
137
152
  - app/controllers/motor/alerts_controller.rb
138
153
  - app/controllers/motor/api_base_controller.rb
139
154
  - app/controllers/motor/application_controller.rb
140
155
  - app/controllers/motor/assets_controller.rb
156
+ - app/controllers/motor/audits_controller.rb
141
157
  - app/controllers/motor/configs_controller.rb
142
158
  - app/controllers/motor/dashboards_controller.rb
143
159
  - app/controllers/motor/data_controller.rb
@@ -157,6 +173,7 @@ files:
157
173
  - app/models/motor/alert.rb
158
174
  - app/models/motor/alert_lock.rb
159
175
  - app/models/motor/application_record.rb
176
+ - app/models/motor/audit.rb
160
177
  - app/models/motor/config.rb
161
178
  - app/models/motor/dashboard.rb
162
179
  - app/models/motor/form.rb
@@ -214,8 +231,8 @@ files:
214
231
  - lib/motor/ui_configs.rb
215
232
  - lib/motor/version.rb
216
233
  - ui/dist/fonts/ionicons.woff2
217
- - ui/dist/main-eea896c6f0ede15495f0.css.gz
218
- - ui/dist/main-eea896c6f0ede15495f0.js.gz
234
+ - ui/dist/main-358ea31cd7020f915067.css.gz
235
+ - ui/dist/main-358ea31cd7020f915067.js.gz
219
236
  - ui/dist/manifest.json
220
237
  homepage:
221
238
  licenses: