motor-admin 0.1.33 → 0.1.39

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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/motor/load_and_authorize_dynamic_resource.rb +70 -0
  3. data/app/controllers/motor/alerts_controller.rb +12 -6
  4. data/app/controllers/motor/audits_controller.rb +16 -0
  5. data/app/controllers/motor/configs_controller.rb +1 -0
  6. data/app/controllers/motor/dashboards_controller.rb +4 -0
  7. data/app/controllers/motor/data_controller.rb +2 -57
  8. data/app/controllers/motor/forms_controller.rb +4 -0
  9. data/app/controllers/motor/queries_controller.rb +4 -0
  10. data/app/controllers/motor/resources_controller.rb +1 -0
  11. data/app/controllers/motor/ui_controller.rb +4 -0
  12. data/app/models/motor/alert.rb +2 -0
  13. data/app/models/motor/application_record.rb +10 -0
  14. data/app/models/motor/audit.rb +9 -0
  15. data/app/models/motor/config.rb +2 -0
  16. data/app/models/motor/dashboard.rb +2 -0
  17. data/app/models/motor/form.rb +2 -0
  18. data/app/models/motor/query.rb +2 -0
  19. data/app/models/motor/resource.rb +2 -0
  20. data/app/views/motor/ui/show.html.erb +1 -1
  21. data/config/routes.rb +1 -0
  22. data/lib/generators/motor/templates/install.rb +40 -16
  23. data/lib/motor.rb +11 -2
  24. data/lib/motor/admin.rb +8 -0
  25. data/lib/motor/alerts/persistance.rb +4 -1
  26. data/lib/motor/alerts/scheduler.rb +1 -1
  27. data/lib/motor/api_query/sort.rb +1 -1
  28. data/lib/motor/build_schema.rb +3 -3
  29. data/lib/motor/build_schema/load_from_rails.rb +2 -1
  30. data/lib/motor/build_schema/merge_schema_configs.rb +8 -4
  31. data/lib/motor/build_schema/reorder_schema.rb +10 -4
  32. data/lib/motor/configs.rb +16 -0
  33. data/lib/motor/configs/build_configs_hash.rb +83 -0
  34. data/lib/motor/configs/build_ui_app_tag.rb +71 -0
  35. data/lib/motor/configs/load_from_cache.rb +81 -0
  36. data/lib/motor/configs/sync_from_file.rb +34 -0
  37. data/lib/motor/configs/sync_from_hash.rb +124 -0
  38. data/lib/motor/configs/sync_middleware.rb +72 -0
  39. data/lib/motor/configs/sync_with_remote.rb +39 -0
  40. data/lib/motor/configs/write_to_file.rb +36 -0
  41. data/lib/motor/dashboards/persistance.rb +4 -1
  42. data/lib/motor/forms/persistance.rb +4 -1
  43. data/lib/motor/net_http_utils.rb +37 -0
  44. data/lib/motor/queries/persistance.rb +4 -1
  45. data/lib/motor/railtie.rb +11 -0
  46. data/lib/motor/tags.rb +2 -1
  47. data/lib/motor/tasks/motor.rake +29 -0
  48. data/lib/motor/version.rb +1 -1
  49. data/ui/dist/{main-b3096c53c77bd4641e52.css.gz → main-e443f49b386b119ff25a.css.gz} +0 -0
  50. data/ui/dist/main-e443f49b386b119ff25a.js.gz +0 -0
  51. data/ui/dist/manifest.json +5 -5
  52. metadata +33 -5
  53. data/lib/motor/ui_configs.rb +0 -82
  54. data/ui/dist/main-b3096c53c77bd4641e52.js.gz +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 51ebe00c16e1c384a460c6a6b11339c967fbff8b80c0096cb9471abb9082d43c
4
- data.tar.gz: 16324c84044ff120d90a5b6955fd7d5cc165e1ec547fe06a69315efc228e17be
3
+ metadata.gz: 2e0ab742c32ae1b59bfa12c6eb1f838e5d379cb5a964521fe7f345de5fabf953
4
+ data.tar.gz: 9f018c3475ae06b4104e35f30774c80b2098a8a23b7fa3a8e4ac35df089ee98e
5
5
  SHA512:
