motor-admin 0.1.9

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 (84) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +32 -0
  4. data/Rakefile +20 -0
  5. data/app/controllers/motor/alerts_controller.rb +74 -0
  6. data/app/controllers/motor/api_base_controller.rb +25 -0
  7. data/app/controllers/motor/application_controller.rb +6 -0
  8. data/app/controllers/motor/assets_controller.rb +28 -0
  9. data/app/controllers/motor/configs_controller.rb +30 -0
  10. data/app/controllers/motor/dashboards_controller.rb +54 -0
  11. data/app/controllers/motor/data_controller.rb +102 -0
  12. data/app/controllers/motor/forms_controller.rb +54 -0
  13. data/app/controllers/motor/queries_controller.rb +54 -0
  14. data/app/controllers/motor/resource_methods_controller.rb +21 -0
  15. data/app/controllers/motor/resources_controller.rb +23 -0
  16. data/app/controllers/motor/run_queries_controller.rb +45 -0
  17. data/app/controllers/motor/schemas_controller.rb +11 -0
  18. data/app/controllers/motor/send_alerts_controller.rb +24 -0
  19. data/app/controllers/motor/tags_controller.rb +11 -0
  20. data/app/controllers/motor/ui_controller.rb +19 -0
  21. data/app/jobs/motor/alert_sending_job.rb +13 -0
  22. data/app/jobs/motor/application_job.rb +6 -0
  23. data/app/mailers/motor/alerts_mailer.rb +50 -0
  24. data/app/mailers/motor/application_mailer.rb +7 -0
  25. data/app/models/motor/alert.rb +22 -0
  26. data/app/models/motor/alert_lock.rb +7 -0
  27. data/app/models/motor/application_record.rb +8 -0
  28. data/app/models/motor/config.rb +7 -0
  29. data/app/models/motor/dashboard.rb +18 -0
  30. data/app/models/motor/form.rb +14 -0
  31. data/app/models/motor/query.rb +14 -0
  32. data/app/models/motor/resource.rb +7 -0
  33. data/app/models/motor/tag.rb +7 -0
  34. data/app/models/motor/taggable_tag.rb +8 -0
  35. data/app/views/layouts/motor/application.html.erb +14 -0
  36. data/app/views/motor/alerts_mailer/alert_email.html.erb +126 -0
  37. data/app/views/motor/ui/show.html.erb +1 -0
  38. data/config/routes.rb +60 -0
  39. data/lib/generators/motor/install_generator.rb +22 -0
  40. data/lib/generators/motor/migration.rb +17 -0
  41. data/lib/generators/motor/templates/install.rb +135 -0
  42. data/lib/motor-admin.rb +3 -0
  43. data/lib/motor.rb +47 -0
  44. data/lib/motor/active_record_utils.rb +7 -0
  45. data/lib/motor/active_record_utils/fetch_methods.rb +24 -0
  46. data/lib/motor/active_record_utils/types.rb +54 -0
  47. data/lib/motor/admin.rb +12 -0
  48. data/lib/motor/alerts.rb +10 -0
  49. data/lib/motor/alerts/persistance.rb +84 -0
  50. data/lib/motor/alerts/scheduled_alerts_cache.rb +29 -0
  51. data/lib/motor/alerts/scheduler.rb +30 -0
  52. data/lib/motor/api.rb +6 -0
  53. data/lib/motor/api_query.rb +22 -0
  54. data/lib/motor/api_query/build_json.rb +109 -0
  55. data/lib/motor/api_query/build_meta.rb +17 -0
  56. data/lib/motor/api_query/filter.rb +55 -0
  57. data/lib/motor/api_query/paginate.rb +18 -0
  58. data/lib/motor/api_query/search.rb +73 -0
  59. data/lib/motor/api_query/sort.rb +27 -0
  60. data/lib/motor/assets.rb +45 -0
  61. data/lib/motor/build_schema.rb +23 -0
  62. data/lib/motor/build_schema/find_display_column.rb +60 -0
  63. data/lib/motor/build_schema/load_from_rails.rb +176 -0
  64. data/lib/motor/build_schema/merge_schema_configs.rb +77 -0
  65. data/lib/motor/build_schema/persist_resource_configs.rb +208 -0
  66. data/lib/motor/build_schema/reorder_schema.rb +52 -0
  67. data/lib/motor/build_schema/utils.rb +17 -0
  68. data/lib/motor/dashboards.rb +8 -0
  69. data/lib/motor/dashboards/persistance.rb +63 -0
  70. data/lib/motor/forms.rb +8 -0
  71. data/lib/motor/forms/persistance.rb +63 -0
  72. data/lib/motor/hash_serializer.rb +21 -0
  73. data/lib/motor/queries.rb +10 -0
  74. data/lib/motor/queries/persistance.rb +63 -0
  75. data/lib/motor/queries/postgresql_exec_query.rb +28 -0
  76. data/lib/motor/queries/run_query.rb +68 -0
  77. data/lib/motor/tags.rb +31 -0
  78. data/lib/motor/ui_configs.rb +62 -0
  79. data/lib/motor/version.rb +5 -0
  80. data/ui/dist/fonts/ionicons.woff2 +0 -0
  81. data/ui/dist/main-46621a8bdbb789e17c3f.css.gz +0 -0
  82. data/ui/dist/main-46621a8bdbb789e17c3f.js.gz +0 -0
  83. data/ui/dist/manifest.json +13 -0
  84. metadata +237 -0
