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,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module BuildSchema
5
+ module MergeSchemaConfigs
6
+ module_function
7
+
8
+ # @param schema [Array<HashWithIndifferentAccess>]
9
+ # @param cache_keys [Hash]
10
+ # @return [Array<HashWithIndifferentAccess>]
11
+ def call(schema, cache_keys = {})
12
+ configs = load_configs(cache_keys)
13
+
14
+ schema.map do |model|
15
+ merge_model(model, configs.fetch(model[:name], {}))
16
+ end
17
+ end
18
+
19
+ # @param model [HashWithIndifferentAccess]
20
+ # @param configs [HashWithIndifferentAccess]
21
+ # @return [HashWithIndifferentAccess]
22
+ def merge_model(model, configs)
23
+ updated_model = model.merge(configs.slice(*Resources::RESOURCE_ATTRS))
24
+
25
+ merge_actions!(updated_model, configs)
26
+ merge_associations!(updated_model, configs)
27
+ merge_columns!(updated_model, configs)
28
+ merge_tabs!(updated_model, configs)
29
+ merge_scopes!(updated_model, configs)
30
+
31
+ updated_model
32
+ end
33
+
34
+ # @param model [HashWithIndifferentAccess]
35
+ # @param configs [HashWithIndifferentAccess]
36
+ # @return [HashWithIndifferentAccess]
37
+ def merge_associations!(model, configs)
38
+ model[:associations] = merge_by_name(
39
+ model[:associations],
40
+ configs[:associations],
41
+ ASSOCIATION_DEFAULTS,
42
+ ->(scope) { !scope[:virtual] }
43
+ )
44
+
45
+ model
46
+ end
47
+
48
+ # @param model [HashWithIndifferentAccess]
49
+ # @param configs [HashWithIndifferentAccess]
50
+ # @return [HashWithIndifferentAccess]
51
+ def merge_columns!(model, configs)
52
+ model[:columns] = merge_by_name(
53
+ merge_custom_sql_columns(model[:columns], configs[:custom_sql]),
54
+ configs[:columns],
55
+ COLUMN_DEFAULTS,
56
+ ->(scope) { !scope[:virtual] }
57
+ )
58
+
59
+ model
60
+ end
61
+
62
+ # @param model [HashWithIndifferentAccess]
63
+ # @param configs [HashWithIndifferentAccess]
64
+ # @return [HashWithIndifferentAccess]
65
+ def merge_actions!(model, configs)
66
+ model[:actions] = merge_by_name(
67
+ model[:actions],
68
+ configs[:actions],
69
+ ACTION_DEFAULTS
70
+ )
71
+
72
+ model
73
+ end
74
+
75
+ # @param model [HashWithIndifferentAccess]
76
+ # @param configs [HashWithIndifferentAccess]
77
+ # @return [HashWithIndifferentAccess]
78
+ def merge_tabs!(model, configs)
79
+ model[:tabs] = merge_by_name(
80
+ model[:tabs],
81
+ configs[:tabs],
82
+ TAB_DEFAULTS
83
+ )
84
+
85
+ model
86
+ end
87
+
88
+ # @param model [HashWithIndifferentAccess]
89
+ # @param configs [HashWithIndifferentAccess]
90
+ # @return [HashWithIndifferentAccess]
91
+ def merge_scopes!(model, configs)
92
+ model[:scopes] = merge_by_name(
93
+ model[:scopes],
94
+ configs[:scopes],
95
+ SCOPE_DEFAULTS,
96
+ ->(scope) { scope[:scope_type] != 'filter' }
97
+ )
98
+
99
+ model
100
+ end
101
+
102
+ # @param defaults [Array<HashWithIndifferentAccess>]
103
+ # @param configs [Array<HashWithIndifferentAccess>]
104
+ # @return [Array<HashWithIndifferentAccess>]
105
+ def merge_by_name(defaults, configs, default_attrs = {}, default_item_check = nil)
106
+ return defaults if configs.blank?
107
+
108
+ (defaults.pluck(:name) + configs.pluck(:name)).uniq.map do |name|
109
+ config_item = configs.find { |e| e[:name] == name }
110
+ default_item = defaults.find { |e| e[:name] == name }
111
+
112
+ next if default_item.nil? && default_item_check&.call(config_item)
113
+
114
+ (default_item || default_attrs).merge(config_item || {})
115
+ end.compact
116
+ end
117
+
118
+ # @param columns [Array<HashWithIndifferentAccess>]
119
+ # @param sql [String]
120
+ # @return [Array<HashWithIndifferentAccess>]
121
+ def merge_custom_sql_columns(columns, sql)
122
+ return columns if sql.blank?
123
+
124
+ query_columns = Resources::CustomSqlColumnsCache.call(sql)
125
+
126
+ columns_index = columns.index_by { |e| e[:name] }
127
+
128
+ merged_columns =
129
+ query_columns.map do |column|
130
+ existing_column = columns_index.delete(column[:name])
131
+
132
+ next existing_column if existing_column
133
+
134
+ column.merge(COLUMN_DEFAULTS).merge(
135
+ access_type: ColumnAccessTypes::READ_ONLY,
136
+ column_source: ColumnSources::QUERY,
137
+ virtual: false
138
+ ).with_indifferent_access
139
+ end
140
+
141
+ reflection_columns = columns_index.values.select { |c| c[:column_source] == ColumnSources::REFLECTION }
142
+
143
+ reflection_columns + merged_columns
144
+ end
145
+
146
+ # @param cache_keys [Hash]
147
+ # @return [HashWithIndifferentAccess<String, HashWithIndifferentAccess>]
148
+ def load_configs(cache_keys)
149
+ resources = Motor::Configs::LoadFromCache.load_resources(cache_key: cache_keys[:resources])
150
+
151
+ resources.each_with_object(ActiveSupport::HashWithIndifferentAccess.new) do |resource, acc|
152
+ acc[resource.name] = resource.preferences
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module ReorderSchema
5
+ COLUMNS_DEFAULT_ORDER_WEIGHTS = {
6
+ id: 0,
7
+ updated_at: 2,
8
+ edited_at: 2,
9
+ created_at: 3,
10
+ inserted_at: 3,
11
+ deleted_at: 4,
12
+ archived_at: 4
13
+ }.with_indifferent_access
14
+
15
+ COLUMNS_DEFAULT_ORDER_WEIGHT = 1
16
+
17
+ module_function
18
+
19
+ # @param cache_keys [Hash]
20
+ # @param schema [Array<HashWithIndifferentAccess>]
21
+ # @return [Array<HashWithIndifferentAccess>]
22
+ def call(schema, cache_keys = {})
23
+ configs = load_configs(cache_keys)
24
+
25
+ schema = sort_by_name(schema, configs['resources.order'])
26
+
27
+ schema.map { |model| reorder_model(model, configs) }
28
+ end
29
+
30
+ def reorder_model(model, configs)
31
+ order_configs = build_order_configs(model[:name], configs)
32
+
33
+ model.merge(
34
+ columns: sort_by_name(sort_columns(model[:columns]), order_configs[:columns], sort_alphabetically: false),
35
+ associations: sort_by_name(model[:associations], order_configs[:associations]),
36
+ actions: sort_by_name(model[:actions], order_configs[:actions], sort_alphabetically: false),
37
+ tabs: sort_by_name(model[:tabs], order_configs[:tabs], sort_alphabetically: false),
38
+ scopes: sort_by_name(model[:scopes], order_configs[:scopes])
39
+ )
40
+ end
41
+
42
+ def build_order_configs(model_name, configs)
43
+ {
44
+ columns: configs["resources.#{model_name}.columns.order"],
45
+ associations: configs["resources.#{model_name}.associations.order"],
46
+ actions: configs["resources.#{model_name}.actions.order"],
47
+ tabs: configs["resources.#{model_name}.tabs.order"],
48
+ scopes: configs["resources.#{model_name}.scopes.order"]
49
+ }
50
+ end
51
+
52
+ # @param list [Array<HashWithIndifferentAccess>]
53
+ # @param sort_alphabetically [Boolean]
54
+ # @param order [Array<String>]
55
+ # @return [Array<HashWithIndifferentAccess>]
56
+ def sort_by_name(list, order, sort_alphabetically: true)
57
+ return list if order.blank? && !sort_alphabetically
58
+
59
+ list.sort_by do |item|
60
+ if order.present?
61
+ order.index(item[:name]) || Float::MAX
62
+ else
63
+ item[:display_name].to_s
64
+ end
65
+ end
66
+ end
67
+
68
+ # @param columns [Array<HashWithIndifferentAccess>]
69
+ # @return [Array<HashWithIndifferentAccess>]
70
+ def sort_columns(columns)
71
+ columns.each_with_object([]) do |column, acc|
72
+ weight = COLUMNS_DEFAULT_ORDER_WEIGHTS.fetch(column[:name], COLUMNS_DEFAULT_ORDER_WEIGHT)
73
+
74
+ (acc[weight] ||= []) << column
75
+ end.flatten.compact
76
+ end
77
+
78
+ # @param cache_keys [Hash]
79
+ # @return [Hash<String, HashWithIndifferentAccess>]
80
+ def load_configs(cache_keys = {})
81
+ configs = Motor::Configs::LoadFromCache.load_configs(cache_key: cache_keys[:configs])
82
+
83
+ configs.each_with_object({}) do |config, acc|
84
+ acc[config.key] = config.value
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module BuildSchema
5
+ module Utils
6
+ ABBREVIATIONS = {
7
+ 'Id' => 'ID',
8
+ 'Uuid' => 'UUID',
9
+ 'Url' => 'URL',
10
+ 'Iso' => 'ISO',
11
+ 'vip' => 'VIP',
12
+ 'Ip' => 'IP',
13
+ 'Zip' => 'ZIP'
14
+ }.freeze
15
+
16
+ module_function
17
+
18
+ def slugify(model)
19
+ model.name.underscore.pluralize.gsub('/', '__')
20
+ end
21
+
22
+ def classify_slug(slug)
23
+ slug.singularize.gsub('__', '/').classify.constantize
24
+ end
25
+
26
+ def humanize_column_name(column_name)
27
+ ABBREVIATIONS.fetch(column_name, column_name.humanize)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module BuildSchema
5
+ module ColumnAccessTypes
6
+ ALL = [
7
+ READ_ONLY = 'read_only',
8
+ WRITE_ONLY = 'write_only',
9
+ READ_WRITE = 'read_write',
10
+ HIDDEN = 'hidden'
11
+ ].freeze
12
+ end
13
+
14
+ module ColumnTypes
15
+ ALL = [
16
+ STRING = 'string',
17
+ INTEGER = 'integer',
18
+ FLOAT = 'float',
19
+ REFERENCE = 'reference',
20
+ DATETIME = 'datetime',
21
+ DATE = 'date',
22
+ BOOLEAN = 'boolean',
23
+ TEXTAREA = 'textarea',
24
+ RICHTEXT = 'richtext',
25
+ CURRENCY = 'currency',
26
+ PERCENTAGE = 'percentage',
27
+ CHANGE = 'change',
28
+ CHART = 'chart',
29
+ TAG = 'tag',
30
+ LINK = 'link',
31
+ COLOR = 'color',
32
+ IMAGE = 'image',
33
+ FILE = 'file',
34
+ JSON = 'json'
35
+ ].freeze
36
+ end
37
+
38
+ module ColumnSources
39
+ ALL = [
40
+ TABLE = 'table',
41
+ QUERY = 'query',
42
+ REFLECTION = 'reflection'
43
+ ].freeze
44
+ end
45
+
46
+ SEARCHABLE_COLUMN_TYPES = %i[citext text string bitstring].freeze
47
+
48
+ DEFAULT_TYPE = 'default'
49
+ DEFAULT_ICON = 'database'
50
+
51
+ COLUMN_NAME_ACCESS_TYPES = {
52
+ id: ColumnAccessTypes::READ_ONLY,
53
+ created_at: ColumnAccessTypes::READ_ONLY,
54
+ created_on: ColumnAccessTypes::READ_ONLY,
55
+ inserted_at: ColumnAccessTypes::READ_ONLY,
56
+ updated_at: ColumnAccessTypes::READ_ONLY,
57
+ updated_on: ColumnAccessTypes::READ_ONLY,
58
+ modified_at: ColumnAccessTypes::READ_ONLY,
59
+ deleted_at: ColumnAccessTypes::READ_ONLY
60
+ }.with_indifferent_access.freeze
61
+
62
+ COLUMN_DEFAULTS = {
63
+ access_type: ColumnAccessTypes::READ_WRITE,
64
+ column_source: ColumnSources::TABLE,
65
+ default_value: nil,
66
+ reference: nil,
67
+ format: {},
68
+ validators: [],
69
+ description: ''
70
+ }.with_indifferent_access
71
+
72
+ ACTION_DEFAULTS = {
73
+ visible: true,
74
+ apply_on: 'member',
75
+ preferences: {}
76
+ }.with_indifferent_access
77
+
78
+ TAB_DEFAULTS = {
79
+ visible: true,
80
+ tab_type: DEFAULT_TYPE,
81
+ preferences: {}
82
+ }.with_indifferent_access
83
+
84
+ SCOPE_DEFAULTS = {
85
+ visible: true,
86
+ scope_type: DEFAULT_TYPE,
87
+ preferences: {}
88
+ }.with_indifferent_access
89
+
90
+ ASSOCIATION_DEFAULTS = {
91
+ visible: true,
92
+ icon: DEFAULT_ICON,
93
+ options: {}
94
+ }.with_indifferent_access
95
+
96
+ module_function
97
+
98
+ def call(cache_keys = {}, current_ability = nil)
99
+ schema = LoadFromRails.call
100
+ schema = MergeSchemaConfigs.call(schema, cache_keys)
101
+ schema = ApplyPermissions.call(schema, current_ability) if current_ability
102
+
103
+ ReorderSchema.call(schema, cache_keys)
104
+ end
105
+
106
+ def for_model(model)
107
+ schema = Motor::BuildSchema::LoadFromRails.build_model_schema(model)
108
+ configs = Motor::Resource.find_by(name: schema[:name]).preferences
109
+
110
+ MergeSchemaConfigs.merge_model(schema, configs)
111
+ end
112
+ end
113
+ end
114
+
115
+ require_relative 'build_schema/active_storage_attachment_schema'
116
+ require_relative 'build_schema/adjust_devise_model_schema'
117
+ require_relative 'build_schema/load_from_rails'
118
+ require_relative 'build_schema/find_display_column'
119
+ require_relative 'build_schema/find_icon'
120
+ require_relative 'build_schema/reorder_schema'
121
+ require_relative 'build_schema/merge_schema_configs'
122
+ require_relative 'build_schema/apply_permissions'
123
+ require_relative 'build_schema/utils'
124
+ require_relative 'build_schema/defaults'
125
+ require_relative 'build_schema/find_searchable_columns'
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module CancanUtils
5
+ module AbilityPatch
6
+ def serialized_rules
7
+ return [] unless @rules
8
+
9
+ @rules.map do |rule|
10
+ {
11
+ base_behavior: rule.base_behavior,
12
+ actions: expand_actions(rule.actions),
13
+ subjects: rule.subjects.map(&:to_s),
14
+ attributes: rule.attributes,
15
+ conditions: rule.conditions.as_json
16
+ }
17
+ end
18
+ end
19
+
20
+ def rules_hash
21
+ serialized_rules.hash
22
+ end
23
+
24
+ private
25
+
26
+ def default_alias_actions
27
+ super.merge(destroy: %i[remove delete])
28
+ end
29
+ end
30
+ end
31
+ 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
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module CancanUtils
5
+ end
6
+ end
7
+
8
+ require_relative 'cancan_utils/ability_patch'
9
+ require_relative 'cancan_utils/can_manage_all'
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Motor
4
+ module Configs
5
+ module BuildConfigsHash
6
+ module_function
7
+
8
+ def call
9
+ cache_keys = LoadFromCache.load_cache_keys
10
+
11
+ normalize_hash(
12
+ engine_version: Motor::VERSION,
13
+ file_version: cache_keys.values.compact.max.to_time,
14
+ resources: build_resources_hash(cache_keys[:resources]),
15
+ configs: build_configs_hash(cache_keys[:configs]),
16
+ queries: build_queries_hash(cache_keys[:queries]),
17
+ dashboards: build_dashboards_hash(cache_keys[:dashboards]),
18
+ forms: build_forms_hash(cache_keys[:forms]),
19
+ alerts: build_alerts_hash(cache_keys[:alerts]),
20
+ api_configs: build_api_configs_hash(cache_keys[:api_configs])
21
+ )
22
+ end
23
+
24
+ def build_queries_hash(cache_key = nil)
25
+ Motor::Configs::LoadFromCache.load_queries(cache_key: cache_key).sort_by(&:id).map do |query|
26
+ query.slice(%i[id name sql_body description preferences])
27
+ .merge(tags: query.tags.map(&:name), updated_at: query.updated_at.to_time)
28
+ end
29
+ end
30
+
31
+ def build_dashboards_hash(cache_key = nil)
32
+ Motor::Configs::LoadFromCache.load_dashboards(cache_key: cache_key).sort_by(&:id).map do |dashboard|
33
+ dashboard.slice(%i[id title description preferences])
34
+ .merge(tags: dashboard.tags.map(&:name), updated_at: dashboard.updated_at.to_time)
35
+ end
36
+ end
37
+
38
+ def build_alerts_hash(cache_key = nil)
39
+ Motor::Configs::LoadFromCache.load_alerts(cache_key: cache_key).sort_by(&:id).map do |alert|
40
+ alert.slice(%i[id name query_id to_emails is_enabled description preferences])
41
+ .merge(tags: alert.tags.map(&:name), updated_at: alert.updated_at.to_time)
42
+ end
43
+ end
44
+
45
+ def build_forms_hash(cache_key = nil)
46
+ Motor::Configs::LoadFromCache.load_forms(cache_key: cache_key).sort_by(&:id).map do |form|
47
+ form.slice(%i[id name http_method api_path description preferences api_config_name])
48
+ .merge(tags: form.tags.map(&:name), updated_at: form.updated_at.to_time)
49
+ end
50
+ end
51
+
52
+ def build_api_configs_hash(cache_key = nil)
53
+ Motor::Configs::LoadFromCache.load_api_configs(cache_key: cache_key).sort_by(&:id).map do |config|
54
+ config.slice(%i[id name url preferences description]).merge(updated_at: config.updated_at.to_time)
55
+ end
56
+ end
57
+
58
+ def build_configs_hash(cache_key = nil)
59
+ Motor::Configs::LoadFromCache.load_configs(cache_key: cache_key).sort_by(&:key).map do |config|
60
+ {
61
+ key: config.key,
62
+ value: config.value,
63
+ updated_at: config.updated_at.to_time
64
+ }
65
+ end
66
+ end
67
+
68
+ def build_resources_hash(cache_key = nil)
69
+ Motor::Configs::LoadFromCache.load_resources(cache_key: cache_key).sort_by(&:name).map do |resource|
70
+ {
71
+ name: resource.name,
72
+ preferences: resource.preferences,
73
+ updated_at: resource.updated_at.to_time
74
+ }
75
+ end
76
+ end
77
+
78
+ def normalize_hash(value)
79
+ case value
80
+ when Hash, ActiveSupport::HashWithIndifferentAccess
81
+ value.to_h.stringify_keys.transform_values { |v| normalize_hash(v) }
82
+ when Array
83
+ value.map { |e| normalize_hash(e) }
84
+ else
85
+ value
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end