motor-admin 0.1.54 → 0.1.60

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -4
  3. data/app/controllers/concerns/motor/current_ability.rb +21 -0
  4. data/app/controllers/concerns/motor/current_user_method.rb +8 -7
  5. data/app/controllers/motor/alerts_controller.rb +4 -4
  6. data/app/controllers/motor/api_base_controller.rb +1 -12
  7. data/app/controllers/motor/application_controller.rb +1 -0
  8. data/app/controllers/motor/audits_controller.rb +1 -1
  9. data/app/controllers/motor/auth_tokens_controller.rb +36 -0
  10. data/app/controllers/motor/configs_controller.rb +2 -2
  11. data/app/controllers/motor/dashboards_controller.rb +8 -4
  12. data/app/controllers/motor/data_controller.rb +9 -4
  13. data/app/controllers/motor/forms_controller.rb +4 -4
  14. data/app/controllers/motor/icons_controller.rb +2 -0
  15. data/app/controllers/motor/queries_controller.rb +4 -4
  16. data/app/controllers/motor/resource_methods_controller.rb +2 -0
  17. data/app/controllers/motor/resources_controller.rb +2 -2
  18. data/app/controllers/motor/run_queries_controller.rb +12 -1
  19. data/app/controllers/motor/ui_controller.rb +1 -1
  20. data/app/views/motor/ui/show.html.erb +1 -1
  21. data/config/routes.rb +1 -0
  22. data/lib/motor.rb +1 -0
  23. data/lib/motor/admin.rb +20 -0
  24. data/lib/motor/api_query/build_json.rb +101 -47
  25. data/lib/motor/api_query/filter.rb +2 -0
  26. data/lib/motor/api_query/search.rb +1 -0
  27. data/lib/motor/assets.rb +10 -1
  28. data/lib/motor/build_schema.rb +3 -1
  29. data/lib/motor/build_schema/active_storage_attachment_schema.rb +1 -0
  30. data/lib/motor/build_schema/apply_permissions.rb +50 -0
  31. data/lib/motor/build_schema/find_display_column.rb +1 -0
  32. data/lib/motor/build_schema/find_icon.rb +5 -1
  33. data/lib/motor/cancan_utils.rb +7 -0
  34. data/lib/motor/cancan_utils/ability_patch.rb +29 -0
  35. data/lib/motor/cancan_utils/can_manage_all.rb +14 -0
  36. data/lib/motor/configs/build_ui_app_tag.rb +26 -16
  37. data/lib/motor/configs/load_from_cache.rb +20 -8
  38. data/lib/motor/queries/run_query.rb +7 -3
  39. data/lib/motor/version.rb +1 -1
  40. data/ui/dist/{main-67fbb3a8f6a6388434c8.css.gz → main-96c4a62d2fb789ab1080.css.gz} +0 -0
  41. data/ui/dist/main-96c4a62d2fb789ab1080.js.gz +0 -0
  42. data/ui/dist/manifest.json +5 -5
  43. metadata +12 -7
  44. data/app/controllers/motor/schemas_controller.rb +0 -11
  45. data/ui/dist/main-67fbb3a8f6a6388434c8.js.gz +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7f9950ae7594d0e67084469151a267b131627fbfe5a2e7954628dd4b1afd5b12
4
- data.tar.gz: cb6f0a797d08007c4e7eeef3b2f501a3b0768807e9ba4e9b36ad9912fe764f93
3
+ metadata.gz: 5d709fb5b88f56a20cb9c2f906f3540b53278e04becc96598556dba70a0dcb4f
4
+ data.tar.gz: 5ffca9ae9451e2aa373957116c1b9b6607f5aba3bc1fa1cee555682c1a2987be
5
5
  SHA512:
6
- metadata.gz: 772e65f663cc7abb9212d31ccfd1dd3fa85a0d890e83e24a6c18a07120cc004a177a5f9efd95f41340e11e211e8ba698d46f0f3dd973579804a9b082eaf0f976
7
- data.tar.gz: 8ec2b114b8689746d0a7f4e7656186c693653e3fcc359771dc82294ffe6a2f0b8e01e624bf9417c9f887a4fa65e8ab6609242a70a2d9bdd345b72f7a05d02347
6
+ metadata.gz: 944babec34990e1889a52e76f652ca725dda31f4bcbb75aefc47cb715beca9a1a6127d6c5aa1e70437beafdf6a0c9cfaaaf3cf29a901524980812e23134cefaa
7
+ data.tar.gz: c79c99d0491e3c02c05296f0ace0ace54bbeeef1cd11f927b49f5edd814503f3e5f0f5a017c184e97bbce9a4b02b7279db36333a8b3fb796fb6125f283e8604a
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # ⚡ Motor Admin
1
+ # ⚡ Motor Admin
2
2
 
3
3
  Low-code Admin panel and Business intelligence Rails engine **(no DSL - configurable from the UI)**.
4
4
 