@@ -0,0 +1,126 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta name="viewport" content="width=device-width">
5
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
6
+ <title><%= @alert.name %></title>
7
+ <style>
8
+ @media only screen and (max-width: 620px) {
9
+ table[class=body] h1 {
10
+ font-size: 28px !important;
11
+ margin-bottom: 10px !important;
12
+ }
13
+ table[class=body] p,
14
+ table[class=body] ul,
15
+ table[class=body] ol,
16
+ table[class=body] td,
17
+ table[class=body] span,
18
+ table[class=body] a {
19
+ font-size: 14px !important;
20
+ }
21
+ table[class=body] .wrapper,
22
+ table[class=body] .article {
23
+ padding: 10px !important;
24
+ }
25
+ table[class=body] .content {
26
+ padding: 0 !important;
27
+ }
28
+ table[class=body] .container {
29
+ padding: 0 !important;
30
+ width: 100% !important;
31
+ }
32
+ table[class=body] .main {
33
+ border-left-width: 0 !important;
34
+ border-radius: 0 !important;
35
+ border-right-width: 0 !important;
36
+ }
37
+ }
38
+
39
+ @media all {
40
+ .ExternalClass {
41
+ width: 100%;
42
+ }
43
+ .ExternalClass,
44
+ .ExternalClass p,
45
+ .ExternalClass span,
46
+ .ExternalClass font,
47
+ .ExternalClass td,
48
+ .ExternalClass div {
49
+ line-height: 100%;
50
+ }
51
+ .apple-link a {
52
+ color: inherit !important;
53
+ font-family: inherit !important;
54
+ font-size: inherit !important;
55
+ font-weight: inherit !important;
56
+ line-height: inherit !important;
57
+ text-decoration: none !important;
58
+ }
59
+ #MessageViewBody a {
60
+ color: inherit;
61
+ text-decoration: none;
62
+ font-size: inherit;
63
+ font-family: inherit;
64
+ font-weight: inherit;
65
+ line-height: inherit;
66
+ }
67
+ }
68
+ </style>
69
+ </head>
70
+ <body class="" style="background-color: #f9fbfc; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
71
+ <table border="0" cellpadding="0" cellspacing="0" class="body" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background-color: #f9fbfc;">
72
+ <tr>
73
+ <td style="font-family: sans-serif; font-size: 14px; vertical-align: top;">&nbsp;</td>
74
+ <td class="container" style="font-family: sans-serif; font-size: 14px; vertical-align: top; display: block; Margin: 0 auto; padding: 10px;">
75
+ <div class="content" style="box-sizing: border-box; display: block; Margin: 0 auto;padding: 10px;">
76
+ <h1 style="margin: 0"><%= @alert.name.presence || @alert.query.name %></h1>
77
+ <% if @query_result.data.length > 1 %>
78
+ <p style="margin: 7px 0; float: left">Showing <%= @query_result.data.first(40).size %> of <%= @query_result.data.size %> rows</p>
79
+ <% end %>
80
+ <a href="<%= Motor::Admin.routes.url_helpers.motor_ui_query_url(@alert.query, { host: 'localhost:3000'}.merge(Rails.application.config.action_mailer.default_url_options || {})) %>" target="blank" style="margin: 7px 0; float: right">Open query </a>
81
+ <table class="main" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%; background: #ffffff; border-radius: 3px;">
82
+ <tr>
83
+ <td class="wrapper" style="font-family: sans-serif; font-size: 14px; vertical-align: top; box-sizing: border-box; padding: 20px; max-width: 0px; overflow: scroll">
84
+ <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: auto;">
85
+ <tbody>
86
+ <tr>
87
+ <td style="font-family: sans-serif; font-size: 14px; overflow: scroll;">
88
+ <div style="oveflow: scroll">
89
+ <table border="0" cellpadding="0" cellspacing="15" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; white-space: nowrap;">
90
+ <tr>
91
+ <% @query_result.columns.each do |column| %>
92
+ <th><%= column[:name] %></th>
93
+ <% end %>
94
+ </tr>
95
+ <% @query_result.data.first(100).each do |row| %>
96
+ <tr>
97
+ <% row.each do |col| %>
98
+ <td><%= col.respond_to?(:strftime) ? l(col, format: :long) : col.to_s.truncate(100) %></th>
99
+ <% end %>
100
+ </tr>
101
+ <% end %>
102
+ </table>
103
+ </div>
104
+ </td>
105
+ </tr>
106
+ </tbody>
107
+ </table>
108
+ </td>
109
+ </tr>
110
+ </table>
111
+ <div class="footer" style="clear: both; Margin-top: 10px; text-align: center; width: 100%;">
112
+ <table border="0" cellpadding="0" cellspacing="0" style="border-collapse: separate; mso-table-lspace: 0pt; mso-table-rspace: 0pt; width: 100%;">
113
+ <tr>
114
+ <td class="content-block powered-by" style="font-family: sans-serif; vertical-align: top; padding-bottom: 10px; padding-top: 10px; font-size: 12px; color: #999999; text-align: center;">
115
+ Sent from MotorAdmin
116
+ </td>
117
+ </tr>
118
+ </table>
119
+ </div>
120
+ </div>
121
+ </td>
122
+ <td style="font-family: sans-serif; font-size: 14px; vertical-align: top;">&nbsp;</td>
123
+ </tr>
124
+ </table>
125
+ </body>
126
+ </html>
@@ -0,0 +1 @@
1
+ <%= raw(Motor::UiConfigs.app_tag) %>
data/config/routes.rb ADDED
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ Motor::Admin.routes.draw do
4
+ namespace :motor, path: '' do
5
+ scope 'api', as: 'api' do
6
+ resources :run_queries, only: %i[show create]
7
+ resources :send_alerts, only: %i[create]
8
+ resources :queries, only: %i[index show create update destroy]
9
+ resources :tags, only: %i[index]
10
+ resources :configs, only: %i[index create]
11
+ resources :resources, only: %i[index create]
12
+ resources :resource_methods, only: %i[show], param: 'resource'
13
+ resources :dashboards, only: %i[index show create update destroy]
14
+ resources :forms, only: %i[index show create update destroy]
15
+ resources :alerts, only: %i[index show create update destroy]
16
+ resource :schema, only: %i[show update]
17
+ resources :resources, path: '/data/:resource',
18
+ only: %i[index show update create destroy],
19
+ controller: 'data' do
20
+ put '/:method', to: 'data#execute'
21
+ resources :association, path: '/:association',
22
+ only: %i[index create],
23
+ controller: 'data'
24
+ end
25
+ end
26
+
27
+ resources :assets, param: 'filename',
28
+ only: :show,
29
+ format: false,
30
+ constraints: { filename: /.+/ }
31
+
32
+ get '/', to: 'ui#show'
33
+
34
+ scope as: 'ui' do
35
+ with_options controller: 'ui' do
36
+ resources :data, only: %i[index show],
37
+ param: 'path',
38
+ constraints: { path: /.+/ }
39
+ resources :reports, only: %i[index show]
40
+ resources :queries, only: %i[index show]
41
+ resources :dashboards, only: %i[index show]
42
+ resources :alerts, only: %i[index show]
43
+ resources :forms, only: %i[index show]
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ Motor::Api.routes.draw do
50
+ namespace :motor, path: '' do
51
+ resources :resources, path: '/:resource',
52
+ only: %i[index show update create destroy],
53
+ controller: 'data' do
54
+ put '/:method', to: 'data#execute'
55
+ resources :association, path: '/:association',
56
+ only: %i[index create],
57
+ controller: 'data'
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/migration'
5
+ require 'active_record'
6
+ require 'rails/generators/active_record'
7
+ require 'generators/motor/migration'
8
+
9
+ module Motor
10
+ module Generators
11
+ class InstallGenerator < Rails::Generators::Base
12
+ include Rails::Generators::Migration
13
+ extend Motor::Generators::Migration
14
+
15
+ source_root File.expand_path('templates', __dir__)
16
+
17
+ def copy_migration
18
+ migration_template 'install.rb', 'db/migrate/install_motor_admin.rb'
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module Generators
5
+ module Migration
6
+ def next_migration_number(dirname)
7
+ next_migration_number = current_migration_number(dirname) + 1
8
+
9
+ if ::ActiveRecord::Base.timestamped_migrations
10
+ [Time.now.utc.strftime('%Y%m%d%H%M%S'), format('%.14d', next_migration_number)].max
11
+ else
12
+ format('%.3d', next_migration_number)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,135 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
2
+ def self.up
3
+ create_table :motor_queries, force: true do |t|
4
+ t.column :name, :string, null: false
5
+ t.column :description, :string
6
+ t.column :sql_body, :string, null: false
7
+ t.column :preferences, :string, null: false, default: '{}'
8
+ t.column :author_id, :integer
9
+ t.column :author_type, :string
10
+ t.column :deleted_at, :datetime
11
+
12
+ t.timestamps
13
+
14
+ t.index :updated_at
15
+ t.index 'lower(name)',
16
+ name: 'motor_queries_lower_name_unique_index',
17
+ unique: true,
18
+ where: 'deleted_at IS NULL'
19
+ end
20
+
21
+ create_table :motor_dashboards, force: true do |t|
22
+ t.column :title, :string, null: false
23
+ t.column :description, :string
24
+ t.column :preferences, :string, null: false, default: '{}'
25
+ t.column :author_id, :integer
26
+ t.column :author_type, :string
27
+ t.column :deleted_at, :datetime
28
+
29
+ t.timestamps
30
+
31
+ t.index :updated_at
32
+ t.index 'lower(title)',
33
+ name: 'motor_dashboards_lower_title_unique_index',
34
+ unique: true,
35
+ where: 'deleted_at IS NULL'
36
+ end
37
+
38
+ create_table :motor_forms, force: true do |t|
39
+ t.column :name, :string, null: false
40
+ t.column :description, :string
41
+ t.column :api_path, :string, null: false
42
+ t.column :http_method, :string, null: false
43
+ t.column :preferences, :string, null: false, default: '{}'
44
+ t.column :author_id, :integer
45
+ t.column :author_type, :string
46
+ t.column :deleted_at, :datetime
47
+
48
+ t.timestamps
49
+
50
+ t.index :updated_at
51
+ t.index 'lower(name)',
52
+ name: 'motor_forms_lower_name_unique_index',
53
+ unique: true,
54
+ where: 'deleted_at IS NULL'
55
+ end
56
+
57
+ create_table :motor_resources, force: true do |t|
58
+ t.column :name, :string, null: false, index: { unique: true }
59
+ t.column :preferences, :string, null: false, default: '{}'
60
+
61
+ t.timestamps
62
+
63
+ t.index :updated_at
64
+ end
65
+
66
+ create_table :motor_configs, force: true do |t|
67
+ t.column :key, :string, null: false, index: { unique: true }
68
+ t.column :value, :string, null: false, default: '{}'
69
+
70
+ t.timestamps
71
+
72
+ t.index :updated_at
73
+ end
74
+
75
+ create_table :motor_alerts, force: true do |t|
76
+ t.references :query, null: false, foreign_key: { to_table: :motor_queries }, index: true
77
+ t.column :name, :string, null: false
78
+ t.column :description, :string
79
+ t.column :to_emails, :string, null: false
80
+ t.column :is_enabled, :boolean, null: false, default: true
81
+ t.column :preferences, :string, null: false, default: '{}'
82
+ t.column :author_id, :integer
83
+ t.column :author_type, :string
84
+ t.column :deleted_at, :datetime
85
+
86
+ t.timestamps
87
+
88
+ t.index :updated_at
89
+ t.index 'lower(name)',
90
+ name: 'motor_alerts_lower_name_unique_index',
91
+ unique: true,
92
+ where: 'deleted_at IS NULL'
93
+ end
94
+
95
+ create_table :motor_alert_locks, force: true do |t|
96
+ t.references :alert, null: false, foreign_key: { to_table: :motor_alerts }
97
+ t.column :lock_timestamp, :string, null: false
98
+
99
+ t.timestamps
100
+
101
+ t.index %i[alert_id lock_timestamp], unique: true
102
+ end
103
+
104
+ create_table :motor_tags, force: true do |t|
105
+ t.column :name, :string, null: false
106
+
107
+ t.timestamps
108
+
109
+ t.index 'lower(name)',
110
+ name: 'motor_tags_lower_name_unique_index',
111
+ unique: true
112
+ end
113
+
114
+ create_table :motor_taggable_tags, force: true do |t|
115
+ t.references :tag, null: false, foreign_key: { to_table: :motor_tags }, index: true
116
+ t.column :taggable_id, :integer, null: false
117
+ t.column :taggable_type, :string, null: false
118
+
119
+ t.index %i[taggable_id taggable_type tag_id],
120
+ name: 'motor_polymorphic_association_tag_index',
121
+ unique: true
122
+ end
123
+ end
124
+
125
+ def self.down
126
+ drop_table :motor_alert_locks
127
+ drop_table :motor_alerts
128
+ drop_table :motor_taggable_tags
129
+ drop_table :motor_tags
130
+ drop_table :motor_resources
131
+ drop_table :motor_configs
132
+ drop_table :motor_queries
133
+ drop_table :motor_dashboards
134
+ end
135
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './motor'
data/lib/motor.rb ADDED
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cancancan'
4
+ require 'ar_lazy_preload'
5
+ require 'js_regex'
6
+ require 'fugit'
7
+ require 'csv'
8
+ require 'active_record/filter'
9
+
10
+ module Motor
11
+ PATH = Pathname.new(__dir__)
12
+
13
+ module_function
14
+
15
+ def reload!
16
+ Kernel.silence_warnings do
17
+ Dir[PATH.join('./motor/**/*.rb')].each do |f|
18
+ next if f.ends_with?('alerts/scheduler.rb')
19
+ next if f.ends_with?('alerts/scheduled_alerts_cache.rb')
20
+ next if f.ends_with?('ui_configs.rb')
21
+
22
+ load f
23
+ end
24
+ end
25
+
26
+ true
27
+ end
28
+
29
+ def development?
30
+ ENV['MOTOR_DEVELOPMENT'].present?
31
+ end
32
+ end
33
+
34
+ require 'motor/version'
35
+ require 'motor/admin'
36
+ require 'motor/api'
37
+ require 'motor/assets'
38
+ require 'motor/build_schema'
39
+ require 'motor/api_query'
40
+ require 'motor/tags'
41
+ require 'motor/ui_configs'
42
+ require 'motor/queries'
43
+ require 'motor/dashboards'
44
+ require 'motor/forms'
45
+ require 'motor/alerts'
46
+ require 'motor/hash_serializer'
47
+ require 'motor/active_record_utils'
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './active_record_utils/types'
4
+ require_relative './active_record_utils/fetch_methods'
5
+
6
+ module ActiveRecordUtils
7
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module ActiveRecordUtils
5
+ module FetchMethods
6
+ EXCLUDE_METHODS = %i[
7
+ password
8
+ current_password
9
+ password_confirmation
10
+ devise_modules
11
+ ].freeze
12
+
13
+ module_function
14
+
15
+ def call(model)
16
+ (model.instance_methods(false) - model.superclass.instance_methods).reject do |name|
17
+ next true if EXCLUDE_METHODS.include?(name)
18
+ next true if name.to_s.match?(/(:?=|\?|_id)\z/)
19
+ next true if name.to_s.match?(/\A(?:validate|autosave)_associated_records/)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end