motor-admin 0.1.9 → 0.1.14

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/motor/active_storage_attachments_controller.rb +28 -0
  3. data/app/controllers/motor/alerts_controller.rb +2 -0
  4. data/app/controllers/motor/assets_controller.rb +15 -3
  5. data/app/controllers/motor/configs_controller.rb +2 -0
  6. data/app/controllers/motor/dashboards_controller.rb +2 -0
  7. data/app/controllers/motor/data_controller.rb +20 -1
  8. data/app/controllers/motor/forms_controller.rb +2 -0
  9. data/app/controllers/motor/queries_controller.rb +2 -0
  10. data/app/controllers/motor/resources_controller.rb +2 -0
  11. data/app/controllers/motor/run_queries_controller.rb +2 -0
  12. data/app/controllers/motor/send_alerts_controller.rb +2 -0
  13. data/app/models/motor/alert.rb +1 -1
  14. data/app/models/motor/alert_lock.rb +1 -1
  15. data/app/models/motor/config.rb +1 -1
  16. data/app/models/motor/dashboard.rb +1 -1
  17. data/app/models/motor/form.rb +1 -1
  18. data/app/models/motor/query.rb +1 -1
  19. data/app/models/motor/resource.rb +1 -1
  20. data/app/models/motor/tag.rb +1 -1
  21. data/app/models/motor/taggable_tag.rb +1 -1
  22. data/config/routes.rb +1 -0
  23. data/lib/motor.rb +1 -0
  24. data/lib/motor/active_record_utils.rb +2 -0
  25. data/lib/motor/active_record_utils/active_storage_links_extension.rb +15 -0
  26. data/lib/motor/active_record_utils/defined_scopes_extension.rb +19 -0
  27. data/lib/motor/admin.rb +14 -3
  28. data/lib/motor/alerts/persistance.rb +1 -2
  29. data/lib/motor/alerts/scheduled_alerts_cache.rb +0 -4
  30. data/lib/motor/api_query.rb +2 -0
  31. data/lib/motor/api_query/apply_scope.rb +26 -0
  32. data/lib/motor/api_query/build_json.rb +3 -2
  33. data/lib/motor/api_query/sort.rb +28 -8
  34. data/lib/motor/assets.rb +4 -4
  35. data/lib/motor/build_schema.rb +60 -7
  36. data/lib/motor/build_schema/active_storage_attachment_schema.rb +84 -0
  37. data/lib/motor/build_schema/find_display_column.rb +1 -1
  38. data/lib/motor/build_schema/load_from_rails.rb +112 -84
  39. data/lib/motor/build_schema/merge_schema_configs.rb +13 -6
  40. data/lib/motor/build_schema/persist_resource_configs.rb +38 -1
  41. data/lib/motor/build_schema/reorder_schema.rb +24 -2
  42. data/lib/motor/dashboards/persistance.rb +2 -2
  43. data/lib/motor/forms/persistance.rb +2 -2
  44. data/lib/motor/queries/persistance.rb +2 -2
  45. data/lib/motor/queries/run_query.rb +10 -5
  46. data/lib/motor/version.rb +1 -1
  47. data/ui/dist/main-f7c7f445c53544d2d7b8.css.gz +0 -0
  48. data/ui/dist/main-f7c7f445c53544d2d7b8.js.gz +0 -0
  49. data/ui/dist/manifest.json +5 -5
  50. metadata +9 -4
  51. data/ui/dist/main-46621a8bdbb789e17c3f.css.gz +0 -0
  52. data/ui/dist/main-46621a8bdbb789e17c3f.js.gz +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d6f58a141ea2313b151ee2648a4b98ea3327b8f81b5d7473d8f4a36e6ffb7a4
4
- data.tar.gz: b30868890700641a08cff0abc865e736823def3b066ca8bf38c4968dddd3e2b4
3
+ metadata.gz: 690872e084d43d0b2759f48a2d33b7399470b6dec88678248982daf0b0ca2e8a
4
+ data.tar.gz: 3949cd11e7773c19dd03c77dd1b52477ed282e3e40e9e688bf9018e7c93c2b13
5
5
  SHA512:
6
- metadata.gz: cc33e35f237bc0d15c57da84d67276efa2b26bc09d6b19845a1f6438f81c4e42ec0d5a64274aecaa817faacbde7d5f5c11245b3a0c81f97e3c3e99c808e8b7ec
7
- data.tar.gz: 44152701924a0f006ed102080b612db45a9ca76caf6180c58ad04ec4eb6f8e2210e667a9ceec5228d7a9854a77fca67bf86d0660b655c7715bd71875e4d4a4d7
6
+ metadata.gz: 244c24b44ab251e6a71fe9e4200062b8d6c3679effb03c902759f3ef389d87cf87f8cf7fcf5c48c15a0c4358fc0ee750964b03c17eccd2f3af7cba19a0282ba8
7
+ data.tar.gz: 9225e1067d918fccd2dbef1c661e5128299579bd05fe4b52738bcb1ca2cc90c7495cbd07a556661000654ff0e4f6fd3e76d8049d44e3748f759e39566f89ffeb
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class ActiveStorageAttachmentsController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
7
+ load_and_authorize_resource :attachment, class: 'ActiveStorage::Attachment', parent: false
8
+
9
+ def create
10
+ if @attachment.record.respond_to?("#{@attachment.name}_attachment=") || @attachment.record.respond_to?("#{@attachment.name}_attachments=")
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
+ )
15
+
16
+ head :ok
17
+ else
18
+ head :unprocessable_entity
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def attachment_params
25
+ params.require(:data).except(:file).permit!
26
+ end
27
+ end
28
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class AlertsController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
5
7
  load_and_authorize_resource :alert, only: %i[index show update destroy]
6
8
 
7
9
  before_action :build_alert, only: :create
@@ -9,19 +9,31 @@ module Motor
9
9
  'text/css'
10
10
  ].freeze
11
11
 
12
+ MIME_TYPES = {
13
+ '.js' => 'application/javascript',
14
+ '.css' => 'text/css',
15
+ '.woff2' => 'font/woff2'
16
+ }.freeze
17
+
12
18
  def show
13
19
  filename = params[:filename]
14
20
 
15
21
  return [404, {}, ''] unless Motor::Assets.manifest.values.include?(filename)
16
22
 
23
+ assign_headers(filename)
24
+
17
25
  self.response_body = CACHE_STORE.fetch(filename) do
18
- Motor::Assets.load_asset(filename)
26
+ Motor::Assets.load_asset(filename, gzip: headers['Content-Encoding'] == 'gzip')
19
27
  end
28
+ end
20
29
 
21
- headers['Content-Type'] = Marcel::MimeType.for(name: filename)
30
+ private
22
31
 
23
- headers['Content-Encoding'] = 'gzip' if !Motor.development? && GZIP_TYPES.include?(headers['Content-Type'])
32
+ def assign_headers(filename)
33
+ content_type = MIME_TYPES[File.extname(filename)]
24
34
 
35
+ headers['Content-Type'] = content_type
36
+ headers['Content-Encoding'] = 'gzip' if !Motor.development? && GZIP_TYPES.include?(content_type)
25
37
  headers['Cache-Control'] = 'max-age=31536000'
26
38
  end
27
39
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class ConfigsController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
5
7
  load_and_authorize_resource
6
8
 
7
9
  def index
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class DashboardsController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
5
7
  load_and_authorize_resource :dashboard, only: %i[index show update destroy]
6
8
 
7
9
  before_action :build_dashboard, only: :create
@@ -4,8 +4,11 @@ module Motor
4
4
  class DataController < ApiBaseController
5
5
  INSTANCE_VARIABLE_NAME = 'resource'
6
6
 
7
+ wrap_parameters :data, except: %i[include fields]
8
+
7
9
  before_action :load_and_authorize_resource
8
10
  before_action :load_and_authorize_association
11
+ before_action :wrap_io_params
9
12
 
10
13
  def index
11
14
  @resources = Motor::ApiQuery.call(@resources, params)
@@ -96,7 +99,23 @@ module Motor
96
99
  end
97
100
 
98
101
  def resource_params