6
- metadata.gz: 29a38abca31cf14b81ea6cedbfdb62bf0e7a6c04e6890c32dd28c3be6a17033b24e4d9fc69f265f4abd816a7660907cde10b5fae0d6e5098d6f7e9c385190440
7
- data.tar.gz: c03e22ec45ded369d732b0a59f6035dc8579764d75016161eb294f517b9d714db4485057fae41f54309b2ca03f81daa684147c125ba5c3fb35696e52f91eb4ce
6
+ metadata.gz: '0651978240824b17dc1f379047faf9f5c5a393b9bebe6e37ae692a606630a300454a2c56c88173383defef451787555006d355fe26c2dd38173f0ece03e116a0'
7
+ data.tar.gz: b15c006847a5c9dcea315741ccc3a46e3a2bba49ab05d2aceab44c75a530ba0f5fad79067aae0b8e31abad9cda105a827cbdb892d93fd1c2c1fe72a2f62a8637
@@ -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
@@ -18,12 +18,15 @@ module Motor
18
18
  end
19
19
 
20
20
  def create
21
- ApplicationRecord.transaction { @alert.save! }
22
- Motor::Alerts::ScheduledAlertsCache.clear
23
-
24
- render json: { data: Motor::ApiQuery::BuildJson.call(@alert, params) }
25
- rescue Motor::Alerts::Persistance::NameAlreadyExists
26
- name_already_exists_response
21
+ if Motor::Alerts::Persistance.name_already_exists?(@alert)
22
+ name_already_exists_response
23
+ else
24
+ ApplicationRecord.transaction { @alert.save! }
25
+ Motor::Alerts::ScheduledAlertsCache.clear
26
+ Motor::Configs::WriteToFile.call
27
+
28
+ render json: { data: Motor::ApiQuery::BuildJson.call(@alert, params) }
29
+ end
27
30
  rescue Motor::Alerts::Persistance::InvalidInterval
28
31
  invalid_interval_response
29
32
  end
@@ -31,6 +34,7 @@ module Motor
31
34
  def update
32
35
  Motor::Alerts::Persistance.update_from_params!(@alert, alert_params)
33
36
  Motor::Alerts::ScheduledAlertsCache.clear
37
+ Motor::Configs::WriteToFile.call
34
38
 
35
39
  render json: { data: Motor::ApiQuery::BuildJson.call(@alert, params) }
36
40
  rescue Motor::Alerts::Persistance::NameAlreadyExists
@@ -42,6 +46,8 @@ module Motor
42
46
  def destroy
43
47
  @alert.update!(deleted_at: Time.current)
44
48
 
49
+ Motor::Configs::WriteToFile.call
50
+
45
51
  head :ok
46
52
  end
47
53
 
@@ -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
@@ -17,6 +17,7 @@ module Motor
17
17
  end
18
18
 
19
19
  @config.save!
20
+ Motor::Configs::WriteToFile.call
20
21
 
21
22
  render json: { data: Motor::ApiQuery::BuildJson.call(@config, params) }
22
23
  rescue ActiveRecord::RecordNotUnique
@@ -22,6 +22,7 @@ module Motor
22
22
  render json: { errors: [{ source: 'title', detail: 'Title already exists' }] }, status: :unprocessable_entity
23
23
  else
24
24
  ApplicationRecord.transaction { @dashboard.save! }
25
+ Motor::Configs::WriteToFile.call
25
26
 
26
27
  render json: { data: Motor::ApiQuery::BuildJson.call(@dashboard, params) }
27
28
  end
@@ -31,6 +32,7 @@ module Motor
31
32
 
32
33
  def update
33
34
  Motor::Dashboards::Persistance.update_from_params!(@dashboard, dashboard_params)
35
+ Motor::Configs::WriteToFile.call
34
36
 
35
37
  render json: { data: Motor::ApiQuery::BuildJson.call(@dashboard, params) }
36
38
  rescue Motor::Dashboards::Persistance::TitleAlreadyExists
@@ -40,6 +42,8 @@ module Motor
40
42
  def destroy
41
43
  @dashboard.update!(deleted_at: Time.current)
42
44
 
45
+ Motor::Configs::WriteToFile.call
46
+
43
47
  head :ok
44
48
  end
45
49
 