5
- 🤓 [Demo App](https://motor-admin.herokuapp.com/demo) | [Features overview](https://www.youtube.com/watch?v=ngVoci8Hll4&list=PLu7llEMh0KcOkR3Uy_RJT0cXPZQKAYVsq&index=1)
5
+ 🤓 [Demo App](https://motor-admin.herokuapp.com/demo) | 👀 [Features overview](https://www.youtube.com/watch?v=ngVoci8Hll4&list=PLu7llEMh0KcOkR3Uy_RJT0cXPZQKAYVsq&index=1) | ⭐ [Stargathers](https://github.com/omohokcoj/motor-admin/stargazers)
6
6
 
7
7
  [![Admin Panel](https://user-images.githubusercontent.com/5418788/119318538-1f30e300-bc82-11eb-94a4-107c31c93b13.png)](https://motor-admin.herokuapp.com/demo)
8
8
 
@@ -32,6 +32,7 @@ $ rails motor:install && rake db:migrate
32
32
  * [Data visualization](#data-visualization)
33
33
  * [Dashboards](#dashboards)
34
34
  * [Email alerts](#email-alerts)
35
+ * [Authorization](#authorization)
35
36
  * [Intelligence search](#intelligence-search)
36
37
  * [Optimized for mobile](#optimized-for-mobile)
37
38
  * [Configurations sync between environments](#configurations-sync)
@@ -90,6 +91,9 @@ Sender address can be specified using `MOTOR_ALERTS_FROM_ADDRESS` environment va
90
91
 
91
92
  Intelligence search can be opened via the top right corner button or using <kbd>Cmd</kbd> + <kbd>P</kbd> shortcut.
92
93
 
94
+ ### Authorization
95
+
96
+ Motor Admin allows to set row-level and column-level permissions via [cancan](https://github.com/CanCanCommunity/cancancan) gem. Admin UI permissions should be defined in `app/models/motor/ability.rb` file in `Motor::Ability` class. See [Motor Admin guide](https://github.com/omohokcoj/motor-admin/blob/master/guides/defining_permissions.md) and [CanCan documentation](https://github.com/CanCanCommunity/cancancan/blob/develop/docs/Defining-Abilities.md) to learn how to define user permissions.
93
97
 
94
98
  ### Optimized for Mobile
95
99
 
@@ -141,8 +145,6 @@ MOTOR_DEVELOPMENT=true rails s
141
145
 
142
146
  ## Comming Soon
143
147
 
144
- * User groups
145
- * Row-level permissions
146
148
  * Multiple databases
147
149
  * NoSQL data sources
148
150
  * Pro Bussines intelligence features
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module CurrentAbility
5
+ def current_ability
6
+ @current_ability ||=
7
+ if defined?(Motor::Ability) && current_user
8
+ klass = Motor::Ability.dup.tap do |k|
9
+ k.prepend(Motor::CancanUtils::AbilityPatch)
10
+ end
11
+
12
+ params = [current_user]
13
+ params << request if Motor::Ability.instance_method(:initialize).arity == 2
14
+
15
+ klass.new(*params)
16
+ else
17
+ Motor::CancanUtils::CanManageAll.new
18
+ end
19
+ end
20
+ end
21
+ end
@@ -3,13 +3,14 @@
3
3
  module Motor
4
4
  module CurrentUserMethod
5
5
  def current_user
6
- if defined?(current_admin)
7
- current_admin
8
- elsif defined?(current_admin_user)
9
- current_admin_user
10
- elsif defined?(super)
11
- super
12
- end
6
+ @current_user ||=
7
+ if defined?(current_admin)
8
+ current_admin
9
+ elsif defined?(current_admin_user)
10
+ current_admin_user
11
+ elsif defined?(super)
12
+ super
13
+ end
13
14
  end
14
15
  end
15
16
  end
@@ -10,11 +10,11 @@ module Motor
10
10
  authorize_resource :alert, only: :create
11
11
 
12
12
  def index
13
- render json: { data: Motor::ApiQuery::BuildJson.call(@alerts.active, params) }
13
+ render json: { data: Motor::ApiQuery::BuildJson.call(@alerts.active, params, current_ability) }
14
14
  end
15
15
 
16
16
  def show
17
- render json: { data: Motor::ApiQuery::BuildJson.call(@alert, params) }
17
+ render json: { data: Motor::ApiQuery::BuildJson.call(@alert, params, current_ability) }
18
18
  end
19
19
 
20
20
  def create
@@ -25,7 +25,7 @@ module Motor
25
25
  Motor::Alerts::ScheduledAlertsCache.clear
26
26
  Motor::Configs::WriteToFile.call
27
27
 
28
- render json: { data: Motor::ApiQuery::BuildJson.call(@alert, params) }
28
+ render json: { data: Motor::ApiQuery::BuildJson.call(@alert, params, current_ability) }
29
29
  end
30
30
  rescue Motor::Alerts::Persistance::InvalidInterval
31
31
  invalid_interval_response
@@ -36,7 +36,7 @@ module Motor
36
36
  Motor::Alerts::ScheduledAlertsCache.clear
37
37
  Motor::Configs::WriteToFile.call
38
38
 
39
- render json: { data: Motor::ApiQuery::BuildJson.call(@alert, params) }
39
+ render json: { data: Motor::ApiQuery::BuildJson.call(@alert, params, current_ability) }
40
40
  rescue Motor::Alerts::Persistance::NameAlreadyExists
41
41
  name_already_exists_response
42
42
  rescue Motor::Alerts::Persistance::InvalidInterval
@@ -3,14 +3,7 @@
3
3
  module Motor
4
4
  class ApiBaseController < ActionController::API
5
5
  include Motor::CurrentUserMethod
6
-
7
- class CanCanAbilityManageAll
8
- include CanCan::Ability
9
-
10
- def initialize(_)
11
- can :manage, :all
12
- end
13
- end
6
+ include Motor::CurrentAbility
14
7
 
15
8
  unless Rails.env.test?
16
9
  rescue_from StandardError do |e|
@@ -19,9 +12,5 @@ module Motor
19
12
  render json: { errors: [e.message] }, status: :internal_server_error
20
13
  end
21
14
  end
22
-
23
- def current_ability
24
- CanCanAbilityManageAll.new(current_user)
25
- end
26
15
  end
27
16
  end
@@ -3,5 +3,6 @@
3
3
  module Motor
4
4
  class ApplicationController < ActionController::Base
5
5
  include Motor::CurrentUserMethod
6
+ include Motor::CurrentAbility
6
7
  end
7
8
  end
@@ -8,7 +8,7 @@ module Motor
8
8
  audits = Motor::ApiQuery.call(@audits, params)
9
9
 
10
10
  render json: {
11
- data: Motor::ApiQuery::BuildJson.call(audits, params),
11
+ data: Motor::ApiQuery::BuildJson.call(audits, params, current_ability),
12
12
  meta: Motor::ApiQuery::BuildMeta.call(audits, params)
13
13
  }
14
14
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class AuthTokensController < ApiBaseController
5
+ GENERIC_TOKEN_TTL = 2.hours
6
+
7
+ skip_authorization_check
8
+
9
+ def create
10
+ return render json: {} unless current_user
11
+
12
+ if defined?(Devise::JWT)
13
+ respond_with_devise_jwt
14
+ elsif defined?(JWT)
15
+ respond_with_generic_jwt
16
+ else
17
+ render json: {}
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def respond_with_devise_jwt
24
+ warden.set_user(current_user)
25
+
26
+ render json: { token: request.env['warden-jwt_auth.token'] }
27
+ end
28
+
29
+ def respond_with_generic_jwt
30
+ payload = { uid: current_user.id, exp: GENERIC_TOKEN_TTL.from_now.to_i }
31
+ token = JWT.encode(payload, Rails.application.secrets.secret_key_base)
32
+
33
+ render json: { token: token }
34
+ end
35
+ end
36
+ end
@@ -7,7 +7,7 @@ module Motor
7
7
  load_and_authorize_resource
8
8
 
9
9
  def index
10
- render json: { data: Motor::ApiQuery::BuildJson.call(@configs, params) }
10
+ render json: { data: Motor::ApiQuery::BuildJson.call(@configs, params, current_ability) }
11
11
  end
12
12
 
13
13
  def create
@@ -19,7 +19,7 @@ module Motor
19
19
  @config.save!
20
20
  Motor::Configs::WriteToFile.call
21
21
 
22
- render json: { data: Motor::ApiQuery::BuildJson.call(@config, params) }
22
+ render json: { data: Motor::ApiQuery::BuildJson.call(@config, params, current_ability) }
23
23
  rescue ActiveRecord::RecordNotUnique
24
24
  retry
25
25
  end
@@ -10,11 +10,11 @@ module Motor
10
10
  authorize_resource :dashboard, only: :create
11
11
 
12
12
  def index
13
- render json: { data: Motor::ApiQuery::BuildJson.call(@dashboards.active, params) }
13
+ render json: { data: Motor::ApiQuery::BuildJson.call(@dashboards.active, params, current_ability) }
14
14
  end
15
15
 
16
16
  def show
17
- render json: { data: Motor::ApiQuery::BuildJson.call(@dashboard, params) }
17
+ render json: { data: Motor::ApiQuery::BuildJson.call(@dashboard, params, current_ability) }
18
18
  end
19
19
 
20
20
  def create
@@ -24,7 +24,7 @@ module Motor
24
24
  ApplicationRecord.transaction { @dashboard.save! }
25
25
  Motor::Configs::WriteToFile.call
26
26
 
27
- render json: { data: Motor::ApiQuery::BuildJson.call(@dashboard, params) }
27
+ render json: { data: Motor::ApiQuery::BuildJson.call(@dashboard, params, current_ability) }
28
28
  end
29
29
  rescue ActiveRecord::RecordNotUnique
30
30
  retry
@@ -34,7 +34,7 @@ module Motor
34
34
  Motor::Dashboards::Persistance.update_from_params!(@dashboard, dashboard_params)
35
35
  Motor::Configs::WriteToFile.call
36
36
 
37
- render json: { data: Motor::ApiQuery::BuildJson.call(@dashboard, params) }
37
+ render json: { data: Motor::ApiQuery::BuildJson.call(@dashboard, params, current_ability) }
38
38
  rescue Motor::Dashboards::Persistance::TitleAlreadyExists
39
39
  render json: { errors: [{ source: 'title', detail: 'Title already exists' }] }, status: :unprocessable_entity
40
40
  end
@@ -51,6 +51,10 @@ module Motor
51
51
 
52
52
  def build_dashboard
53
53
  @dashboard = Motor::Dashboards::Persistance.build_from_params(dashboard_params)
54
+
55
+ @dashboard.define_singleton_method(:tags) do
56
+ taggable_tags.map(&:tag)
57
+ end
54
58
  end
55
59
 
56
60
  def dashboard_params
@@ -11,19 +11,19 @@ module Motor
11
11
  @resources = Motor::ApiQuery.call(@resources, params)
12
12
 
13
13
  render json: {
14
- data: Motor::ApiQuery::BuildJson.call(@resources, params),
14
+ data: Motor::ApiQuery::BuildJson.call(@resources, params, current_ability),
15
15
  meta: Motor::ApiQuery::BuildMeta.call(@resources, params)
16
16
  }
17
17
  end
18
18
 
19
19
  def show
20
- render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params) }
20
+ render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params, current_ability) }
21
21
  end
22
22
 
23
23
  def create
24
24
  @resource.save!
25
25
 
26
- render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params) }
26
+ render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params, current_ability) }
27
27
  rescue ActiveRecord::RecordInvalid
28
28
  render json: { errors: @resource.errors }, status: :unprocessable_entity
29
29
  end
@@ -31,7 +31,7 @@ module Motor
31
31
  def update
32
32
  @resource.update!(resource_params)
33
33
 
34
- render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params) }
34
+ render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params, current_ability) }
35
35
  rescue ActiveRecord::RecordInvalid