99
- params.fetch(:data, {}).except(resource_class.primary_key).permit!
102
+ if params[:data].present?
103
+ params.require(:data).except(resource_class.primary_key).permit!
104
+ else
105
+ {}
106
+ end
107
+ 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
100
119
  end
101
120
  end
102
121
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class FormsController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
5
7
  load_and_authorize_resource :form, only: %i[index show update destroy]
6
8
 
7
9
  before_action :build_form, only: :create
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class QueriesController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
5
7
  load_and_authorize_resource :query, only: %i[index show update destroy]
6
8
 
7
9
  before_action :build_query, only: :create
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class ResourcesController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
5
7
  load_and_authorize_resource
6
8
 
7
9
  def index
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class RunQueriesController < ApiBaseController
5
+ wrap_parameters :data
6
+
5
7
  load_and_authorize_resource :query, only: :show, parent: false
6
8
 
7
9
  before_action :build_query, only: :create
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class SendAlertsController < ApiBaseController
5
+ wrap_parameters :data
6
+
5
7
  before_action :build_alert, only: :create
6
8
  authorize_resource :alert, only: :create
7
9
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Motor
4
- class Alert < ApplicationRecord
4
+ class Alert < ::Motor::ApplicationRecord
5
5
  belongs_to :query
6
6
  belongs_to :author, polymorphic: true, optional: true
7
7
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Motor
4
- class AlertLock < ApplicationRecord
4
+ class AlertLock < ::Motor::ApplicationRecord
5
5
  belongs_to :alert
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Motor
4
- class Config < ApplicationRecord
4
+ class Config < ::Motor::ApplicationRecord
5
5
  serialize :value, HashSerializer
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Motor
4
- class Dashboard < ApplicationRecord
4
+ class Dashboard < ::Motor::ApplicationRecord
5
5
  belongs_to :author, polymorphic: true, optional: true
6
6
 
7
7
  has_many :taggable_tags, as: :taggable
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Motor
4
- class Form < ApplicationRecord
4
+ class Form < ::Motor::ApplicationRecord
5
5
  belongs_to :author, polymorphic: true, optional: true
6
6
 
7
7
  has_many :taggable_tags, as: :taggable
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Motor
4
- class Query < ApplicationRecord
4
+ class Query < ::Motor::ApplicationRecord
5
5
  belongs_to :author, polymorphic: true, optional: true
6
6
 
7
7
  has_many :taggable_tags, as: :taggable
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Motor
4
- class Resource < ApplicationRecord
4
+ class Resource < ::Motor::ApplicationRecord
5
5
  serialize :preferences, HashSerializer
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Motor
4
- class Tag < ApplicationRecord
4
+ class Tag < ::Motor::ApplicationRecord
5
5
  has_many :taggable_tags
6
6
  end
7
7
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Motor
4
- class TaggableTag < ApplicationRecord
4
+ class TaggableTag < ::Motor::ApplicationRecord
5
5
  belongs_to :tag
6
6
  belongs_to :taggable, polymorphic: true
7
7
  end
data/config/routes.rb CHANGED
@@ -13,6 +13,7 @@ Motor::Admin.routes.draw do
13
13
  resources :dashboards, only: %i[index show create update destroy]
14
14
  resources :forms, only: %i[index show create update destroy]
15
15
  resources :alerts, only: %i[index show create update destroy]
16
+ resources :active_storage_attachments, only: %i[create], path: 'data/active_storage__attachments'
16
17
  resource :schema, only: %i[show update]
17
18
  resources :resources, path: '/data/:resource',
18
19
  only: %i[index show update create destroy],
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 'base64'
9
10
 
10
11
  module Motor
11
12
  PATH = Pathname.new(__dir__)
@@ -2,6 +2,8 @@
2
2
 
3
3
  require_relative './active_record_utils/types'
4
4
  require_relative './active_record_utils/fetch_methods'
5
+ require_relative './active_record_utils/defined_scopes_extension'
6
+ require_relative './active_record_utils/active_storage_links_extension'
5
7
 
6
8
  module ActiveRecordUtils
7
9
  end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module ActiveRecordUtils