@@ -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,57 +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 ActiveRecord::RecordNotFound
90
- head :not_found
91
- rescue StandardError => e
92
- render json: { errors: [e.message] }, status: :unprocessable_entity
93
- end
94
-
95
- def load_and_authorize_association
96
- return if params[:association].blank?
97
-
98
- association = resource_class.reflections[params[:association]]
99
-
100
- if association
101
- CanCan::ControllerResource.new(
102
- self,
103
- class: association.klass,
104
- parent: false,
105
- through: :resource,
106
- through_association: params[:association].to_sym,
107
- instance_name: INSTANCE_VARIABLE_NAME
108
- ).load_and_authorize_resource
109
- else
110
- render json: { message: 'Unknown association' }, status: :not_found
111
- end
112
- rescue ActiveRecord::RecordNotFound
113
- head :not_found
114
- rescue StandardError => e
115
- render json: { errors: [e.message] }, status: :unprocessable_entity
116
- end
117
-
118
63
  def resource_params
119
64
  if params[:data].present?
120
65
  params.require(:data).except(resource_class.primary_key).permit!
@@ -22,6 +22,7 @@ module Motor
22
22
  render json: { errors: [{ source: 'name', detail: 'Name already exists' }] }, status: :unprocessable_entity
23
23
  else
24
24
  ApplicationRecord.transaction { @form.save! }
25
+ Motor::Configs::WriteToFile.call
25
26
 
26
27
  render json: { data: Motor::ApiQuery::BuildJson.call(@form, params) }
27
28
  end
@@ -31,6 +32,7 @@ module Motor
31
32
 
32
33
  def update
33
34
  Motor::Forms::Persistance.update_from_params!(@form, form_params)
35
+ Motor::Configs::WriteToFile.call
34
36
 
35
37
  render json: { data: Motor::ApiQuery::BuildJson.call(@form, params) }
36
38
  rescue Motor::Forms::Persistance::NameAlreadyExists
@@ -40,6 +42,8 @@ module Motor
40
42
  def destroy
41
43
  @form.update!(deleted_at: Time.current)
42
44
 
45
+ Motor::Configs::WriteToFile.call
46
+
43
47
  head :ok
44
48
  end
45
49
 
@@ -22,6 +22,7 @@ module Motor
22
22
  render json: { errors: [{ source: 'name', detail: 'Name already exists' }] }, status: :unprocessable_entity
23
23
  else
24
24
  ApplicationRecord.transaction { @query.save! }
25
+ Motor::Configs::WriteToFile.call
25
26
 
26
27
  render json: { data: Motor::ApiQuery::BuildJson.call(@query, params) }
27
28
  end
@@ -31,6 +32,7 @@ module Motor
31
32
 
32
33
  def update
33
34
  Motor::Queries::Persistance.update_from_params!(@query, query_params)
35
+ Motor::Configs::WriteToFile.call
34
36
 
35
37
  render json: { data: Motor::ApiQuery::BuildJson.call(@query, params) }
36
38
  rescue Motor::Queries::Persistance::NameAlreadyExists
@@ -40,6 +42,8 @@ module Motor
40
42
  def destroy
41
43
  @query.update!(deleted_at: Time.current)
42
44
 
45
+ Motor::Configs::WriteToFile.call
46
+
43
47
  head :ok
44
48
  end
45
49
 
@@ -12,6 +12,7 @@ module Motor
12
12
 
13
13
  def create
14
14
  Motor::BuildSchema::PersistResourceConfigs.call(@resource)
15
+ Motor::Configs::WriteToFile.call
15
16
 
16
17
  render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params) }
17
18
  end
@@ -7,12 +7,16 @@ module Motor
7
7
  def index
8
8
  Motor.reload! if Motor.development?
9
9
 
10
+ Motor::Configs::SyncFromFile.call
11
+
10
12
  render :show
11
13
  end
12
14
 
13
15
  def show
14
16
  Motor.reload! if Motor.development?
15
17
 
18
+ Motor::Configs::SyncFromFile.call
19
+
16
20
  render :show
17
21
  end
18
22
  end
@@ -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
@@ -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
@@ -1 +1 @@
1
- <%= raw(Motor::UiConfigs.app_tag) %>
1
+ <%= raw(Motor::Configs::BuildUiAppTag.call) %>
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