36
36
  render json: { errors: @resource.errors }, status: :unprocessable_entity
37
37
  end
@@ -47,6 +47,11 @@ module Motor
47
47
  end
48
48
 
49
49
  def execute
50
+ resource_preferences = Motor::Resource.find_by(name: @resource.class.name.underscore).preferences
51
+ resource_action = resource_preferences[:actions].find { |a| a[:preferences][:method_name] == params[:method] }
52
+
53
+ authorize!(resource_action[:name].to_sym, @resource)
54
+
50
55
  @resource.public_send(params[:method].to_sym)
51
56
 
52
57
  head :ok
@@ -10,11 +10,11 @@ module Motor
10
10
  authorize_resource :form, only: :create
11
11
 
12
12
  def index
13
- render json: { data: Motor::ApiQuery::BuildJson.call(@forms.active, params) }
13
+ render json: { data: Motor::ApiQuery::BuildJson.call(@forms.active, params, current_ability) }
14
14
  end
15
15
 
16
16
  def show
17
- render json: { data: Motor::ApiQuery::BuildJson.call(@form, params) }
17
+ render json: { data: Motor::ApiQuery::BuildJson.call(@form, params, current_ability) }
18
18
  end
19
19
 
20
20
  def create
@@ -24,7 +24,7 @@ module Motor
24
24
  ApplicationRecord.transaction { @form.save! }
25
25
  Motor::Configs::WriteToFile.call
26
26
 
27
- render json: { data: Motor::ApiQuery::BuildJson.call(@form, params) }
27
+ render json: { data: Motor::ApiQuery::BuildJson.call(@form, params, current_ability) }
28
28
  end
29
29
  rescue ActiveRecord::RecordNotUnique
30
30
  retry
@@ -34,7 +34,7 @@ module Motor
34
34
  Motor::Forms::Persistance.update_from_params!(@form, form_params)
35
35
  Motor::Configs::WriteToFile.call
36
36
 
37
- render json: { data: Motor::ApiQuery::BuildJson.call(@form, params) }
37
+ render json: { data: Motor::ApiQuery::BuildJson.call(@form, params, current_ability) }
38
38
  rescue Motor::Forms::Persistance::NameAlreadyExists
39
39
  render json: { errors: [{ source: 'name', detail: 'Name already exists' }] }, status: :unprocessable_entity
40
40
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class IconsController < ApiBaseController
5
+ skip_authorization_check
6
+
5
7
  CACHE_STORE = ActiveSupport::Cache::MemoryStore.new
6
8
 
7
9
  def index
@@ -10,11 +10,11 @@ module Motor
10
10
  authorize_resource :query, only: :create
11
11
 
12
12
  def index
13
- render json: { data: Motor::ApiQuery::BuildJson.call(@queries.active, params) }
13
+ render json: { data: Motor::ApiQuery::BuildJson.call(@queries.active, params, current_ability) }
14
14
  end
15
15
 
16
16
  def show
17
- render json: { data: Motor::ApiQuery::BuildJson.call(@query, params) }
17
+ render json: { data: Motor::ApiQuery::BuildJson.call(@query, params, current_ability) }
18
18
  end
