motor-admin 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
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