motor-admin-cstham8 0.4.35

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 (167) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +661 -0
  3. data/README.md +230 -0
  4. data/Rakefile +11 -0
  5. data/app/channels/motor/application_cable/channel.rb +14 -0
  6. data/app/channels/motor/application_cable/connection.rb +27 -0
  7. data/app/channels/motor/notes_channel.rb +9 -0
  8. data/app/channels/motor/notifications_channel.rb +9 -0
  9. data/app/controllers/concerns/motor/current_ability.rb +21 -0
  10. data/app/controllers/concerns/motor/current_user_method.rb +18 -0
  11. data/app/controllers/concerns/motor/load_and_authorize_dynamic_resource.rb +73 -0
  12. data/app/controllers/concerns/motor/wrap_io_params.rb +25 -0
  13. data/app/controllers/motor/active_storage_attachments_controller.rb +64 -0
  14. data/app/controllers/motor/alerts_controller.rb +82 -0
  15. data/app/controllers/motor/api_base_controller.rb +33 -0
  16. data/app/controllers/motor/api_configs_controller.rb +54 -0
  17. data/app/controllers/motor/application_controller.rb +8 -0
  18. data/app/controllers/motor/assets_controller.rb +43 -0
  19. data/app/controllers/motor/audits_controller.rb +16 -0
  20. data/app/controllers/motor/auth_tokens_controller.rb +36 -0
  21. data/app/controllers/motor/configs_controller.rb +33 -0
  22. data/app/controllers/motor/dashboards_controller.rb +64 -0
  23. data/app/controllers/motor/data_controller.rb +88 -0
  24. data/app/controllers/motor/forms_controller.rb +61 -0
  25. data/app/controllers/motor/icons_controller.rb +22 -0
  26. data/app/controllers/motor/note_tags_controller.rb +13 -0
  27. data/app/controllers/motor/notes_controller.rb +58 -0
  28. data/app/controllers/motor/notifications_controller.rb +33 -0
  29. data/app/controllers/motor/queries_controller.rb +64 -0
  30. data/app/controllers/motor/reminders_controller.rb +38 -0
  31. data/app/controllers/motor/resource_default_queries_controller.rb +23 -0
  32. data/app/controllers/motor/resource_methods_controller.rb +23 -0
  33. data/app/controllers/motor/resources_controller.rb +26 -0
  34. data/app/controllers/motor/run_api_requests_controller.rb +56 -0
  35. data/app/controllers/motor/run_graphql_requests_controller.rb +48 -0
  36. data/app/controllers/motor/run_queries_controller.rb +77 -0
  37. data/app/controllers/motor/schema_controller.rb +31 -0
  38. data/app/controllers/motor/send_alerts_controller.rb +26 -0
  39. data/app/controllers/motor/sessions_controller.rb +23 -0
  40. data/app/controllers/motor/slack_conversations_controller.rb +11 -0
  41. data/app/controllers/motor/tags_controller.rb +11 -0
  42. data/app/controllers/motor/ui_controller.rb +51 -0
  43. data/app/controllers/motor/users_for_autocomplete_controller.rb +23 -0
  44. data/app/jobs/motor/alert_sending_job.rb +13 -0
  45. data/app/jobs/motor/application_job.rb +6 -0
  46. data/app/jobs/motor/notify_note_mentions_job.rb +9 -0
  47. data/app/jobs/motor/notify_reminder_job.rb +9 -0
  48. data/app/mailers/motor/alerts_mailer.rb +39 -0
  49. data/app/mailers/motor/application_mailer.rb +33 -0
  50. data/app/mailers/motor/notifications_mailer.rb +33 -0
  51. data/app/models/motor/alert.rb +30 -0
  52. data/app/models/motor/alert_lock.rb +7 -0
  53. data/app/models/motor/api_config.rb +28 -0
  54. data/app/models/motor/application_record.rb +18 -0
  55. data/app/models/motor/audit.rb +13 -0
  56. data/app/models/motor/config.rb +13 -0
  57. data/app/models/motor/dashboard.rb +26 -0
  58. data/app/models/motor/form.rb +23 -0
  59. data/app/models/motor/note.rb +18 -0
  60. data/app/models/motor/note_tag.rb +7 -0
  61. data/app/models/motor/note_tag_tag.rb +8 -0
  62. data/app/models/motor/notification.rb +14 -0
  63. data/app/models/motor/query.rb +33 -0
  64. data/app/models/motor/reminder.rb +13 -0
  65. data/app/models/motor/resource.rb +15 -0
  66. data/app/models/motor/tag.rb +7 -0
  67. data/app/models/motor/taggable_tag.rb +8 -0
  68. data/app/views/layouts/motor/application.html.erb +17 -0
  69. data/app/views/layouts/motor/mailer.html.erb +72 -0
  70. data/app/views/motor/alerts_mailer/alert_email.html.erb +54 -0
  71. data/app/views/motor/notifications_mailer/notify_mention_email.html.erb +28 -0
  72. data/app/views/motor/notifications_mailer/notify_reminder_email.html.erb +28 -0
  73. data/app/views/motor/ui/show.html.erb +1 -0
  74. data/config/locales/el.yml +420 -0
  75. data/config/locales/en.yml +340 -0
  76. data/config/locales/es.yml +420 -0
  77. data/config/locales/ja.yml +340 -0
  78. data/config/locales/pt.yml +416 -0
  79. data/config/routes.rb +65 -0
  80. data/lib/generators/motor/install_generator.rb +24 -0
  81. data/lib/generators/motor/install_notes_generator.rb +22 -0
  82. data/lib/generators/motor/migration.rb +17 -0
  83. data/lib/generators/motor/templates/install.rb +271 -0
  84. data/lib/generators/motor/templates/install_api_configs.rb +86 -0
  85. data/lib/generators/motor/templates/install_notes.rb +83 -0
  86. data/lib/generators/motor/templates/upgrade_motor_api_actions.rb +71 -0
  87. data/lib/generators/motor/upgrade_generator.rb +43 -0
  88. data/lib/motor/active_record_utils/action_text_attribute_patch.rb +19 -0
  89. data/lib/motor/active_record_utils/active_record_connection_column_patch.rb +14 -0
  90. data/lib/motor/active_record_utils/active_record_filter.rb +405 -0
  91. data/lib/motor/active_record_utils/active_storage_blob_patch.rb +30 -0
  92. data/lib/motor/active_record_utils/active_storage_links_extension.rb +11 -0
  93. data/lib/motor/active_record_utils/defined_scopes_extension.rb +25 -0
  94. data/lib/motor/active_record_utils/fetch_methods.rb +24 -0
  95. data/lib/motor/active_record_utils/types.rb +64 -0
  96. data/lib/motor/active_record_utils.rb +45 -0
  97. data/lib/motor/admin.rb +141 -0
  98. data/lib/motor/alerts/persistance.rb +97 -0
  99. data/lib/motor/alerts/scheduled_alerts_cache.rb +29 -0
  100. data/lib/motor/alerts/scheduler.rb +30 -0
  101. data/lib/motor/alerts/slack_sender.rb +74 -0
  102. data/lib/motor/alerts.rb +52 -0
  103. data/lib/motor/api_configs.rb +41 -0
  104. data/lib/motor/api_query/apply_scope.rb +44 -0
  105. data/lib/motor/api_query/build_json.rb +171 -0
  106. data/lib/motor/api_query/build_meta.rb +20 -0
  107. data/lib/motor/api_query/filter.rb +125 -0
  108. data/lib/motor/api_query/paginate.rb +19 -0
  109. data/lib/motor/api_query/search.rb +60 -0
  110. data/lib/motor/api_query/sort.rb +64 -0
  111. data/lib/motor/api_query.rb +24 -0
  112. data/lib/motor/assets.rb +62 -0
  113. data/lib/motor/build_schema/active_storage_attachment_schema.rb +125 -0
  114. data/lib/motor/build_schema/adjust_devise_model_schema.rb +60 -0
  115. data/lib/motor/build_schema/apply_permissions.rb +64 -0
  116. data/lib/motor/build_schema/defaults.rb +66 -0
  117. data/lib/motor/build_schema/find_display_column.rb +65 -0
  118. data/lib/motor/build_schema/find_icon.rb +135 -0
  119. data/lib/motor/build_schema/find_searchable_columns.rb +33 -0
  120. data/lib/motor/build_schema/load_from_rails.rb +361 -0
  121. data/lib/motor/build_schema/merge_schema_configs.rb +157 -0
  122. data/lib/motor/build_schema/reorder_schema.rb +88 -0
  123. data/lib/motor/build_schema/utils.rb +31 -0
  124. data/lib/motor/build_schema.rb +125 -0
  125. data/lib/motor/cancan_utils/ability_patch.rb +31 -0
  126. data/lib/motor/cancan_utils/can_manage_all.rb +14 -0
  127. data/lib/motor/cancan_utils.rb +9 -0
  128. data/lib/motor/configs/build_configs_hash.rb +90 -0
  129. data/lib/motor/configs/build_ui_app_tag.rb +177 -0
  130. data/lib/motor/configs/load_from_cache.rb +110 -0
  131. data/lib/motor/configs/sync_from_file.rb +35 -0
  132. data/lib/motor/configs/sync_from_hash.rb +159 -0
  133. data/lib/motor/configs/sync_middleware.rb +72 -0
  134. data/lib/motor/configs/sync_with_remote.rb +47 -0
  135. data/lib/motor/configs/write_to_file.rb +36 -0
  136. data/lib/motor/configs.rb +39 -0
  137. data/lib/motor/dashboards/persistance.rb +73 -0
  138. data/lib/motor/dashboards.rb +8 -0
  139. data/lib/motor/forms/persistance.rb +93 -0
  140. data/lib/motor/forms.rb +8 -0
  141. data/lib/motor/hash_serializer.rb +21 -0
  142. data/lib/motor/net_http_utils.rb +50 -0
  143. data/lib/motor/notes/notify_mentions.rb +71 -0
  144. data/lib/motor/notes/notify_reminder.rb +48 -0
  145. data/lib/motor/notes/persist.rb +36 -0
  146. data/lib/motor/notes/reminders_scheduler.rb +39 -0
  147. data/lib/motor/notes/tags.rb +34 -0
  148. data/lib/motor/notes.rb +12 -0
  149. data/lib/motor/queries/persistance.rb +90 -0
  150. data/lib/motor/queries/postgresql_exec_query.rb +28 -0
  151. data/lib/motor/queries/render_sql_template.rb +61 -0
  152. data/lib/motor/queries/run_query.rb +289 -0
  153. data/lib/motor/queries.rb +11 -0
  154. data/lib/motor/railtie.rb +11 -0
  155. data/lib/motor/resources/custom_sql_columns_cache.rb +17 -0
  156. data/lib/motor/resources/fetch_configured_model.rb +269 -0
  157. data/lib/motor/resources/persist_configs.rb +232 -0
  158. data/lib/motor/resources.rb +19 -0
  159. data/lib/motor/slack/client.rb +62 -0
  160. data/lib/motor/slack.rb +16 -0
  161. data/lib/motor/tags.rb +32 -0
  162. data/lib/motor/tasks/motor.rake +54 -0
  163. data/lib/motor/version.rb +5 -0
  164. data/lib/motor-admin-cstham8.rb +3 -0
  165. data/lib/motor.rb +87 -0
  166. data/ui/dist/manifest.json +1990 -0
  167. metadata +303 -0
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class AssetsController < ActionController::Metal
5
+ CACHE_STORE = ActiveSupport::Cache::MemoryStore.new
6
+
7
+ GZIP_TYPES = [
8
+ 'application/javascript',
9
+ 'text/css',
10
+ 'image/svg+xml'
11
+ ].freeze
12
+
13
+ MIME_TYPES = {
14
+ '.js' => 'application/javascript',
15
+ '.css' => 'text/css',
16
+ '.svg' => 'image/svg+xml',
17
+ '.png' => 'image/png',
18
+ '.woff2' => 'font/woff2'
19
+ }.freeze
20
+
21
+ def show
22
+ filename = params[:filename]
23
+
24
+ return [404, {}, ''] unless Motor::Assets.manifest.value?(filename)
25
+
26
+ assign_headers(filename)
27
+
28
+ self.response_body = CACHE_STORE.fetch(filename) do
29
+ Motor::Assets.load_asset(filename, gzip: headers['Content-Encoding'] == 'gzip')
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def assign_headers(filename)
36
+ content_type = MIME_TYPES[File.extname(filename)]
37
+
38
+ headers['Content-Type'] = content_type
39
+ headers['Content-Encoding'] = 'gzip' if !Motor.development? && GZIP_TYPES.include?(content_type)
40
+ headers['Cache-Control'] = 'max-age=31536000'
41
+ end
42
+ end
43
+ 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, current_ability),
12
+ meta: Motor::ApiQuery::BuildMeta.call(audits, params)
13
+ }
14
+ end
15
+ end
16
+ 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.credentials.secret_key_base)
32
+
33
+ render json: { token: token }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class ConfigsController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
7
+ load_and_authorize_resource
8
+
9
+ def index
10
+ render json: { data: Motor::ApiQuery::BuildJson.call(@configs, params, current_ability) }
11
+ end
12
+
13
+ def create
14
+ @config =
15
+ Motor::Config.find_or_initialize_by(key: @config.key).tap do |config|
16
+ config.value = @config.value
17
+ end
18
+
19
+ @config.save!
20
+ Motor::Configs::WriteToFile.call
21
+
22
+ render json: { data: Motor::ApiQuery::BuildJson.call(@config, params, current_ability) }
23
+ rescue ActiveRecord::RecordNotUnique
24
+ retry
25
+ end
26
+
27
+ private
28
+
29
+ def config_params
30
+ params.require(:data).permit!
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class DashboardsController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
7
+ load_and_authorize_resource :dashboard, only: %i[index show update destroy]
8
+
9
+ before_action :build_dashboard, only: :create
10
+ authorize_resource :dashboard, only: :create
11
+
12
+ def index
13
+ render json: { data: Motor::ApiQuery::BuildJson.call(@dashboards.active, params, current_ability) }
14
+ end
15
+
16
+ def show
17
+ render json: { data: Motor::ApiQuery::BuildJson.call(@dashboard, params, current_ability) }
18
+ end
19
+
20
+ def create
21
+ if Motor::Dashboards::Persistance.title_already_exists?(@dashboard)
22
+ render json: { errors: [{ source: 'title', detail: 'Title already exists' }] }, status: :unprocessable_entity
23
+ else
24
+ ApplicationRecord.transaction { @dashboard.save! }
25
+ Motor::Configs::WriteToFile.call
26
+
27
+ render json: { data: Motor::ApiQuery::BuildJson.call(@dashboard, params, current_ability) }
28
+ end
29
+ rescue ActiveRecord::RecordNotUnique
30
+ retry
31
+ end
32
+
33
+ def update
34
+ Motor::Dashboards::Persistance.update_from_params!(@dashboard, dashboard_params)
35
+ Motor::Configs::WriteToFile.call
36
+
37
+ render json: { data: Motor::ApiQuery::BuildJson.call(@dashboard, params, current_ability) }
38
+ rescue Motor::Dashboards::Persistance::TitleAlreadyExists
39
+ render json: { errors: [{ source: 'title', detail: 'Title already exists' }] }, status: :unprocessable_entity
40
+ end
41
+
42
+ def destroy
43
+ @dashboard.update!(deleted_at: Time.current)
44
+
45
+ Motor::Configs::WriteToFile.call
46
+
47
+ head :ok
48
+ end
49
+
50
+ private
51
+
52
+ def build_dashboard
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
58
+ end
59
+
60
+ def dashboard_params
61
+ params.require(:data).permit(:title, :description, preferences: {}, tags: [])
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class DataController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
7
+ include Motor::WrapIoParams
8
+ include Motor::LoadAndAuthorizeDynamicResource
9
+
10
+ def index
11
+ @resources = Motor::ApiQuery.call(@resources, params)
12
+
13
+ if params[:format] == 'csv'
14
+ render plain: Motor::ActiveRecordUtils.generate_csv_for_relation(@resources, reset_limit: true)
15
+ else
16
+ render json: {
17
+ data: Motor::ApiQuery::BuildJson.call(@resources, params, current_ability),
18
+ meta: Motor::ApiQuery::BuildMeta.call(@resources, params)
19
+ }
20
+ end
21
+ end
22
+
23
+ def show
24
+ render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params, current_ability) }
25
+ end
26
+
27
+ def create
28
+ if @associated_resource
29
+ if @resource_class.reflections[params[:association]].through_reflection?
30
+ @associated_resource.save!
31
+
32
+ @resource = @associated_resource
33
+ else
34
+ @resource.public_send(params[:association].to_sym).create!(resource_params) do |resource|
35
+ @resource = resource
36
+ end
37
+ end
38
+ else
39
+ @resource.save!
40
+ end
41
+
42
+ render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params, current_ability) }
43
+ rescue ActiveRecord::RecordInvalid
44
+ render json: { errors: @resource.errors }, status: :unprocessable_entity
45
+ end
46
+
47
+ def update
48
+ @resource.update!(resource_params)
49
+
50
+ render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params, current_ability) }
51
+ rescue ActiveRecord::RecordInvalid
52
+ render json: { errors: @resource.errors }, status: :unprocessable_entity
53
+ end
54
+
55
+ def destroy
56
+ if @resource.respond_to?(:deleted_at)
57
+ @resource.update(deleted_at: Time.current)
58
+ elsif @resource.respond_to?(:archived_at)
59
+ @resource.update(archived_at: Time.current)
60
+ else
61
+ @resource.destroy!
62
+ end
63
+
64
+ head :ok
65
+ end
66
+
67
+ def execute
68
+ resource_preferences = Motor::Resource.find_by(name: @resource.class.name.underscore).preferences
69
+ resource_action = resource_preferences[:actions].find { |a| a.dig(:preferences, :method_name) == params[:method] }
70
+
71
+ authorize!(resource_action[:name].to_sym, @resource)
72
+
73
+ @resource.public_send(params[:method].to_sym)
74
+
75
+ head :ok
76
+ end
77
+
78
+ private
79
+
80
+ def resource_params
81
+ if params[:data].present?
82
+ params.require(:data).permit!
83
+ else
84
+ {}
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class FormsController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
7
+ load_and_authorize_resource :form, only: %i[index show update destroy]
8
+
9
+ before_action :build_form, only: :create
10
+ authorize_resource :form, only: :create
11
+
12
+ def index
13
+ render json: { data: Motor::ApiQuery::BuildJson.call(@forms.active, params, current_ability) }
14
+ end
15
+
16
+ def show
17
+ render json: { data: Motor::ApiQuery::BuildJson.call(@form, params, current_ability) }
18
+ end
19
+
20
+ def create
21
+ if Motor::Forms::Persistance.name_already_exists?(@form)
22
+ render json: { errors: [{ source: 'name', detail: 'Name already exists' }] }, status: :unprocessable_entity
23
+ else
24
+ ApplicationRecord.transaction { @form.save! }
25
+ Motor::Configs::WriteToFile.call
26
+
27
+ render json: { data: Motor::ApiQuery::BuildJson.call(@form, params, current_ability) }
28
+ end
29
+ rescue ActiveRecord::RecordNotUnique
30
+ retry
31
+ end
32
+
33
+ def update
34
+ Motor::Forms::Persistance.update_from_params!(@form, form_params)
35
+ Motor::Configs::WriteToFile.call
36
+
37
+ render json: { data: Motor::ApiQuery::BuildJson.call(@form, params, current_ability) }
38
+ rescue Motor::Forms::Persistance::NameAlreadyExists
39
+ render json: { errors: [{ source: 'name', detail: 'Name already exists' }] }, status: :unprocessable_entity
40
+ end
41
+
42
+ def destroy
43
+ @form.update!(deleted_at: Time.current)
44
+
45
+ Motor::Configs::WriteToFile.call
46
+
47
+ head :ok
48
+ end
49
+
50
+ private
51
+
52
+ def build_form
53
+ @form = Motor::Forms::Persistance.build_from_params(form_params)
54
+ end
55
+
56
+ def form_params
57
+ params.require(:data).permit(:name, :description, :api_path, :http_method,
58
+ :api_config_name, preferences: {}, tags: [])
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class IconsController < ApiBaseController
5
+ skip_authorization_check
6
+
7
+ CACHE_STORE = ActiveSupport::Cache::MemoryStore.new
8
+
9
+ def index
10
+ data = CACHE_STORE.fetch('icons') do
11
+ Motor::Assets.icons.map do |icon|
12
+ svg = Motor::Assets.load_asset(icon, gzip: !Motor.development?)
13
+ svg = ActiveSupport::Gzip.decompress(svg) unless Motor.development?
14
+
15
+ [icon.split('/').last, svg]
16
+ end
17
+ end
18
+
19
+ render json: { data: data }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class NoteTagsController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
7
+ load_and_authorize_resource :note_tag
8
+
9
+ def index
10
+ render json: { data: @note_tags.to_a }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class NotesController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
7
+ load_and_authorize_resource :note, only: %i[index show update destroy]
8
+ before_action :build_note, only: :create
9
+ authorize_resource :note, only: :create
10
+
11
+ def index
12
+ @notes = Motor::ApiQuery.call(@notes.active.order(created_at: :asc), params)
13
+
14
+ render json: { data: Motor::ApiQuery::BuildJson.call(@notes, params, current_ability) }
15
+ end
16
+
17
+ def show
18
+ render json: { data: Motor::ApiQuery::BuildJson.call(@note, params, current_ability) }
19
+ end
20
+
21
+ def create
22
+ Motor::Notes::Persist.call(@note, current_user)
23
+ Motor::NotifyNoteMentionsJob.perform_later(@note, current_user)
24
+
25
+ render json: { data: Motor::ApiQuery::BuildJson.call(@note, params, current_ability) }
26
+ end
27
+
28
+ def update
29
+ @note.assign_attributes(note_params)
30
+
31
+ Motor::Notes::Persist.call(@note, current_user)
32
+ Motor::NotifyNoteMentionsJob.perform_later(@note, current_user)
33
+
34
+ render json: { data: Motor::ApiQuery::BuildJson.call(@note, params, current_ability) }
35
+ end
36
+
37
+ def destroy
38
+ @note.update!(deleted_at: Time.current)
39
+
40
+ Motor::NotesChannel.broadcast_to(
41
+ @note.values_at(:record_type, :record_id).join(':'),
42
+ ['remove', @note.as_json(only: %i[id body created_at updated_at])]
43
+ )
44
+
45
+ head :ok
46
+ end
47
+
48
+ private
49
+
50
+ def build_note
51
+ @note = Motor::Note.new(note_params.merge(author: current_user))
52
+ end
53
+
54
+ def note_params
55
+ params.require(:data).permit(:body, :record_id, :record_type)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class NotificationsController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
7
+ load_and_authorize_resource :notification
8
+
9
+ def index
10
+ @notifications =
11
+ Motor::ApiQuery.call(@notifications.where(recipient: current_user).order(created_at: :desc), params)
12
+
13
+ render json: {
14
+ data: Motor::ApiQuery::BuildJson.call(@notifications, params, current_ability),
15
+ meta: Motor::ApiQuery::BuildMeta.call(@notifications, params)
16
+ }
17
+ end
18
+
19
+ def update
20
+ @notification.update!(notification_params)
21
+
22
+ Motor::NotificationsChannel.broadcast_to(current_user, ['update', @notification.as_json(include: %i[record])])
23
+
24
+ render json: { data: Motor::ApiQuery::BuildJson.call(@notification, params, current_ability) }
25
+ end
26
+
27
+ private
28
+
29
+ def notification_params
30
+ params.require(:data).permit(:status)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class QueriesController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
7
+ load_and_authorize_resource :query, only: %i[index show update destroy]
8
+
9
+ before_action :build_query, only: :create
10
+ authorize_resource :query, only: :create
11
+
12
+ def index
13
+ render json: { data: Motor::ApiQuery::BuildJson.call(@queries.active, params, current_ability) }
14
+ end
15
+
16
+ def show
17
+ render json: { data: Motor::ApiQuery::BuildJson.call(@query, params, current_ability) }
18
+ end
19
+
20
+ def create
21
+ if Motor::Queries::Persistance.name_already_exists?(@query)
22
+ render json: { errors: [{ source: 'name', detail: 'Name already exists' }] }, status: :unprocessable_entity
23
+ else
24
+ ApplicationRecord.transaction do
25
+ Motor::Queries::Persistance.assign_or_create_api_config!(@query)
26
+ @query.save!
27
+ end
28
+
29
+ Motor::Configs::WriteToFile.call
30
+
31
+ render json: { data: Motor::ApiQuery::BuildJson.call(@query, params, current_ability) }
32
+ end
33
+ rescue ActiveRecord::RecordNotUnique
34
+ retry
35
+ end
36
+
37
+ def update
38
+ Motor::Queries::Persistance.update_from_params!(@query, query_params)
39
+ Motor::Configs::WriteToFile.call
40
+
41
+ render json: { data: Motor::ApiQuery::BuildJson.call(@query, params, current_ability) }
42
+ rescue Motor::Queries::Persistance::NameAlreadyExists
43
+ render json: { errors: [{ source: 'name', detail: 'Name already exists' }] }, status: :unprocessable_entity
44
+ end
45
+
46
+ def destroy
47
+ @query.update!(deleted_at: Time.current)
48
+
49
+ Motor::Configs::WriteToFile.call
50
+
51
+ head :ok
52
+ end
53
+
54
+ private
55
+
56
+ def build_query
57
+ @query = Motor::Queries::Persistance.build_from_params(query_params)
58
+ end
59
+
60
+ def query_params
61
+ params.require(:data).permit(:name, :sql_body, :description, preferences: {}, tags: [])
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class RemindersController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
7
+ load_and_authorize_resource :reminder
8
+
9
+ def create
10
+ @reminder.update!(author: current_user, recipient: current_user)
11
+
12
+ broadcast_note_update(@reminder.record)
13
+
14
+ render json: { data: Motor::ApiQuery::BuildJson.call(@reminder, params, current_ability) }
15
+ end
16
+
17
+ def destroy
18
+ @reminder.destroy!
19
+
20
+ broadcast_note_update(@reminder.record)
21
+
22
+ head :ok
23
+ end
24
+
25
+ private
26
+
27
+ def broadcast_note_update(note)
28
+ Motor::NotesChannel.broadcast_to(
29
+ note.values_at(:record_type, :record_id).join(':'),
30
+ ['update', note.as_json(include: %i[author tags reminders])]
31
+ )
32
+ end
33
+
34
+ def reminder_params
35
+ params.require(:data).permit(:record_id, :record_type, :scheduled_at)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class ResourceDefaultQueriesController < ApiBaseController
5
+ skip_authorization_check
6
+
7
+ before_action :authorize_resource
8
+
9
+ def show
10
+ render json: { data: { sql: resource_class.all.to_sql } }
11
+ end
12
+
13
+ private
14
+
15
+ def resource_class
16
+ @resource_class ||= Motor::BuildSchema::Utils.classify_slug(params[:resource])
17
+ end
18
+
19
+ def authorize_resource
20
+ authorize!(resource_class, :manage)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class ResourceMethodsController < ApiBaseController
5
+ skip_authorization_check
6
+
7
+ before_action :authorize_resource
8
+
9
+ def show
10
+ render json: { data: ActiveRecordUtils::FetchMethods.call(resource_class) }
11
+ end
12
+
13
+ private
14
+
15
+ def resource_class
16
+ @resource_class ||= Motor::BuildSchema::Utils.classify_slug(params[:resource])
17
+ end
18
+
19
+ def authorize_resource
20
+ authorize!(resource_class, :manage)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ class ResourcesController < ApiBaseController
5
+ wrap_parameters :data, except: %i[include fields]
6
+
7
+ load_and_authorize_resource
8
+
9
+ def index
10
+ render json: { data: Motor::ApiQuery::BuildJson.call(@resources, params, current_ability) }
11
+ end
12
+
13
+ def create
14
+ Motor::Resources::PersistConfigs.call(@resource)
15
+ Motor::Configs::WriteToFile.call
16
+
17
+ render json: { data: Motor::ApiQuery::BuildJson.call(@resource, params, current_ability) }
18
+ end
19
+
20
+ private
21
+
22
+ def resource_params
23
+ params.require(:data).permit!
24
+ end
25
+ end
26
+ end