19
19
 
20
20
  def create
@@ -24,7 +24,7 @@ module Motor
24
24
  ApplicationRecord.transaction { @query.save! }
25
25
  Motor::Configs::WriteToFile.call
26
26
 
27
- render json: { data: Motor::ApiQuery::BuildJson.call(@query, params) }
27
+ render json: { data: Motor::ApiQuery::BuildJson.call(@query, params, current_ability) }
28
28
  end
29
29
  rescue ActiveRecord::RecordNotUnique
30
30
  retry
@@ -34,7 +34,7 @@ module Motor
34
34
  Motor::Queries::Persistance.update_from_params!(@query, query_params)
35
35
  Motor::Configs::WriteToFile.call
36
36
 
37
- render json: { data: Motor::ApiQuery::BuildJson.call(@query, params) }
37
+ render json: { data: Motor::ApiQuery::BuildJson.call(@query, params, current_ability) }
38
38
  rescue Motor::Queries::Persistance::NameAlreadyExists
39
39
  render json: { errors: [{ source: 'name', detail: 'Name already exists' }] }, status: :unprocessable_entity
40
40
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Motor
4
4
  class ResourceMethodsController < ApiBaseController
5
+ skip_authorization_check
6
+
5
7
  before_action :authorize_resource
6
8
 
7
9
  def show
@@ -7,14 +7,14 @@ module Motor
7
7
  load_and_authorize_resource
8
8
 
9
9
  def index
10
- render json: { data: Motor::ApiQuery::BuildJson.call(@resources, params) }
10
+ render json: { data: Motor::ApiQuery::BuildJson.call(@resources, params, current_ability) }
11
11
  end
12
12
 
13
13
  def create
14
14
  Motor::BuildSchema::PersistResourceConfigs.call(@resource)
15
15
  Motor::Configs::WriteToFile.call
16
16
 
17
- render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params) }
17
+ render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params, current_ability) }
18
18
  end
19
19
 
20
20
  private
@@ -20,7 +20,8 @@ module Motor
20
20
  private
21
21
 
22
22
  def render_result
23
- query_result = Queries::RunQuery.call(@query, variables_hash: params[:variables])
23
+ variables = params.fetch(:variables, {}).merge(current_user_variables)
24
+ query_result = Queries::RunQuery.call(@query, variables_hash: variables)
24
25
 
25
26
  if query_result.error
26
27
  render json: { errors: [{ detail: query_result.error }] }, status: :unprocessable_entity
@@ -29,6 +30,16 @@ module Motor
29
30
  end
30
31
  end
31
32
 
33
+ def current_user_variables
34
+ return {} unless current_user
35
+
36
+ current_user
37
+ .attributes
38
+ .slice('id', 'email')
39
+ .transform_keys { |key| "current_user_#{key}" }
40
+ .compact
41
+ end
42
+
32
43
  def query_result_hash(query_result)
