mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-12-11 14:45:59 +08:00
Replace reports materialized view with normal queries and re-enable fragment caching [SCI-2894]
This commit is contained in:
parent
4e416fe81b
commit
311189a2e9
28 changed files with 73 additions and 290 deletions
|
|
@ -40,7 +40,7 @@ class ReportsController < ApplicationController
|
||||||
render json: ::ReportDatatable.new(
|
render json: ::ReportDatatable.new(
|
||||||
view_context,
|
view_context,
|
||||||
current_user,
|
current_user,
|
||||||
current_team.datatables_reports.visible_by(current_user, current_team)
|
Report.visible_by(current_user, current_team)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@ class ReportDatatable < CustomDatatable
|
||||||
include InputSanitizeHelper
|
include InputSanitizeHelper
|
||||||
|
|
||||||
TABLE_COLUMNS = %w(
|
TABLE_COLUMNS = %w(
|
||||||
Views::Datatables::DatatablesReport.project_name
|
Report.project_name
|
||||||
Views::Datatables::DatatablesReport.name
|
Report.name
|
||||||
Views::Datatables::DatatablesReport.created_by
|
Report.created_by
|
||||||
Views::Datatables::DatatablesReport.last_modified_by
|
Report.modified_by
|
||||||
Views::Datatables::DatatablesReport.created_at
|
Report.created_at
|
||||||
Views::Datatables::DatatablesReport.updated_at
|
Report.updated_at
|
||||||
).freeze
|
).freeze
|
||||||
|
|
||||||
def_delegator :@view, :edit_project_report_path
|
def_delegator :@view, :edit_project_report_path
|
||||||
|
|
@ -32,20 +32,32 @@ class ReportDatatable < CustomDatatable
|
||||||
def data
|
def data
|
||||||
records.map do |record|
|
records.map do |record|
|
||||||
{
|
{
|
||||||
'0' => record.id,
|
'0' => record.id,
|
||||||
'1' => sanitize_input(record.project_name),
|
'1' => sanitize_input(record.project_name),
|
||||||
'2' => sanitize_input(record.name),
|
'2' => sanitize_input(record.name),
|
||||||
'3' => sanitize_input(record.created_by),
|
'3' => sanitize_input(record.created_by),
|
||||||
'4' => sanitize_input(record.last_modified_by),
|
'4' => sanitize_input(record.modified_by),
|
||||||
'5' => I18n.l(record.created_at, format: :full),
|
'5' => I18n.l(record.created_at, format: :full),
|
||||||
'6' => I18n.l(record.updated_at, format: :full),
|
'6' => I18n.l(record.updated_at, format: :full),
|
||||||
'edit' => edit_project_report_path(record.project_id, record.id)
|
'edit' => edit_project_report_path(record.project_id, record.id)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_raw_records
|
def get_raw_records
|
||||||
@reports
|
res = @reports.joins(:project)
|
||||||
|
.joins(
|
||||||
|
'LEFT OUTER JOIN users AS creators ' \
|
||||||
|
'ON reports.user_id = creators.id'
|
||||||
|
).joins(
|
||||||
|
'LEFT OUTER JOIN users AS modifiers '\
|
||||||
|
'ON reports.last_modified_by_id = modifiers.id'
|
||||||
|
)
|
||||||
|
.select('reports.* AS reports')
|
||||||
|
.select('projects.name AS project_name')
|
||||||
|
.select('creators.full_name AS created_by')
|
||||||
|
.select('modifiers.full_name AS modified_by')
|
||||||
|
Report.from(res, :reports)
|
||||||
end
|
end
|
||||||
|
|
||||||
# ==== Insert 'presenter'-like methods below if necessary
|
# ==== Insert 'presenter'-like methods below if necessary
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ class Checklist < ApplicationRecord
|
||||||
length: { maximum: Constants::TEXT_MAX_LENGTH }
|
length: { maximum: Constants::TEXT_MAX_LENGTH }
|
||||||
validates :step, presence: true
|
validates :step, presence: true
|
||||||
|
|
||||||
belongs_to :step, inverse_of: :checklists, optional: true
|
belongs_to :step, inverse_of: :checklists, touch: true, optional: true
|
||||||
belongs_to :created_by,
|
belongs_to :created_by,
|
||||||
foreign_key: 'created_by_id',
|
foreign_key: 'created_by_id',
|
||||||
class_name: 'User',
|
class_name: 'User',
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ class Experiment < ApplicationRecord
|
||||||
include ArchivableModel
|
include ArchivableModel
|
||||||
include SearchableModel
|
include SearchableModel
|
||||||
|
|
||||||
belongs_to :project, inverse_of: :experiments, optional: true
|
belongs_to :project, inverse_of: :experiments, touch: true, optional: true
|
||||||
belongs_to :created_by,
|
belongs_to :created_by,
|
||||||
foreign_key: :created_by_id,
|
foreign_key: :created_by_id,
|
||||||
class_name: 'User',
|
class_name: 'User',
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ class MyModule < ApplicationRecord
|
||||||
foreign_key: 'restored_by_id',
|
foreign_key: 'restored_by_id',
|
||||||
class_name: 'User',
|
class_name: 'User',
|
||||||
optional: true
|
optional: true
|
||||||
belongs_to :experiment, inverse_of: :my_modules, optional: true
|
belongs_to :experiment, inverse_of: :my_modules, touch: true, optional: true
|
||||||
belongs_to :my_module_group, inverse_of: :my_modules, optional: true
|
belongs_to :my_module_group, inverse_of: :my_modules, optional: true
|
||||||
has_many :results, inverse_of: :my_module, dependent: :destroy
|
has_many :results, inverse_of: :my_module, dependent: :destroy
|
||||||
has_many :my_module_tags, inverse_of: :my_module, dependent: :destroy
|
has_many :my_module_tags, inverse_of: :my_module, dependent: :destroy
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,6 @@ class Project < ApplicationRecord
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
after_commit do
|
|
||||||
Views::Datatables::DatatablesReport.refresh_materialized_view
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.visible_from_user_by_name(user, team, name)
|
def self.visible_from_user_by_name(user, team, name)
|
||||||
if user.is_admin_of_team? team
|
if user.is_admin_of_team? team
|
||||||
return where('projects.archived IS FALSE AND projects.name ILIKE ?',
|
return where('projects.archived IS FALSE AND projects.name ILIKE ?',
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ class ProjectComment < Comment
|
||||||
belongs_to :project,
|
belongs_to :project,
|
||||||
foreign_key: :associated_id,
|
foreign_key: :associated_id,
|
||||||
inverse_of: :project_comments,
|
inverse_of: :project_comments,
|
||||||
|
touch: true,
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
validates :project, presence: true
|
validates :project, presence: true
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,6 @@ class Report < ApplicationRecord
|
||||||
# or many module elements (if grouped by module)
|
# or many module elements (if grouped by module)
|
||||||
has_many :report_elements, inverse_of: :report, dependent: :delete_all
|
has_many :report_elements, inverse_of: :report, dependent: :delete_all
|
||||||
|
|
||||||
after_commit do
|
|
||||||
Views::Datatables::DatatablesReport.refresh_materialized_view
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.search(
|
def self.search(
|
||||||
user,
|
user,
|
||||||
include_archived,
|
include_archived,
|
||||||
|
|
@ -56,6 +52,20 @@ class Report < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.visible_by(user, team)
|
||||||
|
projects = team.projects.joins(
|
||||||
|
'LEFT OUTER JOIN user_projects ON user_projects.project_id = projects.id'
|
||||||
|
).where(archived: false)
|
||||||
|
|
||||||
|
# Only admins see all projects of the team
|
||||||
|
unless user.is_admin_of_team?(team)
|
||||||
|
projects = projects.where(
|
||||||
|
'visibility = 1 OR user_projects.user_id = :user_id', user_id: user.id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
where(project: projects)
|
||||||
|
end
|
||||||
|
|
||||||
def root_elements
|
def root_elements
|
||||||
(report_elements.order(:position)).select { |el| el.parent.blank? }
|
(report_elements.order(:position)).select { |el| el.parent.blank? }
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
class ResultAsset < ApplicationRecord
|
class ResultAsset < ApplicationRecord
|
||||||
validates :result, :asset, presence: true
|
validates :result, :asset, presence: true
|
||||||
|
|
||||||
belongs_to :result, inverse_of: :result_asset, optional: true
|
belongs_to :result, inverse_of: :result_asset, touch: true, optional: true
|
||||||
belongs_to :asset,
|
belongs_to :asset,
|
||||||
inverse_of: :result_asset,
|
inverse_of: :result_asset,
|
||||||
dependent: :destroy,
|
dependent: :destroy,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ class ResultComment < Comment
|
||||||
belongs_to :result,
|
belongs_to :result,
|
||||||
foreign_key: :associated_id,
|
foreign_key: :associated_id,
|
||||||
inverse_of: :result_comments,
|
inverse_of: :result_comments,
|
||||||
|
touch: true,
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
validates :result, presence: true
|
validates :result, presence: true
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,6 @@ class ResultTable < ApplicationRecord
|
||||||
belongs_to :table,
|
belongs_to :table,
|
||||||
inverse_of: :result_table,
|
inverse_of: :result_table,
|
||||||
dependent: :destroy,
|
dependent: :destroy,
|
||||||
|
touch: true,
|
||||||
optional: true
|
optional: true
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,6 @@ class ResultText < ApplicationRecord
|
||||||
presence: true,
|
presence: true,
|
||||||
length: { maximum: Constants::RICH_TEXT_MAX_LENGTH }
|
length: { maximum: Constants::RICH_TEXT_MAX_LENGTH }
|
||||||
validates :result, presence: true
|
validates :result, presence: true
|
||||||
belongs_to :result, inverse_of: :result_text, optional: true
|
belongs_to :result, inverse_of: :result_text, touch: true, optional: true
|
||||||
has_many :tiny_mce_assets, inverse_of: :result_text, dependent: :destroy
|
has_many :tiny_mce_assets, inverse_of: :result_text, dependent: :destroy
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ class StepAsset < ApplicationRecord
|
||||||
|
|
||||||
belongs_to :step,
|
belongs_to :step,
|
||||||
inverse_of: :step_assets,
|
inverse_of: :step_assets,
|
||||||
|
touch: true,
|
||||||
optional: true
|
optional: true
|
||||||
belongs_to :asset,
|
belongs_to :asset,
|
||||||
inverse_of: :step_asset,
|
inverse_of: :step_asset,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ class StepComment < Comment
|
||||||
belongs_to :step,
|
belongs_to :step,
|
||||||
foreign_key: :associated_id,
|
foreign_key: :associated_id,
|
||||||
inverse_of: :step_comments,
|
inverse_of: :step_comments,
|
||||||
|
touch: true,
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
validates :step, presence: true
|
validates :step, presence: true
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
class StepTable < ApplicationRecord
|
class StepTable < ApplicationRecord
|
||||||
validates :step, :table, presence: true
|
validates :step, :table, presence: true
|
||||||
|
|
||||||
belongs_to :step, inverse_of: :step_tables, optional: true
|
belongs_to :step, inverse_of: :step_tables, touch: true, optional: true
|
||||||
belongs_to :table, inverse_of: :step_table, optional: true
|
belongs_to :table, inverse_of: :step_table, optional: true
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -34,12 +34,6 @@ class Team < ApplicationRecord
|
||||||
has_many :tiny_mce_assets, inverse_of: :team, dependent: :destroy
|
has_many :tiny_mce_assets, inverse_of: :team, dependent: :destroy
|
||||||
has_many :repositories, dependent: :destroy
|
has_many :repositories, dependent: :destroy
|
||||||
has_many :reports, inverse_of: :team, dependent: :destroy
|
has_many :reports, inverse_of: :team, dependent: :destroy
|
||||||
has_many :datatables_reports,
|
|
||||||
class_name: 'Views::Datatables::DatatablesReport'
|
|
||||||
|
|
||||||
after_commit do
|
|
||||||
Views::Datatables::DatatablesReport.refresh_materialized_view
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_view_state
|
def default_view_state
|
||||||
{ 'projects' =>
|
{ 'projects' =>
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,10 @@ class TinyMceAsset < ApplicationRecord
|
||||||
after_destroy :release_team_space
|
after_destroy :release_team_space
|
||||||
|
|
||||||
belongs_to :team, inverse_of: :tiny_mce_assets, optional: true
|
belongs_to :team, inverse_of: :tiny_mce_assets, optional: true
|
||||||
belongs_to :step, inverse_of: :tiny_mce_assets, optional: true
|
belongs_to :step, inverse_of: :tiny_mce_assets, touch: true, optional: true
|
||||||
belongs_to :result_text,
|
belongs_to :result_text,
|
||||||
inverse_of: :tiny_mce_assets,
|
inverse_of: :tiny_mce_assets,
|
||||||
|
touch: true,
|
||||||
optional: true
|
optional: true
|
||||||
has_attached_file :image,
|
has_attached_file :image,
|
||||||
styles: { large: [Constants::LARGE_PIC_FORMAT, :jpg] },
|
styles: { large: [Constants::LARGE_PIC_FORMAT, :jpg] },
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,12 @@ class UserMyModule < ApplicationRecord
|
||||||
validates :user, presence: true, uniqueness: { scope: :my_module }
|
validates :user, presence: true, uniqueness: { scope: :my_module }
|
||||||
validates :my_module, presence: true
|
validates :my_module, presence: true
|
||||||
|
|
||||||
belongs_to :user, inverse_of: :user_my_modules, optional: true
|
belongs_to :user, inverse_of: :user_my_modules, touch: true, optional: true
|
||||||
belongs_to :assigned_by,
|
belongs_to :assigned_by,
|
||||||
foreign_key: 'assigned_by_id',
|
foreign_key: 'assigned_by_id',
|
||||||
class_name: 'User',
|
class_name: 'User',
|
||||||
optional: true
|
optional: true
|
||||||
belongs_to :my_module, inverse_of: :user_my_modules,
|
belongs_to :my_module, inverse_of: :user_my_modules,
|
||||||
|
touch: true,
|
||||||
optional: true
|
optional: true
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,19 +5,15 @@ class UserProject < ApplicationRecord
|
||||||
validates :user, presence: true, uniqueness: { scope: :project }
|
validates :user, presence: true, uniqueness: { scope: :project }
|
||||||
validates :project, presence: true
|
validates :project, presence: true
|
||||||
|
|
||||||
belongs_to :user, inverse_of: :user_projects, optional: true
|
belongs_to :user, inverse_of: :user_projects, touch: true, optional: true
|
||||||
belongs_to :assigned_by,
|
belongs_to :assigned_by,
|
||||||
foreign_key: 'assigned_by_id',
|
foreign_key: 'assigned_by_id',
|
||||||
class_name: 'User',
|
class_name: 'User',
|
||||||
optional: true
|
optional: true
|
||||||
belongs_to :project, inverse_of: :user_projects, optional: true
|
belongs_to :project, inverse_of: :user_projects, touch: true, optional: true
|
||||||
|
|
||||||
before_destroy :destroy_associations
|
before_destroy :destroy_associations
|
||||||
|
|
||||||
after_commit do
|
|
||||||
Views::Datatables::DatatablesReport.refresh_materialized_view
|
|
||||||
end
|
|
||||||
|
|
||||||
def role_str
|
def role_str
|
||||||
I18n.t("user_projects.enums.role.#{role.to_s}")
|
I18n.t("user_projects.enums.role.#{role.to_s}")
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ class UserTeam < ApplicationRecord
|
||||||
validates :user, presence: true
|
validates :user, presence: true
|
||||||
validates :team, presence: true
|
validates :team, presence: true
|
||||||
|
|
||||||
belongs_to :user, inverse_of: :user_teams, optional: true
|
belongs_to :user, inverse_of: :user_teams, touch: true, optional: true
|
||||||
belongs_to :assigned_by,
|
belongs_to :assigned_by,
|
||||||
foreign_key: 'assigned_by_id',
|
foreign_key: 'assigned_by_id',
|
||||||
class_name: 'User',
|
class_name: 'User',
|
||||||
|
|
@ -15,10 +15,6 @@ class UserTeam < ApplicationRecord
|
||||||
before_destroy :destroy_associations
|
before_destroy :destroy_associations
|
||||||
after_create :create_samples_table_state
|
after_create :create_samples_table_state
|
||||||
|
|
||||||
after_commit do
|
|
||||||
Views::Datatables::DatatablesReport.refresh_materialized_view
|
|
||||||
end
|
|
||||||
|
|
||||||
def role_str
|
def role_str
|
||||||
I18n.t("user_teams.enums.role.#{role}")
|
I18n.t("user_teams.enums.role.#{role}")
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,83 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Views
|
|
||||||
module Datatables
|
|
||||||
class DatatablesReport < ApplicationRecord
|
|
||||||
belongs_to :team
|
|
||||||
|
|
||||||
# this isn't strictly necessary, but it will prevent
|
|
||||||
# rails from calling save, which would fail anyway.
|
|
||||||
def readonly?
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
class << self
|
|
||||||
def visible_by(user, team)
|
|
||||||
permitted_by_team = get_permitted_by_team_tokenized
|
|
||||||
permitted_by_project = get_permitted_by_project_tokenized
|
|
||||||
if user.is_admin_of_team? team
|
|
||||||
allowed_ids = for_admin(user, permitted_by_team)
|
|
||||||
else
|
|
||||||
allowed_ids = for_non_admin(
|
|
||||||
user, permitted_by_team, permitted_by_project
|
|
||||||
)
|
|
||||||
end
|
|
||||||
where(id: allowed_ids).where(project_archived: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
def refresh_materialized_view
|
|
||||||
Scenic.database.refresh_materialized_view(:datatables_reports,
|
|
||||||
concurrently: true,
|
|
||||||
cascade: false)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
PermissionItem = Struct.new(:report_id, :users_ids, :visibility)
|
|
||||||
|
|
||||||
def tokenize(items)
|
|
||||||
items.collect do |item|
|
|
||||||
PermissionItem.new(item[0], item[1], item[2])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_permitted_by_team_tokenized
|
|
||||||
tokenize(pluck(:id, :users_with_team_read_permissions, :project_visibility))
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_permitted_by_project_tokenized
|
|
||||||
tokenize(pluck(:id, :users_with_project_read_permissions, :project_visibility))
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_by_project_item(permitted_by_project, item)
|
|
||||||
permitted_by_project.select { |el| el.report_id == item.report_id }
|
|
||||||
.first
|
|
||||||
end
|
|
||||||
|
|
||||||
def for_admin(user, permitted_by_team)
|
|
||||||
allowed_ids = []
|
|
||||||
permitted_by_team.each do |item|
|
|
||||||
next unless user.id.in? item.users_ids
|
|
||||||
allowed_ids << item.report_id
|
|
||||||
end
|
|
||||||
allowed_ids
|
|
||||||
end
|
|
||||||
|
|
||||||
def for_non_admin(user, permitted_by_team, permitted_by_project)
|
|
||||||
allowed_ids = []
|
|
||||||
permitted_by_team.each do |item|
|
|
||||||
next unless user.id.in? item.users_ids
|
|
||||||
by_project = get_by_project_item(permitted_by_project, item)
|
|
||||||
next unless user_can_view?(user, by_project)
|
|
||||||
allowed_ids << item.report_id
|
|
||||||
end
|
|
||||||
allowed_ids
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_can_view?(user, by_project)
|
|
||||||
user.id.in?(by_project.users_ids) || by_project.visibility == 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
<% cache [current_user, project] do %>
|
||||||
<% active = !project.archived %>
|
<% active = !project.archived %>
|
||||||
<% if (active && (can_manage_project?(project) || can_archive_project?(project))) || (!active && can_restore_project?(project)) %>
|
<% if (active && (can_manage_project?(project) || can_archive_project?(project))) || (!active && can_restore_project?(project)) %>
|
||||||
<div class="dropdown pull-right">
|
<div class="dropdown pull-right">
|
||||||
|
|
@ -35,3 +36,4 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<% projects.each_with_index do |project, i| %>
|
<% projects.each_with_index do |project, i| %>
|
||||||
<div class="col-lg-3 col-md-4 col-sm-6 col-xs-12">
|
<div class="col-lg-3 col-md-4 col-sm-6 col-xs-12">
|
||||||
|
<% cache [current_user, project] do %>
|
||||||
<%= render partial: "projects/index/project.html.erb", locals: { project: project } %>
|
<%= render partial: "projects/index/project.html.erb", locals: { project: project } %>
|
||||||
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
<% if (i + 1) % 4 == 0 %>
|
<% if (i + 1) % 4 == 0 %>
|
||||||
<div class="clearfix visible-lg-block"></div>
|
<div class="clearfix visible-lg-block"></div>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
class CreateDatatablesReports < ActiveRecord::Migration[5.1]
|
|
||||||
def change
|
|
||||||
create_view :datatables_reports, materialized: true
|
|
||||||
add_index :datatables_reports, :id, unique: true
|
|
||||||
add_index :datatables_reports, :team_id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RemoveDatatablesReportsView < ActiveRecord::Migration[5.1]
|
||||||
|
def up
|
||||||
|
ActiveRecord::Base.connection.execute(
|
||||||
|
'DROP MATERIALIZED VIEW IF EXISTS datatables_reports'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
34
db/schema.rb
34
db/schema.rb
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 20181008130519) do
|
ActiveRecord::Schema.define(version: 20181212162649) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
|
@ -1038,36 +1038,4 @@ ActiveRecord::Schema.define(version: 20181008130519) do
|
||||||
JOIN user_teams ON ((teams.id = user_teams.team_id)));
|
JOIN user_teams ON ((teams.id = user_teams.team_id)));
|
||||||
SQL
|
SQL
|
||||||
|
|
||||||
create_view "datatables_reports", materialized: true, sql_definition: <<-SQL
|
|
||||||
SELECT DISTINCT ON (reports.id) reports.id,
|
|
||||||
reports.name,
|
|
||||||
projects.name AS project_name,
|
|
||||||
( SELECT users.full_name
|
|
||||||
FROM users
|
|
||||||
WHERE (users.id = reports.user_id)) AS created_by,
|
|
||||||
( SELECT users.full_name
|
|
||||||
FROM users
|
|
||||||
WHERE (users.id = reports.last_modified_by_id)) AS last_modified_by,
|
|
||||||
reports.created_at,
|
|
||||||
reports.updated_at,
|
|
||||||
projects.archived AS project_archived,
|
|
||||||
projects.visibility AS project_visibility,
|
|
||||||
projects.id AS project_id,
|
|
||||||
reports.team_id,
|
|
||||||
ARRAY( SELECT DISTINCT user_teams_1.user_id
|
|
||||||
FROM user_teams user_teams_1
|
|
||||||
WHERE (user_teams_1.team_id = teams.id)) AS users_with_team_read_permissions,
|
|
||||||
ARRAY( SELECT DISTINCT user_projects_1.user_id
|
|
||||||
FROM user_projects user_projects_1
|
|
||||||
WHERE (user_projects_1.project_id = projects.id)) AS users_with_project_read_permissions
|
|
||||||
FROM ((((reports
|
|
||||||
JOIN projects ON ((projects.id = reports.project_id)))
|
|
||||||
JOIN user_projects ON ((user_projects.project_id = projects.id)))
|
|
||||||
JOIN teams ON ((teams.id = projects.team_id)))
|
|
||||||
JOIN user_teams ON ((user_teams.team_id = teams.id)));
|
|
||||||
SQL
|
|
||||||
|
|
||||||
add_index "datatables_reports", ["id"], name: "index_datatables_reports_on_id", unique: true
|
|
||||||
add_index "datatables_reports", ["team_id"], name: "index_datatables_reports_on_team_id"
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
SELECT DISTINCT ON (id)
|
|
||||||
reports.id AS id,
|
|
||||||
reports.name AS name,
|
|
||||||
projects.name AS project_name,
|
|
||||||
(
|
|
||||||
SELECT users.full_name
|
|
||||||
FROM users
|
|
||||||
WHERE users.id = reports.user_id
|
|
||||||
) AS created_by,
|
|
||||||
(
|
|
||||||
SELECT users.full_name
|
|
||||||
FROM users
|
|
||||||
WHERE users.id = reports.last_modified_by_id
|
|
||||||
) AS last_modified_by,
|
|
||||||
reports.created_at AS created_at,
|
|
||||||
reports.updated_at AS updated_at,
|
|
||||||
projects.archived AS project_archived,
|
|
||||||
projects.visibility AS project_visibility,
|
|
||||||
projects.id AS project_id,
|
|
||||||
reports.team_id AS team_id,
|
|
||||||
ARRAY(
|
|
||||||
SELECT DISTINCT user_teams.user_id
|
|
||||||
FROM user_teams
|
|
||||||
WHERE user_teams.team_id = teams.id
|
|
||||||
) AS users_with_team_read_permissions,
|
|
||||||
ARRAY(
|
|
||||||
SELECT DISTINCT user_projects.user_id
|
|
||||||
FROM user_projects
|
|
||||||
WHERE user_projects.project_id = projects.id
|
|
||||||
) AS users_with_project_read_permissions
|
|
||||||
FROM reports
|
|
||||||
INNER JOIN projects
|
|
||||||
ON projects.id = reports.project_id
|
|
||||||
INNER JOIN user_projects
|
|
||||||
ON user_projects.project_id = projects.id
|
|
||||||
INNER JOIN teams
|
|
||||||
ON teams.id = projects.team_id
|
|
||||||
INNER JOIN user_teams
|
|
||||||
ON user_teams.team_id = teams.id
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Views::Datatables::DatatablesReport, type: :model do
|
|
||||||
describe 'Database table' do
|
|
||||||
it { should have_db_column :id }
|
|
||||||
it { should have_db_column :name }
|
|
||||||
it { should have_db_column :project_name }
|
|
||||||
it { should have_db_column :created_by }
|
|
||||||
it { should have_db_column :last_modified_by }
|
|
||||||
it { should have_db_column :created_at }
|
|
||||||
it { should have_db_column :updated_at }
|
|
||||||
it { should have_db_column :project_archived }
|
|
||||||
it { should have_db_column :project_visibility }
|
|
||||||
it { should have_db_column :project_id }
|
|
||||||
it { should have_db_column :team_id }
|
|
||||||
it { should have_db_column :users_with_team_read_permissions }
|
|
||||||
it { should have_db_column :users_with_project_read_permissions }
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'is readonly' do
|
|
||||||
let(:team) { create :team }
|
|
||||||
it do
|
|
||||||
expect {
|
|
||||||
Views::Datatables::DatatablesReport.create!(team: team)
|
|
||||||
}.to raise_error(
|
|
||||||
ActiveRecord::ReadOnlyRecord,
|
|
||||||
'Views::Datatables::DatatablesReport is marked as readonly'
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#tokenize/1' do
|
|
||||||
it 'returns an array of permission items' do
|
|
||||||
items = [[1, [1, 2]], [2, [3, 4]]]
|
|
||||||
tokenized = described_class.send('tokenize', items)
|
|
||||||
expect(tokenized.first).to have_attributes(report_id: 1,
|
|
||||||
users_ids: [1, 2])
|
|
||||||
expect(tokenized.last).to have_attributes(report_id: 2, users_ids: [3, 4])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#for_admin/3' do
|
|
||||||
let!(:user) { create :user }
|
|
||||||
let!(:user_two) { create :user, email: 'user.two@asdf.com' }
|
|
||||||
let!(:team) { create :team }
|
|
||||||
let!(:project) do
|
|
||||||
create :project, created_by: user, last_modified_by: user, team: team
|
|
||||||
end
|
|
||||||
let!(:user_project) { create :user_project, project: project, user: user }
|
|
||||||
let!(:team_two) { create :team }
|
|
||||||
let!(:user_team) do
|
|
||||||
create :user_team, team: team_two, user: user_two, role: 0
|
|
||||||
end
|
|
||||||
let!(:user_team) { create :user_team, team: team, user: user, role: 2 }
|
|
||||||
|
|
||||||
let!(:project_two) do
|
|
||||||
create :project, created_by: user_two,
|
|
||||||
last_modified_by: user_two,
|
|
||||||
team: team_two
|
|
||||||
end
|
|
||||||
let!(:report_one) do
|
|
||||||
create :report, user: user,
|
|
||||||
team: team,
|
|
||||||
name: 'report one',
|
|
||||||
project: project
|
|
||||||
end
|
|
||||||
let!(:report_two) do
|
|
||||||
create :report, user: user,
|
|
||||||
team: team_two,
|
|
||||||
project: project_two,
|
|
||||||
name: 'report two'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'returns the reports' do
|
|
||||||
reports = team.datatables_reports.visible_by(user, team)
|
|
||||||
expect(reports.length).to eq 1
|
|
||||||
expect(reports.first.id).to eq report_one.id
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
Loading…
Add table
Reference in a new issue