smriti 0.5.0
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +168 -0
- data/Rakefile +15 -0
- data/app/assets/images/smriti/android-chrome-192x192.png +0 -0
- data/app/assets/images/smriti/android-chrome-512x512.png +0 -0
- data/app/assets/images/smriti/apple-touch-icon.png +0 -0
- data/app/assets/images/smriti/favicon-16x16.png +0 -0
- data/app/assets/images/smriti/favicon-32x32.png +0 -0
- data/app/assets/images/smriti/favicon-48x48.png +0 -0
- data/app/assets/images/smriti/favicon.ico +0 -0
- data/app/assets/images/smriti/favicon.svg +18 -0
- data/app/assets/images/smriti/logo.svg +18 -0
- data/app/assets/images/smriti/mask-icon.svg +5 -0
- data/app/assets/stylesheets/smriti/application.css +1040 -0
- data/app/controllers/smriti/admin/application_controller.rb +135 -0
- data/app/controllers/smriti/admin/dashboard_controller.rb +32 -0
- data/app/controllers/smriti/admin/mat_view_definitions_controller.rb +372 -0
- data/app/controllers/smriti/admin/mat_view_runs_controller.rb +185 -0
- data/app/controllers/smriti/admin/preferences_controller.rb +91 -0
- data/app/helpers/smriti/admin/datatable_helper.rb +249 -0
- data/app/helpers/smriti/admin/localized_digit_helper.rb +70 -0
- data/app/helpers/smriti/admin/ui_helper.rb +539 -0
- data/app/javascript/smriti/application.js +8 -0
- data/app/javascript/smriti/controllers/application.js +10 -0
- data/app/javascript/smriti/controllers/body_setup_controller.js +120 -0
- data/app/javascript/smriti/controllers/datatable_controller.js +351 -0
- data/app/javascript/smriti/controllers/details_controller.js +200 -0
- data/app/javascript/smriti/controllers/drawer_controller.js +470 -0
- data/app/javascript/smriti/controllers/flash_controller.js +112 -0
- data/app/javascript/smriti/controllers/index.js +10 -0
- data/app/javascript/smriti/controllers/mv_confirm_controller.js +435 -0
- data/app/javascript/smriti/controllers/tabs_controller.js +184 -0
- data/app/javascript/smriti/controllers/tooltip_controller.js +525 -0
- data/app/javascript/smriti/controllers/turbo_frame_lifecycle_controller.js +342 -0
- data/app/jobs/smriti/application_job.rb +144 -0
- data/app/jobs/smriti/create_view_job.rb +87 -0
- data/app/jobs/smriti/delete_view_job.rb +89 -0
- data/app/jobs/smriti/refresh_view_job.rb +94 -0
- data/app/models/concerns/smriti_i18n.rb +139 -0
- data/app/models/concerns/smriti_paginate.rb +70 -0
- data/app/models/concerns/smriti_query_helper.rb +36 -0
- data/app/models/smriti/application_record.rb +39 -0
- data/app/models/smriti/mat_view_definition.rb +254 -0
- data/app/models/smriti/mat_view_run.rb +275 -0
- data/app/views/layouts/smriti/_footer.html.erb +47 -0
- data/app/views/layouts/smriti/_header.html.erb +25 -0
- data/app/views/layouts/smriti/admin.html.erb +47 -0
- data/app/views/layouts/smriti/turbo_frame.html.erb +3 -0
- data/app/views/smriti/admin/dashboard/index.html.erb +38 -0
- data/app/views/smriti/admin/mat_view_definitions/_definition_actions.html.erb +94 -0
- data/app/views/smriti/admin/mat_view_definitions/_dt-index-empty-row.html.erb +11 -0
- data/app/views/smriti/admin/mat_view_definitions/_dt-index-row.html.erb +27 -0
- data/app/views/smriti/admin/mat_view_definitions/empty.html.erb +1 -0
- data/app/views/smriti/admin/mat_view_definitions/form.html.erb +79 -0
- data/app/views/smriti/admin/mat_view_definitions/index.html.erb +10 -0
- data/app/views/smriti/admin/mat_view_definitions/show.html.erb +40 -0
- data/app/views/smriti/admin/mat_view_runs/_dt-index-empty-row.html.erb +11 -0
- data/app/views/smriti/admin/mat_view_runs/_dt-index-row.html.erb +41 -0
- data/app/views/smriti/admin/mat_view_runs/index.html.erb +1 -0
- data/app/views/smriti/admin/mat_view_runs/show.html.erb +64 -0
- data/app/views/smriti/admin/preferences/show.html.erb +49 -0
- data/app/views/smriti/admin/ui/_card.html.erb +15 -0
- data/app/views/smriti/admin/ui/_datatable.html.erb +34 -0
- data/app/views/smriti/admin/ui/_datatable_filters.html.erb +45 -0
- data/app/views/smriti/admin/ui/_datatable_tbody.html.erb +11 -0
- data/app/views/smriti/admin/ui/_datatable_tfoot.html.erb +70 -0
- data/app/views/smriti/admin/ui/_datatable_thead.html.erb +105 -0
- data/app/views/smriti/admin/ui/_details.html.erb +10 -0
- data/app/views/smriti/admin/ui/_flash.html.erb +6 -0
- data/app/views/smriti/admin/ui/_table.html.erb +8 -0
- data/config/importmap.rb +9 -0
- data/config/locales/ar.yml +223 -0
- data/config/locales/de.yml +230 -0
- data/config/locales/en-AU-ocker.yml +223 -0
- data/config/locales/en-AU.yml +202 -0
- data/config/locales/en-BORK.yml +225 -0
- data/config/locales/en-CA.yml +223 -0
- data/config/locales/en-GB.yml +223 -0
- data/config/locales/en-LOL.yml +219 -0
- data/config/locales/en-SCOT.yml +223 -0
- data/config/locales/en-SHAKESPEARE.yml +225 -0
- data/config/locales/en-US-pirate.yml +222 -0
- data/config/locales/en-US.yml +225 -0
- data/config/locales/en-YODA.yml +221 -0
- data/config/locales/en.yml +223 -0
- data/config/locales/es.yml +226 -0
- data/config/locales/fa.yml +223 -0
- data/config/locales/fr-CA.yml +227 -0
- data/config/locales/fr.yml +227 -0
- data/config/locales/he.yml +218 -0
- data/config/locales/hi.yml +223 -0
- data/config/locales/it.yml +225 -0
- data/config/locales/ja-JP.yml +215 -0
- data/config/locales/pt.yml +225 -0
- data/config/locales/ru.yml +228 -0
- data/config/locales/ur.yml +225 -0
- data/config/locales/zh-CN.yml +214 -0
- data/config/locales/zh-TW.yml +214 -0
- data/config/routes.rb +36 -0
- data/lib/ext/exception.rb +20 -0
- data/lib/generators/smriti/install/install_generator.rb +86 -0
- data/lib/generators/smriti/install/templates/create_mat_view_definitions.rb +29 -0
- data/lib/generators/smriti/install/templates/create_mat_view_runs.rb +32 -0
- data/lib/generators/smriti/install/templates/smriti_initializer.rb +23 -0
- data/lib/smriti/admin/auth_bridge.rb +93 -0
- data/lib/smriti/admin/default_auth.rb +62 -0
- data/lib/smriti/configuration.rb +58 -0
- data/lib/smriti/engine.rb +82 -0
- data/lib/smriti/helpers/ui_test_ids.rb +49 -0
- data/lib/smriti/jobs/adapter.rb +81 -0
- data/lib/smriti/service_response.rb +75 -0
- data/lib/smriti/services/base_service.rb +471 -0
- data/lib/smriti/services/check_matview_exists.rb +76 -0
- data/lib/smriti/services/concurrent_refresh.rb +94 -0
- data/lib/smriti/services/create_view.rb +173 -0
- data/lib/smriti/services/delete_view.rb +111 -0
- data/lib/smriti/services/regular_refresh.rb +90 -0
- data/lib/smriti/services/swap_refresh.rb +181 -0
- data/lib/smriti/version.rb +21 -0
- data/lib/smriti.rb +64 -0
- data/lib/tasks/helpers.rb +185 -0
- data/lib/tasks/smriti_tasks.rake +151 -0
- metadata +206 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright Codevedas Inc. 2025-present
|
|
4
|
+
#
|
|
5
|
+
# This source code is licensed under the MIT license found in the
|
|
6
|
+
# LICENSE file in the root directory of this source tree.
|
|
7
|
+
|
|
8
|
+
module Smriti
|
|
9
|
+
module Admin
|
|
10
|
+
# Smriti::Admin::ApplicationController
|
|
11
|
+
# --------------------------------------
|
|
12
|
+
# Base controller for the Smriti admin interface.
|
|
13
|
+
#
|
|
14
|
+
# Responsibilities:
|
|
15
|
+
# - Provides authentication and authorization via Smriti::Admin::AuthBridge.
|
|
16
|
+
# - Applies the `smriti/admin` layout and includes UI helpers.
|
|
17
|
+
# - Manages locale (`I18n.locale`) and enforces language parameter consistency.
|
|
18
|
+
# - Sets the browser time zone around each request when provided via cookies.
|
|
19
|
+
# - Exposes `smriti_data_theme` (light/dark) for theming via cookies.
|
|
20
|
+
# - Provides frame helpers (`render_frame`, `ensure_frame`) to support Turbo-driven
|
|
21
|
+
# admin UI navigation.
|
|
22
|
+
#
|
|
23
|
+
# Filters:
|
|
24
|
+
# - `before_action`: sets locale and redirects to enforce `lang` consistency.
|
|
25
|
+
# - `around_action`: wraps requests in the browser’s time zone if valid.
|
|
26
|
+
#
|
|
27
|
+
# Methods:
|
|
28
|
+
# - {#default_url_options} ensures `lang` param is included in generated URLs.
|
|
29
|
+
# - {#set_time_zone} runs the request in the cookie-provided time zone if valid.
|
|
30
|
+
# - {#render_frame} renders a UI frame partial given `frame_id`.
|
|
31
|
+
# - {#ensure_frame} requires a `frame_id` param for frame-only actions.
|
|
32
|
+
# - {#redirect_to_lang} redirects when the URL `lang` param differs from `I18n.locale`.
|
|
33
|
+
# - {#set_smriti_locale} sets the session-defined or default locale.
|
|
34
|
+
# - {#smriti_data_theme} returns `light`, `dark`, or `nil` for theming.
|
|
35
|
+
#
|
|
36
|
+
class ApplicationController < ActionController::Base
|
|
37
|
+
include Smriti::Admin::AuthBridge
|
|
38
|
+
|
|
39
|
+
helper Smriti::Admin::UiHelper
|
|
40
|
+
helper Smriti::Admin::LocalizedDigitHelper
|
|
41
|
+
helper Smriti::Admin::DatatableHelper
|
|
42
|
+
helper Smriti::Helpers::UiTestIds
|
|
43
|
+
layout 'smriti/admin'
|
|
44
|
+
|
|
45
|
+
before_action :set_smriti_locale, :redirect_to_lang
|
|
46
|
+
helper_method :smriti_data_theme
|
|
47
|
+
around_action :set_time_zone
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# Default URL options, ensuring `lang` is always included.
|
|
52
|
+
#
|
|
53
|
+
# @api private
|
|
54
|
+
#
|
|
55
|
+
# @return [Hash{Symbol => String}]
|
|
56
|
+
def default_url_options
|
|
57
|
+
{ lang: params[:lang].presence || I18n.locale }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Wraps the request in the browser’s time zone if one is set in cookies.
|
|
61
|
+
#
|
|
62
|
+
# @api private
|
|
63
|
+
#
|
|
64
|
+
# @yield the block representing the request lifecycle
|
|
65
|
+
# @return [void]
|
|
66
|
+
def set_time_zone(&)
|
|
67
|
+
browser_tz = cookies[:browser_tz]
|
|
68
|
+
if browser_tz.present? && ActiveSupport::TimeZone[browser_tz]
|
|
69
|
+
Time.use_zone(browser_tz, &)
|
|
70
|
+
else
|
|
71
|
+
yield
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Ensures a `frame_id` param is present.
|
|
76
|
+
# If missing, redirects to the admin root with an alert.
|
|
77
|
+
#
|
|
78
|
+
# @api private
|
|
79
|
+
#
|
|
80
|
+
# @return [void]
|
|
81
|
+
def ensure_frame
|
|
82
|
+
@frame_id = params[:frame_id]
|
|
83
|
+
return if @frame_id.present?
|
|
84
|
+
|
|
85
|
+
redirect_to admin_root_path, alert: I18n.t('smriti.errors.frame_only')
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Redirects to enforce that the `lang` param matches `I18n.locale`.
|
|
89
|
+
#
|
|
90
|
+
# @api private
|
|
91
|
+
#
|
|
92
|
+
# @return [void]
|
|
93
|
+
def redirect_to_lang
|
|
94
|
+
locale_str = locale.to_s
|
|
95
|
+
return if params[:lang] == locale_str
|
|
96
|
+
|
|
97
|
+
lang = locale_str
|
|
98
|
+
redirect_to url_for(params.permit!.to_h.merge(lang: lang)), status: :see_other
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Sets the locale for Smriti admin requests.
|
|
102
|
+
# Falls back to default locale if session value is invalid.
|
|
103
|
+
#
|
|
104
|
+
# @api private
|
|
105
|
+
#
|
|
106
|
+
# @return [void]
|
|
107
|
+
def set_smriti_locale
|
|
108
|
+
I18n.locale = if (loc = session[:smriti_locale]).present? && Smriti::Engine.available_locales.map(&:to_s).include?(loc)
|
|
109
|
+
loc
|
|
110
|
+
else
|
|
111
|
+
Smriti::Engine.default_locale
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Returns the current theme for the admin UI.
|
|
116
|
+
#
|
|
117
|
+
# @api private
|
|
118
|
+
#
|
|
119
|
+
# @return ["light", "dark", nil] the theme stored in cookies, or `nil` if invalid
|
|
120
|
+
def smriti_data_theme
|
|
121
|
+
theme = cookies[:theme].to_s
|
|
122
|
+
%w[light dark].include?(theme) ? theme : nil
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Returns the current locale.
|
|
126
|
+
#
|
|
127
|
+
# @api private
|
|
128
|
+
#
|
|
129
|
+
# @return [Symbol] the current I18n locale
|
|
130
|
+
def locale
|
|
131
|
+
I18n.locale
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright Codevedas Inc. 2025-present
|
|
4
|
+
#
|
|
5
|
+
# This source code is licensed under the MIT license found in the
|
|
6
|
+
# LICENSE file in the root directory of this source tree.
|
|
7
|
+
|
|
8
|
+
module Smriti
|
|
9
|
+
module Admin
|
|
10
|
+
# Smriti::Admin::DashboardController
|
|
11
|
+
# ------------------------------------
|
|
12
|
+
# Controller for the Smriti admin dashboard.
|
|
13
|
+
#
|
|
14
|
+
# Responsibilities:
|
|
15
|
+
# - Provides the landing page (`index`) for the admin interface.
|
|
16
|
+
# - Authorizes access via {ApplicationController#authorize_smriti!}.
|
|
17
|
+
# - Prepares placeholder metrics content (future: aggregated refresh metrics).
|
|
18
|
+
#
|
|
19
|
+
class DashboardController < ApplicationController
|
|
20
|
+
# GET /:lang/admin
|
|
21
|
+
#
|
|
22
|
+
# Renders the admin dashboard. Currently sets a placeholder message
|
|
23
|
+
# until metric aggregation is implemented.
|
|
24
|
+
#
|
|
25
|
+
# @return [void]
|
|
26
|
+
def index
|
|
27
|
+
authorize_smriti!(:view, :smriti_dashboard)
|
|
28
|
+
@metrics_note = 'Metrics coming soon (see: Aggregate refresh metrics for reporting).'
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright Codevedas Inc. 2025-present
|
|
4
|
+
#
|
|
5
|
+
# This source code is licensed under the MIT license found in the
|
|
6
|
+
# LICENSE file in the root directory of this source tree.
|
|
7
|
+
|
|
8
|
+
module Smriti
|
|
9
|
+
module Admin
|
|
10
|
+
# Smriti::Admin::MatViewDefinitionsController
|
|
11
|
+
# ---------------------------------------------
|
|
12
|
+
# Admin CRUD controller for {Smriti::MatViewDefinition} records.
|
|
13
|
+
#
|
|
14
|
+
# Responsibilities:
|
|
15
|
+
# - Full CRUD lifecycle: index, show, new, create, edit, update, destroy.
|
|
16
|
+
# - Admin-only actions to trigger materialised view operations:
|
|
17
|
+
# - {#create_now} → enqueues {Smriti::CreateViewJob}
|
|
18
|
+
# - {#refresh} → enqueues {Smriti::RefreshViewJob}
|
|
19
|
+
# - {#delete_now} → enqueues {Smriti::DeleteViewJob}
|
|
20
|
+
# - Integrates with Turbo Frames (uses frame-aware redirects/responses).
|
|
21
|
+
# - Normalizes array fields (`unique_index_columns`, `dependencies`) from
|
|
22
|
+
# comma-separated params into arrays.
|
|
23
|
+
#
|
|
24
|
+
# Filters:
|
|
25
|
+
# - `before_action :set_definition` for member actions.
|
|
26
|
+
# - `before_action :normalize_array_fields` for create/update.
|
|
27
|
+
# - `before_action :ensure_frame` to enforce frame context.
|
|
28
|
+
#
|
|
29
|
+
class MatViewDefinitionsController < ApplicationController
|
|
30
|
+
include Smriti::Admin::DatatableHelper
|
|
31
|
+
|
|
32
|
+
before_action :set_definition, only: %i[show edit update destroy create_now refresh delete_now]
|
|
33
|
+
before_action :normalize_array_fields, only: %i[create update]
|
|
34
|
+
before_action :parse_headers_to_params, :ensure_frame
|
|
35
|
+
|
|
36
|
+
# GET /:lang/admin/definitions
|
|
37
|
+
#
|
|
38
|
+
# Two part rendering:
|
|
39
|
+
# - Full page load when no `stream` param: renders index with datatable frame. This is
|
|
40
|
+
# essentially shell of the datatable for initial load.
|
|
41
|
+
# - When shell is loaded, it requests the `stream` version which renders just the datatable rows
|
|
42
|
+
# and pagination controls. This allows for dynamic updates via Turbo Streams.
|
|
43
|
+
#
|
|
44
|
+
# @return [void]
|
|
45
|
+
def index
|
|
46
|
+
authorize_smriti!(:read, :smriti_definitions)
|
|
47
|
+
|
|
48
|
+
assign_index_state
|
|
49
|
+
|
|
50
|
+
if params[:stream].present?
|
|
51
|
+
render_dt_turbo_streams
|
|
52
|
+
return
|
|
53
|
+
end
|
|
54
|
+
render 'index', formats: :html, layout: 'smriti/turbo_frame', locals: { row_meta: @row_meta }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# GET /:lang/admin/definitions/:id
|
|
58
|
+
#
|
|
59
|
+
# Shows a single definition, including run history.
|
|
60
|
+
#
|
|
61
|
+
# @return [void]
|
|
62
|
+
def show
|
|
63
|
+
authorize_smriti!(:read, :smriti_definition, @definition)
|
|
64
|
+
@mv_exists = Smriti::Services::CheckMatviewExists.new(@definition).call.response[:exists]
|
|
65
|
+
@runs = @definition.mat_view_runs.order(created_at: :desc).to_a
|
|
66
|
+
render 'show', formats: :html, layout: 'smriti/turbo_frame'
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# GET /:lang/admin/definitions/new
|
|
70
|
+
#
|
|
71
|
+
# Renders the new definition form.
|
|
72
|
+
#
|
|
73
|
+
# @return [void]
|
|
74
|
+
def new
|
|
75
|
+
authorize_smriti!(:create, :smriti_definition)
|
|
76
|
+
@definition = Smriti::MatViewDefinition.new
|
|
77
|
+
render 'form', formats: :html, layout: 'smriti/turbo_frame'
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# POST /:lang/admin/definitions
|
|
81
|
+
#
|
|
82
|
+
# Creates a new definition from params.
|
|
83
|
+
#
|
|
84
|
+
# @return [void]
|
|
85
|
+
def create
|
|
86
|
+
authorize_smriti!(:create, :smriti_definition)
|
|
87
|
+
@definition = Smriti::MatViewDefinition.new(definition_params)
|
|
88
|
+
if @definition.save
|
|
89
|
+
handle_frame_response(status: 298)
|
|
90
|
+
else
|
|
91
|
+
render 'form', formats: :html, layout: 'smriti/turbo_frame', status: :unprocessable_content
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# GET /:lang/admin/definitions/:id/edit
|
|
96
|
+
#
|
|
97
|
+
# Renders the edit form for an existing definition.
|
|
98
|
+
#
|
|
99
|
+
# @return [void]
|
|
100
|
+
def edit
|
|
101
|
+
authorize_smriti!(:update, :smriti_definition, @definition)
|
|
102
|
+
render 'form', formats: :html, layout: 'smriti/turbo_frame'
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# PATCH/PUT /:lang/admin/definitions/:id
|
|
106
|
+
#
|
|
107
|
+
# Updates an existing definition.
|
|
108
|
+
#
|
|
109
|
+
# @return [void]
|
|
110
|
+
def update
|
|
111
|
+
authorize_smriti!(:update, :smriti_definition, @definition)
|
|
112
|
+
if @definition.update(definition_params)
|
|
113
|
+
handle_frame_response(status: 298)
|
|
114
|
+
else
|
|
115
|
+
render 'form', formats: :html, layout: 'smriti/turbo_frame', status: :unprocessable_content
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# DELETE /:lang/admin/definitions/:id
|
|
120
|
+
#
|
|
121
|
+
# Destroys the definition. Frame-specific redirect/empty response.
|
|
122
|
+
#
|
|
123
|
+
# @return [void]
|
|
124
|
+
def destroy
|
|
125
|
+
authorize_smriti!(:destroy, :smriti_definition, @definition)
|
|
126
|
+
@definition.destroy!
|
|
127
|
+
if @frame_id == 'dash-definitions'
|
|
128
|
+
redirect_to admin_mat_view_definitions_path(frame_id: @frame_id), status: :see_other
|
|
129
|
+
else
|
|
130
|
+
render 'empty', formats: :html, layout: 'smriti/turbo_frame', status: 298
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# POST /:lang/admin/definitions/:id/create_now
|
|
135
|
+
#
|
|
136
|
+
# Immediately enqueues a background job to create the materialised view.
|
|
137
|
+
#
|
|
138
|
+
# @return [void]
|
|
139
|
+
def create_now
|
|
140
|
+
authorize_smriti!(:create, :smriti_definition_view, @definition)
|
|
141
|
+
|
|
142
|
+
force = params[:force].to_s.downcase == 'true'
|
|
143
|
+
Smriti::Jobs::Adapter.enqueue(
|
|
144
|
+
Smriti::CreateViewJob,
|
|
145
|
+
queue: Smriti.configuration.job_queue,
|
|
146
|
+
args: [@definition.id, force, row_count_strategy]
|
|
147
|
+
)
|
|
148
|
+
handle_frame_response
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# POST /:lang/admin/definitions/:id/refresh
|
|
152
|
+
#
|
|
153
|
+
# Immediately enqueues a background job to refresh the materialised view.
|
|
154
|
+
#
|
|
155
|
+
# @return [void]
|
|
156
|
+
def refresh
|
|
157
|
+
authorize_smriti!(:update, :smriti_definition_view, @definition)
|
|
158
|
+
Smriti::Jobs::Adapter.enqueue(
|
|
159
|
+
Smriti::RefreshViewJob,
|
|
160
|
+
queue: Smriti.configuration.job_queue,
|
|
161
|
+
args: [@definition.id, row_count_strategy]
|
|
162
|
+
)
|
|
163
|
+
handle_frame_response
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# POST /:lang/admin/definitions/:id/delete_now
|
|
167
|
+
#
|
|
168
|
+
# Immediately enqueues a background job to delete the materialised view.
|
|
169
|
+
#
|
|
170
|
+
# @return [void]
|
|
171
|
+
def delete_now
|
|
172
|
+
authorize_smriti!(:destroy, :smriti_definition_view, @definition)
|
|
173
|
+
|
|
174
|
+
cascade = params[:cascade].to_s.downcase == 'true'
|
|
175
|
+
Smriti::Jobs::Adapter.enqueue(
|
|
176
|
+
Smriti::DeleteViewJob,
|
|
177
|
+
queue: Smriti.configuration.job_queue,
|
|
178
|
+
args: [@definition.id, cascade, row_count_strategy]
|
|
179
|
+
)
|
|
180
|
+
handle_frame_response
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
private
|
|
184
|
+
|
|
185
|
+
# Returns the configured row count strategy for admin UI operations.
|
|
186
|
+
#
|
|
187
|
+
# @api private
|
|
188
|
+
#
|
|
189
|
+
# @return [Symbol, nil] row count strategy (e.g., :estimated, :exact, :none)
|
|
190
|
+
def row_count_strategy
|
|
191
|
+
Smriti.configuration.admin_ui[:row_count_strategy] || :none
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Handles redirect/response after a frame-based action.
|
|
195
|
+
#
|
|
196
|
+
# @api private
|
|
197
|
+
#
|
|
198
|
+
# @param status [Symbol,Integer] the HTTP status for redirect
|
|
199
|
+
# @return [void]
|
|
200
|
+
def handle_frame_response(status: :see_other)
|
|
201
|
+
if @frame_id == 'dash-definitions'
|
|
202
|
+
dtsort = params[:dtsort]
|
|
203
|
+
dtfilter = params[:dtfilter]
|
|
204
|
+
dtsearch = params[:dtsearch]
|
|
205
|
+
redirect_to admin_mat_view_definitions_path(frame_id: @frame_id, stream: true, dtsort:, dtfilter:, dtsearch:), status: status
|
|
206
|
+
else
|
|
207
|
+
redirect_to admin_mat_view_definition_path(@definition, frame_id: @frame_id), status: status
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Loads a definition by `params[:id]`.
|
|
212
|
+
#
|
|
213
|
+
# @api private
|
|
214
|
+
#
|
|
215
|
+
# @return [void]
|
|
216
|
+
def set_definition
|
|
217
|
+
@definition = Smriti::MatViewDefinition.find(params[:id])
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Normalizes array fields (unique_index_columns, dependencies) from
|
|
221
|
+
# comma-separated strings into arrays.
|
|
222
|
+
#
|
|
223
|
+
# @api private
|
|
224
|
+
#
|
|
225
|
+
# @return [void]
|
|
226
|
+
def normalize_array_fields
|
|
227
|
+
normalize_array_field(:mat_view_definition, :unique_index_columns)
|
|
228
|
+
normalize_array_field(:mat_view_definition, :dependencies)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
# Normalizes a specific array field from a comma-separated string into an array.
|
|
232
|
+
#
|
|
233
|
+
# @api private
|
|
234
|
+
#
|
|
235
|
+
# @param object_key [Symbol] the params object key (e.g., :mat_view_definition)
|
|
236
|
+
# @param array_key [Symbol] the specific array field key (e.g., :unique_index_columns)
|
|
237
|
+
#
|
|
238
|
+
# @return [void]
|
|
239
|
+
def normalize_array_field(object_key, array_key)
|
|
240
|
+
object = params[object_key]
|
|
241
|
+
|
|
242
|
+
values = object[array_key]
|
|
243
|
+
return if values.nil?
|
|
244
|
+
|
|
245
|
+
object[array_key] = values.split(',').map(&:strip).reject(&:blank?)
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Strong params for mat view definitions.
|
|
249
|
+
#
|
|
250
|
+
# @api private
|
|
251
|
+
#
|
|
252
|
+
# @return [ActionController::Parameters]
|
|
253
|
+
def definition_params
|
|
254
|
+
params.require(:mat_view_definition).permit(
|
|
255
|
+
:name, :sql, :refresh_strategy, :schedule_cron,
|
|
256
|
+
unique_index_columns: [], dependencies: []
|
|
257
|
+
)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Loads data for the index datatable with filtering, searching, sorting, and pagination.
|
|
261
|
+
# sets @data.
|
|
262
|
+
#
|
|
263
|
+
# @api private
|
|
264
|
+
#
|
|
265
|
+
# @return [void]
|
|
266
|
+
def index_dt_load_data
|
|
267
|
+
rel = Smriti::MatViewDefinition
|
|
268
|
+
rel = dt_apply_filter(rel, index_dt_columns)
|
|
269
|
+
rel = dt_apply_search(rel, index_dt_columns)
|
|
270
|
+
rel = dt_apply_sort(rel, index_dt_columns)
|
|
271
|
+
@data = dt_apply_pagination(rel, @dt_config[:pagination][:per_page_default])
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Configuration for the index datatable.
|
|
275
|
+
#
|
|
276
|
+
# @api private
|
|
277
|
+
#
|
|
278
|
+
# @return [Hash] datatable configuration
|
|
279
|
+
def index_dt_config
|
|
280
|
+
columns = index_dt_columns
|
|
281
|
+
{
|
|
282
|
+
id: 'mv-definitions-table',
|
|
283
|
+
index_url: admin_mat_view_definitions_path(frame_id: @frame_id),
|
|
284
|
+
frame_id: 'mv-definitions-datatable',
|
|
285
|
+
columns: columns,
|
|
286
|
+
dt_humanize_ref: 'Smriti::MatViewDefinition',
|
|
287
|
+
empty_row_partial_name: 'dt-index-empty-row',
|
|
288
|
+
row_partial_name: 'dt-index-row',
|
|
289
|
+
search_enabled: columns.any? { |_, col| col[:search].present? },
|
|
290
|
+
filter_enabled: columns.any? { |_, col| col[:filter].present? },
|
|
291
|
+
pagination: { per_page_default: 10, per_page_options: [10, 25, 50, 100] }
|
|
292
|
+
}
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
# Column definitions for the index datatable.
|
|
296
|
+
#
|
|
297
|
+
# @api private
|
|
298
|
+
#
|
|
299
|
+
# @return [Hash] column definitions
|
|
300
|
+
def index_dt_columns
|
|
301
|
+
{
|
|
302
|
+
name: {
|
|
303
|
+
label_ref: 'name',
|
|
304
|
+
label_type: 'humanize_attr',
|
|
305
|
+
sort: 'name',
|
|
306
|
+
filter: 'name',
|
|
307
|
+
search: 'name'
|
|
308
|
+
},
|
|
309
|
+
refresh_strategy: {
|
|
310
|
+
label_ref: 'refresh_strategy',
|
|
311
|
+
label_type: 'humanize_attr',
|
|
312
|
+
sort: 'refresh_strategy',
|
|
313
|
+
filter: 'refresh_strategy',
|
|
314
|
+
search: 'refresh_strategy'
|
|
315
|
+
},
|
|
316
|
+
schedule_cron: {
|
|
317
|
+
label_ref: 'schedule_cron',
|
|
318
|
+
label_type: 'humanize_attr',
|
|
319
|
+
sort: 'schedule_cron',
|
|
320
|
+
filter: 'schedule_cron',
|
|
321
|
+
search: 'schedule_cron'
|
|
322
|
+
},
|
|
323
|
+
last_run_at: {
|
|
324
|
+
label_ref: 'last_run_at',
|
|
325
|
+
label_type: 'humanize_attr',
|
|
326
|
+
sort: 'last_run_at',
|
|
327
|
+
filter: nil,
|
|
328
|
+
search: 'last_run_at'
|
|
329
|
+
},
|
|
330
|
+
actions: {
|
|
331
|
+
label_ref: 'actions',
|
|
332
|
+
label_type: 'i18n',
|
|
333
|
+
th_style: 'justify-content: end;',
|
|
334
|
+
filter: nil,
|
|
335
|
+
sort: nil,
|
|
336
|
+
search: nil
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Builds a map of definition names to their existence status in the database.
|
|
342
|
+
# If definitions is nil or empty, returns an empty hash.
|
|
343
|
+
#
|
|
344
|
+
# @api private
|
|
345
|
+
#
|
|
346
|
+
# @param definitions [Array<Smriti::MatViewDefinition>] the definitions to check
|
|
347
|
+
# @return [Hash{String => Boolean}] map of definition names to existence status
|
|
348
|
+
# e.g., { "my_view" => true, "other_view" => false }
|
|
349
|
+
def build_matview_exists_map(definitions)
|
|
350
|
+
return {} if definitions.blank?
|
|
351
|
+
|
|
352
|
+
definitions.to_h do |definition|
|
|
353
|
+
exists = Smriti::Services::CheckMatviewExists.new(definition).call.response[:exists]
|
|
354
|
+
[definition.name, exists]
|
|
355
|
+
end
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
# Assigns instance variables for the index action.
|
|
359
|
+
#
|
|
360
|
+
# @api private
|
|
361
|
+
#
|
|
362
|
+
# @return [void]
|
|
363
|
+
def assign_index_state
|
|
364
|
+
@dt_config = index_dt_config
|
|
365
|
+
@data = []
|
|
366
|
+
|
|
367
|
+
index_dt_load_data
|
|
368
|
+
@row_meta = { mv_exists_map: build_matview_exists_map(@data) }
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
end
|