33
44
  {
34
45
  data: query_result.data,
@@ -4,7 +4,7 @@ module Motor
4
4
  class UiController < ApplicationController
5
5
  layout 'motor/application'
6
6
 
7
- helper_method :current_user
7
+ helper_method :current_user, :current_ability
8
8
 
9
9
  def index
10
10
  render_ui
@@ -1 +1 @@
1
- <%= raw(Motor::Configs::BuildUiAppTag.call(current_user)) %>
1
+ <%= raw(Motor::Configs::BuildUiAppTag.call(current_user, current_ability)) %>
data/config/routes.rb CHANGED
@@ -5,6 +5,7 @@ Motor::Admin.routes.draw do
5
5
  scope 'api', as: :api do
6
6
  resources :run_queries, only: %i[show create]
7
7
  resources :send_alerts, only: %i[create]
8
+ resources :auth_tokens, only: %i[create]
8
9
  resources :queries, only: %i[index show create update destroy]
9
10
  resources :tags, only: %i[index]
10
11
  resources :configs, only: %i[index create]
data/lib/motor.rb CHANGED
@@ -53,6 +53,7 @@ require 'motor/version'
53
53
  require 'motor/admin'
54
54
  require 'motor/assets'
55
55
  require 'motor/active_record_utils'
56
+ require 'motor/cancan_utils'
56
57
  require 'motor/build_schema'
57
58
  require 'motor/api_query'
58
59
  require 'motor/tags'
data/lib/motor/admin.rb CHANGED
@@ -65,10 +65,30 @@ module Motor
65
65
  end
66
66
  end
67
67
 
68
+ initializer 'warden.configure.dispatch_requests' do
69
+ next unless defined?(Warden::JWTAuth)
70
+
71
+ config.after_initialize do
72
+ Warden::JWTAuth.configure do |config|
73
+ config.dispatch_requests += [
74
+ ['POST', /\A#{Regexp.escape(Motor::Admin.routes.url_helpers.motor_api_auth_tokens_path)}\z/]
75
+ ]
76
+ end
77
+ end
78
+ end
79
+
68
80
  initializer 'motor.active_storage.extensions' do
69
81
  config.after_initialize do
70
82
  next unless defined?(ActiveStorage::Engine)
71
83
 
84
+ ActiveSupport.on_load(:active_storage_attachment) do
85
+ ActiveStorage::Attachment.include(Motor::ActiveRecordUtils::ActiveStorageLinksExtension)
86
+ end
87
+
88
+ ActiveSupport.on_load(:active_storage_blob) do
89
+ ActiveStorage::Blob.singleton_class.prepend(Motor::ActiveRecordUtils::ActiveStorageBlobPatch)
90
+ end
91
+
72
92
  ActiveStorage::Attachment.include(Motor::ActiveRecordUtils::ActiveStorageLinksExtension)
73
93
  ActiveStorage::Blob.singleton_class.prepend(Motor::ActiveRecordUtils::ActiveStorageBlobPatch)
74
94
  end
@@ -5,83 +5,90 @@ module Motor
5
5
  module BuildJson
6
6
  module_function
7
7
 
8
- def call(rel, params)
9
- rel = rel.none if params.dig(:page, :limit).yield_self { |limit| limit.present? && limit.to_i.zero? }
10
-
8
+ # @param rel [ActiveRecord::Base, ActiveRecord::Relation]
9
+ # @param params [Hash]
10
+ # @param current_ability [CanCan::Ability]
11
+ # @return [Hash]
12
+ def call(rel, params, current_ability = Motor::CancanUtils::CanManageAll.new)
13
+ rel = rel.none if limit_zero_params?(params)
11
14
  rel = rel.preload_associations_lazily if rel.is_a?(ActiveRecord::Relation)
12
15
 
13
- json_params = {}.with_indifferent_access
14
-
15
- assign_include_params(json_params, rel, params)
16
- assign_fields_params(json_params, rel, params)
17
-
18
- rel.as_json(json_params)
19
- end
16
+ model = rel.is_a?(ActiveRecord::Relation) ? rel.klass : rel.class
20
17
 
21
- def assign_include_params(json_params, _rel, api_params)
22
- return if api_params['include'].blank?
18
+ include_hash = build_include_hash(params['include'])
19
+ models_index = build_models_index(model, include_hash)
23
20
 
24
- include_params = api_params['include']
21
+ json_params = normalize_include_params(include_hash)
25
22
 
26
- if include_params.is_a?(String)
27
- include_params =
28
- include_params.split(',').reduce({}) do |accumulator, path|
29
- hash = {}
23
+ assign_fields_params!(json_params, model, params, current_ability, models_index)
30
24
 
31
- path.split('.').reduce(hash) do |acc, part|
32
- acc_hash = {}
33
-
34
- acc[part] = acc_hash
25
+ rel.as_json(json_params.with_indifferent_access)
26
+ end
35
27
 
36
- acc_hash
37
- end
28
+ # @param include_params [Hash]
29
+ # @return [Hash]
30
+ def build_include_hash(include_params)
31
+ return {} if include_params.blank?
38
32
 
39
- accumulator.deep_merge(hash)
40
- end
33
+ if include_params.is_a?(String)
34
+ build_hash_from_string_path(include_params)
35
+ else
36
+ include_params
41
37
  end
42
-
43
- json_params.deep_merge!(normalize_include_params(include_params))
44
38
  end
45
39
 
46
- def assign_fields_params(json_params, rel, params)
40
+ # @param json_params [Hash]
41
+ # @param model [Class<ActiveRecord::Base>]
42
+ # @param params [Hash]
43
+ # @param current_ability [CanCan::Ability]
44
+ # @param models_index [Hash]
45
+ # @return [void]
46
+ def assign_fields_params!(json_params, model, params, current_ability, models_index)
47
47
  return if params[:fields].blank?
48
48
 
49
- model = rel.is_a?(ActiveRecord::Relation) ? rel.klass : rel.class
50
-
51
49
  params[:fields].each do |key, fields|
52
- fields = fields.split(',') if fields.is_a?(String)
53
-
54
- merge_fields_params!(json_params, key, fields, model)
55
- end
56
- end
50
+ fields_model = models_index[key]
57
51
 
58
- def merge_fields_params!(json_params, key, fields, model)
59
- model_name = model.name.underscore
52
+ next unless model
60
53
 
61
- if key == model_name || model_name.split('/').last == key
62
- json_params.merge!(build_fields_hash(model, fields))
63
- else
64
- hash = find_key_in_params(json_params, key)
54
+ fields = fields.split(',') if fields.is_a?(String)
65
55
 
66
- fields_hash = build_fields_hash(model.reflections[key]&.klass, fields)
56
+ fields_hash = fields_model == model ? json_params : find_key_in_params(json_params, key)
67
57
 
68
- hash.merge!(fields_hash)
58
+ fields_hash.merge!(build_fields_hash(fields_model, fields, current_ability))
69
59
  end
70
60
  end
71
61
 
72
- def build_fields_hash(model, fields)
73
- columns = model ? model.columns.map(&:name) : []
62
+ # @param model [Class<ActiveRecord::Base>]
63
+ # @param fields [Hash]
64
+ # @param current_ability [CanCan::Ability]
65
+ # @return [Hash]
66
+ def build_fields_hash(model, fields, current_ability)
67
+ return { 'methods' => fields } unless model
68
+
69
+ column_names = model.column_names.map(&:to_sym)
70
+ instance_methods = model.instance_methods
71
+ permitted_attributes = current_ability.permitted_attributes(:read, model)
72
+ is_permitted_all = column_names == permitted_attributes
73
+
74
74
  fields_hash = { 'only' => [], 'methods' => [] }
75
75
 
76
76
  fields.each_with_object(fields_hash) do |field, acc|
77
- if field.in?(columns)
77
+ field_symbol = field.to_sym
78
+
79
+ next if !is_permitted_all && permitted_attributes.exclude?(field_symbol)
80
+
81
+ if column_names.include?(field_symbol)
78
82
  acc['only'] << field
79
- elsif model.nil? || model.instance_methods.include?(field.to_sym)
83
+ elsif instance_methods.include?(field_symbol)
80
84
  acc['methods'] << field
81
85
  end
82
86
  end
83
87
  end
84
88
 
89
+ # @param params [Hash]
90
+ # @param key [String]
91
+ # @return [Hash]
85
92
  def find_key_in_params(params, key)
86
93
  params = params['include']
87
94
 
@@ -93,6 +100,8 @@ module Motor
93
100
  end
94
101
  end
95
102
 
103
+ # @param params [Hash]
104
+ # @return [Hash]
96
105
  def normalize_include_params(params)
97
106
  case params
98
107
  when Array
@@ -112,6 +121,51 @@ module Motor
112
121
  raise ArgumentError, "Wrong include param type #{params.class}"
113
122
  end
114
123
  end
124
+
125
+ # @param model [Class<ActiveRecord::Base>]
126
+ # @param includes_hash [Hash]
127
+ # @return [Hash]
128
+ def build_models_index(model, includes_hash)
129
+ default_index = {
130
+ model.name.underscore => model,
131
+ model.name.underscore.split('/').last => model
132
+ }
133
+
134
+ includes_hash.reduce(default_index) do |acc, (key, value)|
135
+ reflection = model.reflections[key]
136
+
137
+ next acc unless reflection
138
+ next acc if reflection.polymorphic?
139
+
140
+ acc[key] = reflection.klass
141
+
142
+ acc.merge(build_models_index(reflection.klass, value))
143
+ end
144
+ end
145
+
146
+ # @param string_path [String]
147
+ # @return [Hash]
148
+ def build_hash_from_string_path(string_path)
149
+ string_path.split(',').reduce({}) do |accumulator, path|
150
+ hash = {}
151
+
152
+ path.split('.').reduce(hash) do |acc, part|
153
+ acc_hash = {}
154
+
155
+ acc[part] = acc_hash
156
+
157
+ acc_hash
158
+ end
159
+
160
+ accumulator.deep_merge(hash)
161
+ end
162
+ end
163
+
164
+ # @param params [Hash]
165
+ # @return [Boolean]
166
+ def limit_zero_params?(params)
167
+ params.dig(:page, :limit).yield_self { |limit| limit.present? && limit.to_i.zero? }
168
+ end
115
169
  end
116
170
  end
117
171
  end
@@ -49,6 +49,8 @@ module Motor
49
49
 
50
50
  def normalize_action(action, value)
51
51
  case action
52
+ when 'includes'
53
+ ['contains', value]
52
54
  when 'contains'
53
55
  ['ilike', value.sub(LIKE_FILTER_VALUE_REGEXP, '%\1%')]
54
56
  when 'starts_with'
@@ -57,6 +57,7 @@ module Motor
57
57
  def find_searchable_columns(model)
58
58
  model.columns.map do |column|
59
59
  next unless column.type.in?(COLUMN_TYPES)
60
+ next if column.respond_to?(:array?) && column.array?
60
61
  next if model.validators_on(column.name).any?(ActiveModel::Validations::InclusionValidator)
61
62
 
62
63
  column.name
data/lib/motor/assets.rb CHANGED
@@ -8,10 +8,19 @@ module Motor
8
8
  MANIFEST_PATH = ASSETS_PATH.join('manifest.json')
9
9
  DEV_SERVER_URL = 'http://localhost:9090/'
10
10
 
11
+ CACHE_STORE =
12
+ if Rails.env.production?
13
+ ActiveSupport::Cache::MemoryStore.new(size: 5.megabytes)
14
+ else
15
+ ActiveSupport::Cache::NullStore.new
16
+ end
17
+
11
18
  module_function
12
19
 
13
20
  def manifest
14
- JSON.parse(MANIFEST_PATH.read)
21
+ CACHE_STORE.fetch('manifest') do
22
+ JSON.parse(MANIFEST_PATH.read)
23
+ end
15
24
  end
16
25
 
17
26
  def icons
@@ -58,9 +58,10 @@ module Motor
58
58
 
59
59
  module_function
60
60
 
61
- def call(cache_keys = {})
61
+ def call(cache_keys = {}, current_ability = nil)
62
62
  schema = LoadFromRails.call
63
63
  schema = MergeSchemaConfigs.call(schema, cache_keys)
64
+ schema = ApplyPermissions.call(schema, current_ability) if current_ability
64
65
 
65
66
  ReorderSchema.call(schema, cache_keys)
66
67
  end
@@ -75,4 +76,5 @@ require_relative './build_schema/find_icon'
75
76
  require_relative './build_schema/persist_resource_configs'
76
77
  require_relative './build_schema/reorder_schema'
77
78
  require_relative './build_schema/merge_schema_configs'
79
+ require_relative './build_schema/apply_permissions'
78
80
  require_relative './build_schema/utils'
@@ -5,6 +5,7 @@ module Motor
5
5
  ACTIVE_STORAGE_ATTACHMENT_SCHEMA = {
6
6
  name: 'active_storage/attachment',
7
7
  slug: 'active_storage__attachments',
8
+ class_name: 'ActiveStorage::Attachment',
8
9
  table_name: 'active_storage_attachments',
9
10
  primary_key: 'id',
10
11
  display_name: 'Attachments',
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module BuildSchema
5
+ module ApplyPermissions
6
+ module_function
7
+
8
+ def call(schema, ability)
9
+ schema.map do |model|
10
+ klass = model[:class_name].constantize
11
+
12
+ next unless ability.can?(:read, klass)
13
+
14
+ model[:associations] = filter_associations(model[:associations], ability)
15
+ model[:columns] = filter_columns(klass, model[:columns], ability)
16
+ model[:actions] = filter_actions(klass, model[:actions], ability)
17
+
18
+ model
19
+ end.compact
20
+ end
21
+
22
+ def filter_associations(associations, ability)
23
+ associations.select do |assoc|
24
+ ability.can?(:read, assoc[:model_name].classify.constantize)
25
+ end
26
+ end
27
+
28
+ def filter_columns(model, columns, ability)
29
+ columns.map do |column|
30
+ next unless ability.can?(:read, model, column[:name])
31
+
32
+ next if column.dig(:reference, :model_name).present? &&
33
+ !ability.can?(:read, column[:reference][:model_name].classify.constantize)
34
+
35
+ unless ability.can?(:update, model, column[:name])
36
+ column = column.merge(access_type: BuildSchema::ColumnAccessTypes::READ_ONLY)
37
+ end
38
+
39
+ column
40
+ end.compact
41
+ end
42
+
43
+ def filter_actions(model, actions, ability)
44
+ actions.select do |action|
45
+ ability.can?(action[:name].to_sym, model)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -55,6 +55,7 @@ module Motor
55
55
  def fetch_column_names(model)
56
56
  model.columns.map do |column|
57
57
  next unless column.type.in?(BuildSchema::SEARCHABLE_COLUMN_TYPES)
58
+ next if column.respond_to?(:array?) && column.array?
58
59
 
59
60
  column.name
60
61
  end.compact
@@ -35,6 +35,7 @@ module Motor
35
35
  'token' => 'key',
36
36
  'secret' => 'lock',
37
37
  'automation' => 'manual-gearbox',
38
+ 'workflow' => 'manual-gearbox',
38
39
  'relationship' => 'hierarchy',
39
40
  'person' => 'user',
40
41
  'people' => 'users',
@@ -96,6 +97,8 @@ module Motor
96
97
  'page' => 'brand-pagekit',
97
98
  'date' => 'calendar-event',
98
99
  'customer' => 'users',
100
+ 'client' => 'users',
101
+ 'ticket' => 'ticket',
99
102
  'contact' => 'users',
100
103
  'member' => 'users',
101
104
  'admin' => 'user-check',
@@ -107,7 +110,8 @@ module Motor
107
110
  'product' => 'building-store',
108
111
  'html' => 'code',
109
112
  'stripe' => 'brand-stripe',
110
- 'email' => 'mail'
113
+ 'email' => 'mail',
114
+ 'status' => 'hash'
111
115
  }.freeze
112
116
 
113
117
  DEFAULT_ICON = 'database'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CancanUtils
4
+ end
5
+
6
+ require_relative './cancan_utils/ability_patch'
7
+ require_relative './cancan_utils/can_manage_all'
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module CancanUtils
5
+ module AbilityPatch
6
+ def serialized_rules
7
+ @rules.map do |rule|
8
+ {
9
+ base_behavior: rule.base_behavior,
10
+ actions: expand_actions(rule.actions),
11
+ subjects: rule.subjects.map(&:to_s),
12
+ attributes: rule.attributes,
13
+ conditions: rule.conditions.as_json
14
+ }
15
+ end
16
+ end
17
+
18
+ def rules_hash
19
+ serialized_rules.hash
20
+ end
21
+
22
+ private
23
+
24
+ def default_alias_actions
25
+ super.merge(destroy: %i[remove delete])
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module CancanUtils
5
+ class CanManageAll
6
+ include CanCan::Ability
7
+ prepend CancanUtils::AbilityPatch
8
+
9
+ def initialize(_ = nil)
10
+ can :manage, :all
11
+ end
12
+ end
13
+ end
14
+ end
@@ -12,57 +12,67 @@ module Motor
12
12
 
13
13
  module_function
14
14
 
15
- def call(current_user = nil)
15
+ def call(current_user = nil, current_ability = nil)
16
16
  cache_keys = LoadFromCache.load_cache_keys
17
17
 
18
18
  CACHE_STORE.fetch("#{cache_keys.hash}#{current_user&.id}") do
19
19
  CACHE_STORE.clear
20
20
 
21
- Motor::ApplicationController.helpers.tag.div('', id: 'app', data: build_data(cache_keys, current_user))
21
+ data = build_data(cache_keys, current_user, current_ability)
22
+ Motor::ApplicationController.helpers.tag.div('', id: 'app', data: data)
22
23
  end
23
24
  end
24
25
 
25
26
  # @return [Hash]
26
- def build_data(cache_keys = {}, current_user = nil)
27
+ def build_data(cache_keys = {}, current_user = nil, current_ability = nil)
27
28
  {
28
29
  current_user: current_user&.as_json(only: %i[id email]),
30
+ current_rules: current_ability.serialized_rules,
29
31
  audits_count: Motor::Audit.count,
30
32
  base_path: Motor::Admin.routes.url_helpers.motor_path,
31
- schema: Motor::BuildSchema.call(cache_keys),
33
+ schema: Motor::BuildSchema.call(cache_keys, current_ability),
32
34
  header_links: header_links_data_hash(cache_keys[:configs]),
33
- queries: queries_data_hash(cache_keys[:queries]),
34
- dashboards: dashboards_data_hash(cache_keys[:dashboards]),
35
- alerts: alerts_data_hash(cache_keys[:alerts]),
36
- forms: forms_data_hash(cache_keys[:forms])
35
+ queries: queries_data_hash(build_cache_key(cache_keys, :queries, current_user, current_ability),
36
+ current_ability),
37
+ dashboards: dashboards_data_hash(build_cache_key(cache_keys, :dashboards, current_user, current_ability),
38
+ current_ability),
39
+ alerts: alerts_data_hash(build_cache_key(cache_keys, :alerts, current_user, current_ability),
40
+ current_ability),
41
+ forms: forms_data_hash(build_cache_key(cache_keys, :forms, current_user, current_ability), current_ability)
37
42
  }
38
43
  end
39
44
 
45
+ # @return [String]
46
+ def build_cache_key(cache_keys, key, current_user, current_ability)
47
+ "#{cache_keys[key].hash}#{current_user&.id}#{current_ability&.rules_hash}"
48
+ end
49
+
40
50
  def header_links_data_hash(cache_key = nil)
41
51
  configs = Motor::Configs::LoadFromCache.load_configs(cache_key: cache_key)
42
52
 
43
53
  configs.find { |c| c.key == 'header.links' }&.value || []
44
54
  end
45
55
 
46
- def queries_data_hash(cache_key = nil)
47
- Motor::Configs::LoadFromCache.load_queries(cache_key: cache_key)
56
+ def queries_data_hash(cache_key = nil, current_ability = nil)
57
+ Motor::Configs::LoadFromCache.load_queries(cache_key: cache_key, current_ability: current_ability)
48
58
  .as_json(only: %i[id name updated_at],
49
59
  include: { tags: { only: %i[id name] } })
50
60
  end
51
61
 
52
- def dashboards_data_hash(cache_key = nil)
53
- Motor::Configs::LoadFromCache.load_dashboards(cache_key: cache_key)
62
+ def dashboards_data_hash(cache_key = nil, current_ability = nil)
63
+ Motor::Configs::LoadFromCache.load_dashboards(cache_key: cache_key, current_ability: current_ability)
54
64
  .as_json(only: %i[id title updated_at],
55
65
  include: { tags: { only: %i[id name] } })
56
66
  end
57
67
 
58
- def alerts_data_hash(cache_key = nil)
59
- Motor::Configs::LoadFromCache.load_alerts(cache_key: cache_key)
68
+ def alerts_data_hash(cache_key = nil, current_ability = nil)
69
+ Motor::Configs::LoadFromCache.load_alerts(cache_key: cache_key, current_ability: current_ability)
60
70
  .as_json(only: %i[id name is_enabled updated_at],
61
71
  include: { tags: { only: %i[id name] } })
62
72
  end
63
73
 
64
- def forms_data_hash(cache_key = nil)
65
- Motor::Configs::LoadFromCache.load_forms(cache_key: cache_key)
74
+ def forms_data_hash(cache_key = nil, current_ability = nil)
75
+ Motor::Configs::LoadFromCache.load_forms(cache_key: cache_key, current_ability: current_ability)
66
76
  .as_json(only: %i[id name updated_at],
67
77
  include: { tags: { only: %i[id name] } })
68
78
  end
@@ -32,27 +32,39 @@ module Motor
32
32
  end
33
33
  end
34
34
 
35
- def load_queries(cache_key: nil)
35
+ def load_queries(cache_key: nil, current_ability: nil)
36
36
  maybe_fetch_from_cache('queries', cache_key) do
37
- Motor::Query.all.active.preload(:tags).load
37
+ rel = Motor::Query.all.active.preload(:tags)
38
+ rel = rel.accessible_by(current_ability) if current_ability
39
+
40
+ rel.load
38
41
  end
39
42
  end
40
43
 
41
- def load_dashboards(cache_key: nil)
44
+ def load_dashboards(cache_key: nil, current_ability: nil)
42
45
  maybe_fetch_from_cache('dashboards', cache_key) do
43
- Motor::Dashboard.all.active.preload(:tags).load
46
+ rel = Motor::Dashboard.all.active.preload(:tags)
47
+ rel = rel.accessible_by(current_ability) if current_ability
48
+
49
+ rel.load
44
50
  end
45
51
  end
46
52
 
47
- def load_alerts(cache_key: nil)
53
+ def load_alerts(cache_key: nil, current_ability: nil)
48
54
  maybe_fetch_from_cache('alerts', cache_key) do
49
- Motor::Alert.all.active.preload(:tags).load
55
+ rel = Motor::Alert.all.active.preload(:tags)
56
+ rel = rel.accessible_by(current_ability) if current_ability
57
+
58
+ rel.load
50
59
  end
51
60
  end
52
61
 
53
- def load_forms(cache_key: nil)
62
+ def load_forms(cache_key: nil, current_ability: nil)
54
63
  maybe_fetch_from_cache('forms', cache_key) do
55
- Motor::Form.all.active.preload(:tags).load
64
+ rel = Motor::Form.all.active.preload(:tags)
65
+ rel = rel.accessible_by(current_ability) if current_ability
66
+
67
+ rel.load
56
68
  end
57
69
  end
58
70
 
@@ -16,6 +16,8 @@ module Motor
16
16
 
17
17
  PG_ERROR_REGEXP = /\APG.+ERROR:/.freeze
18
18
 
19
+ RESERVED_VARIABLES = %w[current_user_id current_user_email].freeze
20
+
19
21
  module_function
20
22
 
21
23
  # @param query [Motor::Query]
@@ -110,11 +112,13 @@ module Motor
110
112
  end
111
113
 
112
114
  # @param variable_configs [Array<Hash>]
113
- # @param variable_hash [Hash]
115
+ # @param variables_hash [Hash]
114
116
  # @return [Hash]
115
117
  def merge_variable_default_values(variable_configs, variables_hash)
116
- variable_configs.each_with_object({}) do |variable, acc|
117
- acc[variable[:name]] = variables_hash[variable[:name]] || variable[:default_value]
118
+ variable_configs.each_with_object(variables_hash.slice(*RESERVED_VARIABLES)) do |variable, acc|
119
+ next if RESERVED_VARIABLES.include?(variable[:name])
120
+
121
+ acc[variable[:name]] ||= variables_hash[variable[:name]] || variable[:default_value]
118
122
  end
119
123
  end
120
124
  end
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.54'
4
+ VERSION = '0.1.60'
5
5
  end
@@ -2068,11 +2068,11 @@
2068
2068
  "mail-opened.svg": "icons/mail-opened.svg",
2069
2069
  "mail.svg": "icons/mail.svg",
2070
2070
  "mailbox.svg": "icons/mailbox.svg",
2071
- "main-67fbb3a8f6a6388434c8.css.gz": "main-67fbb3a8f6a6388434c8.css.gz",
2072
- "main-67fbb3a8f6a6388434c8.js.LICENSE.txt": "main-67fbb3a8f6a6388434c8.js.LICENSE.txt",
2073
- "main-67fbb3a8f6a6388434c8.js.gz": "main-67fbb3a8f6a6388434c8.js.gz",
2074
- "main.css": "main-67fbb3a8f6a6388434c8.css",
2075
- "main.js": "main-67fbb3a8f6a6388434c8.js",
2071
+ "main-96c4a62d2fb789ab1080.css.gz": "main-96c4a62d2fb789ab1080.css.gz",
2072
+ "main-96c4a62d2fb789ab1080.js.LICENSE.txt": "main-96c4a62d2fb789ab1080.js.LICENSE.txt",
2073
+ "main-96c4a62d2fb789ab1080.js.gz": "main-96c4a62d2fb789ab1080.js.gz",
2074
+ "main.css": "main-96c4a62d2fb789ab1080.css",
2075
+ "main.js": "main-96c4a62d2fb789ab1080.js",
2076
2076
  "man.svg": "icons/man.svg",
2077
2077
  "manual-gearbox.svg": "icons/manual-gearbox.svg",
2078
2078
  "map-2.svg": "icons/map-2.svg",
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.54
4
+ version: 0.1.60
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-30 00:00:00.000000000 Z
11
+ date: 2021-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord-filter
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '4.9'
47
+ version: '5.0'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '4.9'
54
+ version: '5.0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: cancancan
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -121,6 +121,7 @@ files:
121
121
  - LICENSE
122
122
  - README.md
123
123
  - Rakefile
124
+ - app/controllers/concerns/motor/current_ability.rb
124
125
  - app/controllers/concerns/motor/current_user_method.rb
125
126
  - app/controllers/concerns/motor/load_and_authorize_dynamic_resource.rb
126
127
  - app/controllers/concerns/motor/wrap_io_params.rb
@@ -130,6 +131,7 @@ files:
130
131
  - app/controllers/motor/application_controller.rb
131
132
  - app/controllers/motor/assets_controller.rb
132
133
  - app/controllers/motor/audits_controller.rb
134
+ - app/controllers/motor/auth_tokens_controller.rb
133
135
  - app/controllers/motor/configs_controller.rb
134
136
  - app/controllers/motor/dashboards_controller.rb
135
137
  - app/controllers/motor/data_controller.rb
@@ -139,7 +141,6 @@ files:
139
141
  - app/controllers/motor/resource_methods_controller.rb
140
142
  - app/controllers/motor/resources_controller.rb
141
143
  - app/controllers/motor/run_queries_controller.rb
142
- - app/controllers/motor/schemas_controller.rb
143
144
  - app/controllers/motor/send_alerts_controller.rb
144
145
  - app/controllers/motor/tags_controller.rb
145
146
  - app/controllers/motor/ui_controller.rb
@@ -191,6 +192,7 @@ files:
191
192
  - lib/motor/build_schema.rb
192
193
  - lib/motor/build_schema/active_storage_attachment_schema.rb
193
194
  - lib/motor/build_schema/adjust_devise_model_schema.rb
195
+ - lib/motor/build_schema/apply_permissions.rb
194
196
  - lib/motor/build_schema/find_display_column.rb
195
197
  - lib/motor/build_schema/find_icon.rb
196
198
  - lib/motor/build_schema/load_from_rails.rb
@@ -198,6 +200,9 @@ files:
198
200
  - lib/motor/build_schema/persist_resource_configs.rb
199
201
  - lib/motor/build_schema/reorder_schema.rb
200
202
  - lib/motor/build_schema/utils.rb
203
+ - lib/motor/cancan_utils.rb
204
+ - lib/motor/cancan_utils/ability_patch.rb
205
+ - lib/motor/cancan_utils/can_manage_all.rb
201
206
  - lib/motor/configs.rb
202
207
  - lib/motor/configs/build_configs_hash.rb
203
208
  - lib/motor/configs/build_ui_app_tag.rb
@@ -1485,8 +1490,8 @@ files:
1485
1490
  - ui/dist/icons/zoom-money.svg.gz
1486
1491
  - ui/dist/icons/zoom-out.svg.gz
1487
1492
  - ui/dist/icons/zoom-question.svg.gz
1488
- - ui/dist/main-67fbb3a8f6a6388434c8.css.gz
1489
- - ui/dist/main-67fbb3a8f6a6388434c8.js.gz
1493
+ - ui/dist/main-96c4a62d2fb789ab1080.css.gz
1494
+ - ui/dist/main-96c4a62d2fb789ab1080.js.gz
1490
1495
  - ui/dist/manifest.json
1491
1496
  homepage:
1492
1497
  licenses:
@@ -1,11 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Motor
4
- class SchemasController < ApiBaseController
5
- def show
6
- render json: Motor::BuildSchema.call
7
- end
8
-
9
- def update; end
10
- end
11
- end