5
+ module ActiveStorageLinksExtension
6
+ def path
7
+ Rails.application.routes.url_helpers.rails_blob_path(self)
8
+ end
9
+
10
+ def url
11
+ Rails.application.routes.url_helpers.url_for(self)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module ActiveRecordUtils
5
+ module DefinedScopesExtension
6
+ def scope(name, _body)
7
+ (@__scopes__ ||= []) << name
8
+
9
+ super
10
+ end
11
+
12
+ def defined_scopes
13
+ @__scopes__ || []
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ ActiveRecord::Base.extend(Motor::ActiveRecordUtils::DefinedScopesExtension)
data/lib/motor/admin.rb CHANGED
@@ -2,11 +2,22 @@
2
2
 
3
3
  module Motor
4
4
  class Admin < ::Rails::Engine
5
+ initializer 'motor.filter_params' do
6
+ Rails.application.config.filter_parameters += %i[io]
7
+ end
8
+
5
9
  initializer 'motor.alerts.scheduler' do
6
- next if defined?(Sidekiq) && Sidekiq.server?
10
+ config.after_initialize do |_app|
11
+ next unless defined?(Rails::Server)
12
+
13
+ Motor::Alerts::Scheduler::SCHEDULER_TASK.execute
14
+ end
15
+ end
7
16
 
8
- Motor::Alerts::Scheduler::SCHEDULER_TASK.execute
9
- Motor::Alerts::ScheduledAlertsCache::UPDATE_ALERTS_TASK.execute
17
+ initializer 'motor.active_storage.extensions' do
18
+ ActiveSupport.on_load(:active_storage_attachment) do
19
+ ActiveStorage::Attachment.include(Motor::ActiveRecordUtils::ActiveStorageLinksExtension)
20
+ end
10
21
  end
11
22
  end
12
23
  end
@@ -44,10 +44,9 @@ module Motor
44
44
  end
45
45
 
46
46
  def update_from_params!(alert, params)
47
- raise NameAlreadyExists if name_already_exists?(alert)
48
-
49
47
  alert = assign_attributes(alert, params)
50
48
 
49
+ raise NameAlreadyExists if name_already_exists?(alert)
51
50
  raise InvalidInterval unless alert.cron
52
51
 
53
52
  ApplicationRecord.transaction do
@@ -3,10 +3,6 @@
3
3
  module Motor
4
4
  module Alerts
5
5
  module ScheduledAlertsCache
6
- UPDATE_ALERTS_TASK = Concurrent::TimerTask.new(
7
- execution_interval: 2.minutes
8
- ) { Motor::Alerts::ScheduledAlertsCache.load_alerts }
9
-
10
6
  CACHE_STORE = ActiveSupport::Cache::MemoryStore.new(size: 5.megabytes)
11
7
 
12
8
  module_function
@@ -4,6 +4,7 @@ require_relative './api_query/sort'
4
4
  require_relative './api_query/paginate'
5
5
  require_relative './api_query/filter'
6
6
  require_relative './api_query/search'
7
+ require_relative './api_query/apply_scope'
7
8
  require_relative './api_query/build_meta'
8
9
  require_relative './api_query/build_json'
9
10
 
@@ -15,6 +16,7 @@ module Motor
15
16
  rel = ApiQuery::Sort.call(rel, params[:sort])
16
17
  rel = ApiQuery::Paginate.call(rel, params[:page])
17
18
  rel = ApiQuery::Filter.call(rel, params[:filter])
19
+ rel = ApiQuery::ApplyScope.call(rel, params[:scope])
18
20
 
19
21
  ApiQuery::Search.call(rel, params[:q] || params[:search] || params[:query])
20
22
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module ApiQuery
5
+ module ApplyScope
6
+ module_function
7
+
8
+ def call(rel, scope)
9
+ return rel if scope.blank?
10
+
11
+ scope_symbol = scope.to_sym
12
+
13
+ if rel.klass.defined_scopes.include?(scope_symbol)
14
+ rel.public_send(scope_symbol)
15
+ else
16
+ configs = Motor::Resource.find_by_name(rel.klass.name.underscore)
17
+ scope_configs = configs.preferences[:scopes].find { |s| s[:name] == scope }
18
+
19
+ return rel unless scope_configs
20
+
21
+ ApiQuery::Filter.call(rel, scope_configs[:preferences][:filter])
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end