mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-03 02:14:29 +08:00
adds unit specs for datatable service [fixes SCI-2068]
This commit is contained in:
parent
26883af386
commit
4f951f6679
11 changed files with 260 additions and 370 deletions
|
@ -16,7 +16,6 @@ class MyModulesController < ApplicationController
|
||||||
before_action :load_repository, only: %i(assign_repository_records
|
before_action :load_repository, only: %i(assign_repository_records
|
||||||
unassign_repository_records
|
unassign_repository_records
|
||||||
repository_index)
|
repository_index)
|
||||||
before_action :load_columns_mappings, only: :repository_index
|
|
||||||
before_action :check_manage_permissions,
|
before_action :check_manage_permissions,
|
||||||
only: %i(update destroy description due_date)
|
only: %i(update destroy description due_date)
|
||||||
before_action :check_view_info_permissions, only: :show
|
before_action :check_view_info_permissions, only: :show
|
||||||
|
@ -373,11 +372,11 @@ class MyModulesController < ApplicationController
|
||||||
page = (params[:start].to_i / per_page) + 1
|
page = (params[:start].to_i / per_page) + 1
|
||||||
records = RepositoryDatatableService.new(@repository,
|
records = RepositoryDatatableService.new(@repository,
|
||||||
params,
|
params,
|
||||||
@columns_mappings,
|
|
||||||
current_user,
|
current_user,
|
||||||
@my_module)
|
@my_module)
|
||||||
@assigned_rows = records.assigned_rows
|
@assigned_rows = records.assigned_rows
|
||||||
@repository_row_count = records.repository_rows.count
|
@repository_row_count = records.repository_rows.count
|
||||||
|
@columns_mappings = records.mappings
|
||||||
@repository_rows = records.repository_rows.page(page).per(per_page)
|
@repository_rows = records.repository_rows.page(page).per(per_page)
|
||||||
render 'repository_rows/index.json'
|
render 'repository_rows/index.json'
|
||||||
end
|
end
|
||||||
|
@ -602,16 +601,6 @@ class MyModulesController < ApplicationController
|
||||||
render_403 unless can_read_team?(@repository.team)
|
render_403 unless can_read_team?(@repository.team)
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_columns_mappings
|
|
||||||
# Make mappings of custom columns, so we have same id for every column
|
|
||||||
i = 5
|
|
||||||
@columns_mappings = {}
|
|
||||||
@repository.repository_columns.order(:id).each do |column|
|
|
||||||
@columns_mappings[column.id] = i.to_s
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_manage_permissions
|
def check_manage_permissions
|
||||||
render_403 unless can_manage_module?(@my_module)
|
render_403 unless can_manage_module?(@my_module)
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,7 +6,6 @@ class RepositoryRowsController < ApplicationController
|
||||||
before_action :load_info_modal_vars, only: :show
|
before_action :load_info_modal_vars, only: :show
|
||||||
before_action :load_vars, only: %i(edit update)
|
before_action :load_vars, only: %i(edit update)
|
||||||
before_action :load_repository, only: %i(create delete_records index)
|
before_action :load_repository, only: %i(create delete_records index)
|
||||||
before_action :load_columns_mappings, only: :index
|
|
||||||
before_action :check_create_permissions, only: :create
|
before_action :check_create_permissions, only: :create
|
||||||
before_action :check_edit_permissions, only: %i(edit update)
|
before_action :check_edit_permissions, only: %i(edit update)
|
||||||
before_action :check_destroy_permissions, only: :delete_records
|
before_action :check_destroy_permissions, only: :delete_records
|
||||||
|
@ -17,10 +16,10 @@ class RepositoryRowsController < ApplicationController
|
||||||
page = (params[:start].to_i / per_page) + 1
|
page = (params[:start].to_i / per_page) + 1
|
||||||
records = RepositoryDatatableService.new(@repository,
|
records = RepositoryDatatableService.new(@repository,
|
||||||
params,
|
params,
|
||||||
@columns_mappings,
|
|
||||||
current_user)
|
current_user)
|
||||||
@assigned_rows = records.assigned_rows
|
@assigned_rows = records.assigned_rows
|
||||||
@repository_row_count = records.repository_rows.count
|
@repository_row_count = records.repository_rows.count
|
||||||
|
@columns_mappings = records.mappings
|
||||||
@repository_rows = records.repository_rows.page(page).per(per_page)
|
@repository_rows = records.repository_rows.page(page).per(per_page)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -255,16 +254,6 @@ class RepositoryRowsController < ApplicationController
|
||||||
render_403 unless can_read_team?(@repository.team)
|
render_403 unless can_read_team?(@repository.team)
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_columns_mappings
|
|
||||||
# Make mappings of custom columns, so we have same id for every column
|
|
||||||
i = 5
|
|
||||||
@columns_mappings = {}
|
|
||||||
@repository.repository_columns.order(:id).each do |column|
|
|
||||||
@columns_mappings[column.id] = i.to_s
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_create_permissions
|
def check_create_permissions
|
||||||
render_403 unless can_manage_repository_rows?(@repository.team)
|
render_403 unless can_manage_repository_rows?(@repository.team)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,332 +0,0 @@
|
||||||
require 'active_record'
|
|
||||||
|
|
||||||
class RepositoryDatatable < CustomDatatable
|
|
||||||
include ActionView::Helpers::TextHelper
|
|
||||||
include SamplesHelper
|
|
||||||
include InputSanitizeHelper
|
|
||||||
include Rails.application.routes.url_helpers
|
|
||||||
include ActionView::Helpers::UrlHelper
|
|
||||||
include ApplicationHelper
|
|
||||||
include ActiveRecord::Sanitization::ClassMethods
|
|
||||||
|
|
||||||
ASSIGNED_SORT_COL = 'assigned'.freeze
|
|
||||||
|
|
||||||
REPOSITORY_TABLE_DEFAULT_STATE = {
|
|
||||||
'time' => 0,
|
|
||||||
'start' => 0,
|
|
||||||
'length' => 5,
|
|
||||||
'order' => [[2, 'desc']],
|
|
||||||
'search' => { 'search' => '',
|
|
||||||
'smart' => true,
|
|
||||||
'regex' => false,
|
|
||||||
'caseInsensitive' => true },
|
|
||||||
'columns' => [],
|
|
||||||
'assigned' => 'assigned',
|
|
||||||
'ColReorder' => [*0..4]
|
|
||||||
}
|
|
||||||
5.times do
|
|
||||||
REPOSITORY_TABLE_DEFAULT_STATE['columns'] << {
|
|
||||||
'visible' => true,
|
|
||||||
'search' => { 'search' => '',
|
|
||||||
'smart' => true,
|
|
||||||
'regex' => false,
|
|
||||||
'caseInsensitive' => true }
|
|
||||||
}
|
|
||||||
end
|
|
||||||
REPOSITORY_TABLE_DEFAULT_STATE.freeze
|
|
||||||
|
|
||||||
def initialize(view,
|
|
||||||
repository,
|
|
||||||
my_module = nil,
|
|
||||||
user = nil)
|
|
||||||
super(view)
|
|
||||||
@repository = repository
|
|
||||||
@team = repository.team
|
|
||||||
@my_module = my_module
|
|
||||||
@user = user
|
|
||||||
end
|
|
||||||
|
|
||||||
# Define sortable columns, so 1st column will be sorted by attribute
|
|
||||||
# in sortable_columns[0]
|
|
||||||
def sortable_columns
|
|
||||||
sort_array = [
|
|
||||||
ASSIGNED_SORT_COL,
|
|
||||||
'RepositoryRow.name',
|
|
||||||
'RepositoryRow.created_at',
|
|
||||||
'User.full_name'
|
|
||||||
]
|
|
||||||
|
|
||||||
sort_array.push(*repository_columns_sort_by)
|
|
||||||
@sortable_columns = sort_array
|
|
||||||
end
|
|
||||||
|
|
||||||
# Define attributes on which we perform search
|
|
||||||
def searchable_columns
|
|
||||||
search_array = [
|
|
||||||
'RepositoryRow.name',
|
|
||||||
'RepositoryRow.created_at',
|
|
||||||
'User.full_name'
|
|
||||||
]
|
|
||||||
|
|
||||||
# search_array.push(*repository_columns_sort_by)
|
|
||||||
@searchable_columns ||= filter_search_array search_array
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
# filters the search array by checking if the the column is visible
|
|
||||||
def filter_search_array(input_array)
|
|
||||||
param_index = 2
|
|
||||||
filtered_array = []
|
|
||||||
input_array.each do |col|
|
|
||||||
next if columns_params.to_a[param_index].nil?
|
|
||||||
params_col =
|
|
||||||
columns_params.to_a.find { |v| v[1]['data'] == param_index.to_s }
|
|
||||||
filtered_array.push(col) unless params_col[1]['searchable'] == 'false'
|
|
||||||
param_index += 1
|
|
||||||
end
|
|
||||||
filtered_array
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get array of columns to sort by (for custom columns)
|
|
||||||
def repository_columns_sort_by
|
|
||||||
array = []
|
|
||||||
@repository.repository_columns.count.times do
|
|
||||||
array << 'RepositoryCell.value'
|
|
||||||
end
|
|
||||||
array
|
|
||||||
end
|
|
||||||
|
|
||||||
# Returns json of current repository rows (already paginated)
|
|
||||||
def data
|
|
||||||
records.map do |record|
|
|
||||||
row = {
|
|
||||||
'DT_RowId': record.id,
|
|
||||||
'1': assigned_row(record),
|
|
||||||
'2': escape_input(record.name),
|
|
||||||
'3': I18n.l(record.created_at, format: :full),
|
|
||||||
'4': escape_input(record.created_by.full_name),
|
|
||||||
'recordEditUrl':
|
|
||||||
Rails.application.routes.url_helpers
|
|
||||||
.edit_repository_repository_row_path(@repository,
|
|
||||||
record.id),
|
|
||||||
'recordUpdateUrl':
|
|
||||||
Rails.application.routes.url_helpers
|
|
||||||
.repository_repository_row_path(@repository, record.id),
|
|
||||||
'recordInfoUrl':
|
|
||||||
Rails.application.routes.url_helpers.repository_row_path(record.id)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add custom columns
|
|
||||||
record.repository_cells.each do |cell|
|
|
||||||
row[@columns_mappings[cell.repository_column.id]] =
|
|
||||||
custom_auto_link(
|
|
||||||
display_tooltip(cell.value.data,
|
|
||||||
Constants::NAME_MAX_LENGTH),
|
|
||||||
simple_format: true,
|
|
||||||
team: @team
|
|
||||||
)
|
|
||||||
end
|
|
||||||
row
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def assigned_row(record)
|
|
||||||
if @assigned_rows && @assigned_rows.include?(record)
|
|
||||||
"<span class='circle'> </span>"
|
|
||||||
else
|
|
||||||
"<span class='circle disabled'> </span>"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Query database for records (this will be later paginated and filtered)
|
|
||||||
# after that "data" function will return json
|
|
||||||
def get_raw_records
|
|
||||||
repository_rows = RepositoryRow
|
|
||||||
.preload(
|
|
||||||
:repository_columns,
|
|
||||||
:created_by,
|
|
||||||
repository_cells: :value
|
|
||||||
)
|
|
||||||
.joins(:created_by)
|
|
||||||
.where(repository: @repository)
|
|
||||||
|
|
||||||
# Make mappings of custom columns, so we have same id for every column
|
|
||||||
i = 5
|
|
||||||
@columns_mappings = {}
|
|
||||||
@repository.repository_columns.order(:id).each do |column|
|
|
||||||
@columns_mappings[column.id] = i.to_s
|
|
||||||
i += 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if @my_module
|
|
||||||
@assigned_rows = @my_module.repository_rows
|
|
||||||
.preload(
|
|
||||||
:repository_columns,
|
|
||||||
:created_by,
|
|
||||||
repository_cells: :value
|
|
||||||
)
|
|
||||||
.joins(:created_by)
|
|
||||||
.where(repository: @repository)
|
|
||||||
return @assigned_rows if dt_params[:assigned] == 'assigned'
|
|
||||||
else
|
|
||||||
@assigned_rows = repository_rows.joins(
|
|
||||||
'INNER JOIN my_module_repository_rows ON
|
|
||||||
(repository_rows.id = my_module_repository_rows.repository_row_id)'
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
repository_rows
|
|
||||||
end
|
|
||||||
|
|
||||||
# Override default behaviour
|
|
||||||
# Don't filter and paginate records when sorting by custom column - everything
|
|
||||||
# is done in sort_records method - you might ask why, well if you want the
|
|
||||||
# number of samples/all samples it's dependant upon sort_record query
|
|
||||||
def fetch_records
|
|
||||||
records = get_raw_records
|
|
||||||
records = filter_records(records) if dt_params[:search].present? &&
|
|
||||||
dt_params[:search][:value].present?
|
|
||||||
records = sort_records(records) if order_params.present?
|
|
||||||
records = paginate_records(records) unless dt_params[:length].present? &&
|
|
||||||
dt_params[:length] == '-1'
|
|
||||||
escape_special_chars
|
|
||||||
records
|
|
||||||
end
|
|
||||||
|
|
||||||
# Overriden to make it work for custom columns, because they are polymorphic
|
|
||||||
# NOTE: Function assumes the provided records/rows are only from the current
|
|
||||||
# repository!
|
|
||||||
def filter_records(repo_rows)
|
|
||||||
return repo_rows unless dt_params[:search].present? &&
|
|
||||||
dt_params[:search][:value].present?
|
|
||||||
search_val = dt_params[:search][:value]
|
|
||||||
|
|
||||||
filtered_rows = repo_rows.find_by_sql(
|
|
||||||
"SELECT DISTINCT repository_rows.*
|
|
||||||
FROM repository_rows
|
|
||||||
INNER JOIN (
|
|
||||||
SELECT users.*
|
|
||||||
FROM users
|
|
||||||
) AS users
|
|
||||||
ON users.id = repository_rows.created_by_id
|
|
||||||
LEFT OUTER JOIN (
|
|
||||||
SELECT repository_cells.repository_row_id,
|
|
||||||
repository_text_values.data AS text_value,
|
|
||||||
to_char(repository_date_values.data, 'DD.MM.YYYY HH24:MI')
|
|
||||||
AS date_value
|
|
||||||
FROM repository_cells
|
|
||||||
INNER JOIN repository_text_values
|
|
||||||
ON repository_text_values.id = repository_cells.value_id
|
|
||||||
FULL OUTER JOIN repository_date_values
|
|
||||||
ON repository_date_values.id = repository_cells.value_id
|
|
||||||
) AS values
|
|
||||||
ON values.repository_row_id = repository_rows.id
|
|
||||||
WHERE repository_rows.repository_id = #{@repository.id}
|
|
||||||
AND (repository_rows.name ILIKE '%#{search_val}%'
|
|
||||||
OR to_char(repository_rows.created_at, 'DD.MM.YYYY HH24:MI')
|
|
||||||
ILIKE '%#{search_val}%'
|
|
||||||
OR users.full_name ILIKE '%#{search_val}%'
|
|
||||||
OR text_value ILIKE '%#{search_val}%'
|
|
||||||
OR date_value ILIKE '%#{search_val}%')"
|
|
||||||
)
|
|
||||||
repo_rows.where(id: filtered_rows)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Override default sort method if needed
|
|
||||||
def sort_records(records)
|
|
||||||
if sort_column(order_params) == ASSIGNED_SORT_COL
|
|
||||||
# If "assigned" column is sorted when viewing assigned items
|
|
||||||
return records if @my_module && dt_params[:assigned] == 'assigned'
|
|
||||||
# If "assigned" column is sorted
|
|
||||||
direction = sort_null_direction(order_params)
|
|
||||||
if @my_module
|
|
||||||
# Depending on the sort, order nulls first or
|
|
||||||
# nulls last on repository_cells association
|
|
||||||
records.joins(
|
|
||||||
"LEFT OUTER JOIN my_module_repository_rows ON
|
|
||||||
(repository_rows.id = my_module_repository_rows.repository_row_id
|
|
||||||
AND (my_module_repository_rows.my_module_id = #{@my_module.id} OR
|
|
||||||
my_module_repository_rows.id IS NULL))"
|
|
||||||
).order("my_module_repository_rows.id NULLS #{direction}")
|
|
||||||
else
|
|
||||||
sort_assigned_records(records, order_params['dir'])
|
|
||||||
end
|
|
||||||
elsif sorting_by_custom_column
|
|
||||||
ci = sortable_displayed_columns[
|
|
||||||
order_params['column'].to_i - 1
|
|
||||||
]
|
|
||||||
column_id = @columns_mappings.key((ci.to_i + 1).to_s)
|
|
||||||
dir = sort_direction(order_params)
|
|
||||||
|
|
||||||
records.joins(
|
|
||||||
"LEFT OUTER JOIN (SELECT repository_cells.repository_row_id,
|
|
||||||
repository_text_values.data AS value FROM repository_cells
|
|
||||||
INNER JOIN repository_text_values
|
|
||||||
ON repository_text_values.id = repository_cells.value_id
|
|
||||||
WHERE repository_cells.repository_column_id = #{column_id}) AS values
|
|
||||||
ON values.repository_row_id = repository_rows.id"
|
|
||||||
).order("values.value #{dir}")
|
|
||||||
else
|
|
||||||
super(records)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def sort_null_direction(item)
|
|
||||||
val = sort_direction(item)
|
|
||||||
val == 'ASC' ? 'LAST' : 'FIRST'
|
|
||||||
end
|
|
||||||
|
|
||||||
def inverse_sort_direction(item)
|
|
||||||
val = sort_direction(item)
|
|
||||||
val == 'ASC' ? 'DESC' : 'ASC'
|
|
||||||
end
|
|
||||||
|
|
||||||
def sorting_by_custom_column
|
|
||||||
sort_column(order_params) == 'repository_cells.value'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Escapes special characters in search query
|
|
||||||
def escape_special_chars
|
|
||||||
if dt_params[:search].present?
|
|
||||||
dt_params[:search][:value] = ActiveRecord::Base
|
|
||||||
.__send__(:sanitize_sql_like,
|
|
||||||
dt_params[:search][:value])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def new_sort_column(item)
|
|
||||||
coli = item[:column].to_i - 1
|
|
||||||
model, column = sortable_columns[sortable_displayed_columns[coli].to_i]
|
|
||||||
.split('.')
|
|
||||||
|
|
||||||
return model if model == ASSIGNED_SORT_COL
|
|
||||||
[model.constantize.table_name, column].join('.')
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate_sortable_displayed_columns
|
|
||||||
sort_order = RepositoryTableState.load_state(@user, @repository)
|
|
||||||
.first['ColReorder']
|
|
||||||
sort_order.shift
|
|
||||||
sort_order.map! { |i| (i.to_i - 1).to_s }
|
|
||||||
|
|
||||||
@sortable_displayed_columns = sort_order
|
|
||||||
end
|
|
||||||
|
|
||||||
def sort_assigned_records(records, direction)
|
|
||||||
assigned = records.joins(:my_module_repository_rows).distinct.pluck(:id)
|
|
||||||
unassigned = records.where.not(id: assigned).pluck(:id)
|
|
||||||
if direction == 'asc'
|
|
||||||
ids = assigned + unassigned
|
|
||||||
elsif direction == 'desc'
|
|
||||||
ids = unassigned + assigned
|
|
||||||
end
|
|
||||||
|
|
||||||
order_by_index = ActiveRecord::Base.send(
|
|
||||||
:sanitize_sql_array,
|
|
||||||
["position((',' || repository_rows.id || ',') in ?)",
|
|
||||||
ids.join(',') + ',']
|
|
||||||
)
|
|
||||||
records.order(order_by_index)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -43,7 +43,7 @@ class RepositoryTableState < ApplicationRecord
|
||||||
else
|
else
|
||||||
# add column
|
# add column
|
||||||
index = repository_state['columns'].count
|
index = repository_state['columns'].count
|
||||||
repository_state['columns'][index] = RepositoryDatatable::
|
repository_state['columns'][index] = Constants::
|
||||||
REPOSITORY_TABLE_DEFAULT_STATE['columns'].first
|
REPOSITORY_TABLE_DEFAULT_STATE['columns'].first
|
||||||
repository_state['ColReorder'].insert(2, index.to_s)
|
repository_state['ColReorder'].insert(2, index.to_s)
|
||||||
end
|
end
|
||||||
|
@ -52,12 +52,12 @@ class RepositoryTableState < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.create_state(user, repository)
|
def self.create_state(user, repository)
|
||||||
default_columns_num = RepositoryDatatable::
|
default_columns_num = Constants::
|
||||||
REPOSITORY_TABLE_DEFAULT_STATE['columns'].count
|
REPOSITORY_TABLE_DEFAULT_STATE['columns'].count
|
||||||
repository_state =
|
repository_state =
|
||||||
RepositoryDatatable::REPOSITORY_TABLE_DEFAULT_STATE.deep_dup
|
Constants::REPOSITORY_TABLE_DEFAULT_STATE.deep_dup
|
||||||
repository.repository_columns.each_with_index do |_, index|
|
repository.repository_columns.each_with_index do |_, index|
|
||||||
repository_state['columns'] << RepositoryDatatable::
|
repository_state['columns'] << Constants::
|
||||||
REPOSITORY_TABLE_DEFAULT_STATE['columns'].first
|
REPOSITORY_TABLE_DEFAULT_STATE['columns'].first
|
||||||
repository_state['ColReorder'] << (default_columns_num + index)
|
repository_state['ColReorder'] << (default_columns_num + index)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,19 +1,28 @@
|
||||||
class RepositoryDatatableService
|
class RepositoryDatatableService
|
||||||
|
|
||||||
attr_reader :repository_rows, :assigned_rows
|
attr_reader :repository_rows, :assigned_rows, :mappings
|
||||||
|
|
||||||
def initialize(repository, params, mappings, user, my_module = nil)
|
def initialize(repository, params, user, my_module = nil)
|
||||||
@mappings = mappings
|
|
||||||
@repository = repository
|
@repository = repository
|
||||||
@mappings = mappings
|
|
||||||
@user = user
|
@user = user
|
||||||
@my_module = my_module
|
@my_module = my_module
|
||||||
@params = params
|
@params = params
|
||||||
|
create_columns_mappings
|
||||||
process_query
|
process_query
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def create_columns_mappings
|
||||||
|
# Make mappings of custom columns, so we have same id for every column
|
||||||
|
i = 5
|
||||||
|
@mappings = {}
|
||||||
|
@repository.repository_columns.order(:id).each do |column|
|
||||||
|
@mappings[column.id] = i.to_s
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def process_query
|
def process_query
|
||||||
contitions = build_conditions(@params)
|
contitions = build_conditions(@params)
|
||||||
order_obj = contitions[:order_by_column]
|
order_obj = contitions[:order_by_column]
|
||||||
|
|
|
@ -75,7 +75,7 @@ Rails.application.configure do
|
||||||
config.assets.raise_runtime_errors = true
|
config.assets.raise_runtime_errors = true
|
||||||
|
|
||||||
# Only log info and higher on development
|
# Only log info and higher on development
|
||||||
config.log_level = :debug
|
config.log_level = :info
|
||||||
|
|
||||||
# Only allow Better Errors to work on trusted ip, use ifconfig to see which
|
# Only allow Better Errors to work on trusted ip, use ifconfig to see which
|
||||||
# one you use and put it into application.yml!
|
# one you use and put it into application.yml!
|
||||||
|
|
|
@ -859,6 +859,31 @@ class Constants
|
||||||
]
|
]
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
|
# Repository default table state
|
||||||
|
REPOSITORY_TABLE_DEFAULT_STATE = {
|
||||||
|
'time' => 0,
|
||||||
|
'start' => 0,
|
||||||
|
'length' => 5,
|
||||||
|
'order' => [[2, 'desc']],
|
||||||
|
'search' => { 'search' => '',
|
||||||
|
'smart' => true,
|
||||||
|
'regex' => false,
|
||||||
|
'caseInsensitive' => true },
|
||||||
|
'columns' => [],
|
||||||
|
'assigned' => 'assigned',
|
||||||
|
'ColReorder' => [*0..4]
|
||||||
|
}
|
||||||
|
5.times do
|
||||||
|
REPOSITORY_TABLE_DEFAULT_STATE['columns'] << {
|
||||||
|
'visible' => true,
|
||||||
|
'search' => { 'search' => '',
|
||||||
|
'smart' => true,
|
||||||
|
'regex' => false,
|
||||||
|
'caseInsensitive' => true }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
REPOSITORY_TABLE_DEFAULT_STATE.freeze
|
||||||
|
|
||||||
EXPORTABLE_ZIP_EXPIRATION_DAYS = 7
|
EXPORTABLE_ZIP_EXPIRATION_DAYS = 7
|
||||||
|
|
||||||
# Very basic regex to check for validity of emails
|
# Very basic regex to check for validity of emails
|
||||||
|
|
|
@ -7,6 +7,13 @@ describe RepositoryRowsController, type: :controller do
|
||||||
let!(:team) { create :team, created_by: user }
|
let!(:team) { create :team, created_by: user }
|
||||||
let!(:user_team) { create :user_team, team: team, user: user }
|
let!(:user_team) { create :user_team, team: team, user: user }
|
||||||
let!(:repository) { create :repository, team: team, created_by: user }
|
let!(:repository) { create :repository, team: team, created_by: user }
|
||||||
|
let!(:repository_state) do
|
||||||
|
RepositoryTableState.create(
|
||||||
|
repository: repository,
|
||||||
|
user: user,
|
||||||
|
state: Constants::REPOSITORY_TABLE_DEFAULT_STATE
|
||||||
|
)
|
||||||
|
end
|
||||||
let!(:repository_row) do
|
let!(:repository_row) do
|
||||||
create :repository_row, repository: repository,
|
create :repository_row, repository: repository,
|
||||||
created_by: user,
|
created_by: user,
|
||||||
|
@ -41,4 +48,71 @@ describe RepositoryRowsController, type: :controller do
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context '#index' do
|
||||||
|
before do
|
||||||
|
repository.repository_rows.destroy_all
|
||||||
|
110.times do |index|
|
||||||
|
create :repository_row, name: "row (#{index})",
|
||||||
|
repository: repository,
|
||||||
|
created_by: user,
|
||||||
|
last_modified_by: user
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'json object' do
|
||||||
|
it 'returns a valid object' do
|
||||||
|
params = { order: { 0 => { column: '3', dir: 'asc' } },
|
||||||
|
drow: '0',
|
||||||
|
search: { value: '' },
|
||||||
|
length: '10',
|
||||||
|
start: '1',
|
||||||
|
repository_id: repository.id }
|
||||||
|
get :index, params: params, format: :json
|
||||||
|
|
||||||
|
expect(response.status).to eq 200
|
||||||
|
expect(response).to match_response_schema('repository_row_datatables')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'pagination' do
|
||||||
|
it 'returns first 10 records' do
|
||||||
|
params = { order: { 0 => { column: '3', dir: 'asc' } },
|
||||||
|
drow: '0',
|
||||||
|
search: { value: '' },
|
||||||
|
length: '10',
|
||||||
|
start: '1',
|
||||||
|
repository_id: repository.id }
|
||||||
|
get :index, params: params, format: :json
|
||||||
|
response_body = JSON.parse(response.body)
|
||||||
|
expect(response_body['data'].length).to eq 10
|
||||||
|
expect(response_body['data'].first['2']).to eq 'row (0)'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns next 10 records' do
|
||||||
|
params = { order: { 0 => { column: '3', dir: 'asc' } },
|
||||||
|
drow: '0',
|
||||||
|
search: { value: '' },
|
||||||
|
length: '10',
|
||||||
|
start: '11',
|
||||||
|
repository_id: repository.id }
|
||||||
|
get :index, params: params, format: :json
|
||||||
|
response_body = JSON.parse(response.body)
|
||||||
|
expect(response_body['data'].length).to eq 10
|
||||||
|
expect(response_body['data'].first['2']).to eq 'row (10)'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns first 25 records' do
|
||||||
|
params = { order: { 0 => { column: '2', dir: 'desc' } },
|
||||||
|
drow: '0',
|
||||||
|
search: { value: '' },
|
||||||
|
length: '25',
|
||||||
|
start: '1',
|
||||||
|
repository_id: repository.id }
|
||||||
|
get :index, params: params, format: :json
|
||||||
|
response_body = JSON.parse(response.body)
|
||||||
|
expect(response_body['data'].length).to eq 25
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe SearchRepository, type: :model do
|
|
||||||
pending "add some examples to (or delete) #{__FILE__}"
|
|
||||||
end
|
|
116
spec/services/repository_datatable_service_spec.rb
Normal file
116
spec/services/repository_datatable_service_spec.rb
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe RepositoryDatatableService do
|
||||||
|
let!(:team) { create :team }
|
||||||
|
let!(:user) { create :user, email: 'user_one@asdf.com' }
|
||||||
|
let!(:repository) do
|
||||||
|
create :repository, name: 'my repo', created_by: user, team: team
|
||||||
|
end
|
||||||
|
let!(:repository_column) do
|
||||||
|
create :repository_column, name: 'My column', data_type: :RepositoryListValue
|
||||||
|
end
|
||||||
|
let!(:repository_state) do
|
||||||
|
RepositoryTableState.create(
|
||||||
|
repository: repository,
|
||||||
|
user: user,
|
||||||
|
state: Constants::REPOSITORY_TABLE_DEFAULT_STATE
|
||||||
|
)
|
||||||
|
end
|
||||||
|
let!(:repository_row) do
|
||||||
|
create :repository_row, name: 'A row',
|
||||||
|
repository: repository,
|
||||||
|
created_by: user,
|
||||||
|
last_modified_by: user
|
||||||
|
end
|
||||||
|
let!(:repository_row_two) do
|
||||||
|
create :repository_row, name: 'B row',
|
||||||
|
repository: repository,
|
||||||
|
created_by: user,
|
||||||
|
last_modified_by: user
|
||||||
|
end
|
||||||
|
let!(:list_item) do
|
||||||
|
create :repository_list_item, data: 'bug',
|
||||||
|
repository: repository,
|
||||||
|
repository_column: repository_column,
|
||||||
|
created_by: user,
|
||||||
|
last_modified_by: user
|
||||||
|
end
|
||||||
|
let!(:repository_list_value) do
|
||||||
|
create :repository_list_value, repository_list_item: list_item,
|
||||||
|
created_by: user,
|
||||||
|
last_modified_by: user,
|
||||||
|
repository_cell_attributes: {
|
||||||
|
repository_column: repository_column,
|
||||||
|
repository_row: repository_row
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'object' do
|
||||||
|
let(:params) do
|
||||||
|
{ order: { 0 => { column: '2', dir: 'asc' } }, search: { value: 'row' } }
|
||||||
|
end
|
||||||
|
let(:subject) { RepositoryDatatableService.new(repository, params, user) }
|
||||||
|
|
||||||
|
describe '#build_conditions/1' do
|
||||||
|
it 'parsers the contitions' do
|
||||||
|
contitions = subject.send(:build_conditions, params)
|
||||||
|
expect(contitions[:search_value]).to eq 'row'
|
||||||
|
expect(contitions[:order_by_column]).to eq({ column: 2, dir: 'asc' })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#sortable_columns' do
|
||||||
|
it 'returns an array of all columns that are sortable' do
|
||||||
|
columns = subject.send(:sortable_columns)
|
||||||
|
expect(columns.length).to eq 5
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#sort_null_direction' do
|
||||||
|
it 'returns LAST if value is ascending' do
|
||||||
|
result = subject.send(:sort_null_direction, 'ASC')
|
||||||
|
expect(result).to eq 'LAST'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns FIRST if value is not ascending' do
|
||||||
|
result = subject.send(:sort_null_direction, 'DESC')
|
||||||
|
expect(result).to eq 'FIRST'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'ordering' do
|
||||||
|
it 'is ordered by row name asc' do
|
||||||
|
params = { order: { 0 => { column: '2', dir: 'asc' } },
|
||||||
|
search: { value: '' } }
|
||||||
|
subject = RepositoryDatatableService.new(repository, params, user)
|
||||||
|
expect(subject.repository_rows.first.name).to eq 'A row'
|
||||||
|
expect(subject.repository_rows.last.name).to eq 'B row'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is ordered by row name desc' do
|
||||||
|
params = { order: { 0 => { column: '2', dir: 'desc' } },
|
||||||
|
search: { value: '' } }
|
||||||
|
subject = RepositoryDatatableService.new(repository, params, user)
|
||||||
|
expect(subject.repository_rows.first.name).to eq 'B row'
|
||||||
|
expect(subject.repository_rows.last.name).to eq 'A row'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'search' do
|
||||||
|
before do
|
||||||
|
create :repository_row, name: 'test',
|
||||||
|
repository: repository,
|
||||||
|
created_by: user,
|
||||||
|
last_modified_by: user
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns only the searched entity' do
|
||||||
|
params = { order: { 0 => { column: '2', dir: 'desc' } },
|
||||||
|
search: { value: 'test' } }
|
||||||
|
subject = RepositoryDatatableService.new(repository, params, user)
|
||||||
|
expect(subject.repository_rows.first.name).to eq 'test'
|
||||||
|
expect(subject.repository_rows.count).to eq 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
25
spec/support/api/schemas/repository_row_datatables.json
Normal file
25
spec/support/api/schemas/repository_row_datatables.json
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": ["draw", "recordsTotal", "recordsFiltered", "data"],
|
||||||
|
"properties": {
|
||||||
|
"draw": { "type": "integer" },
|
||||||
|
"recordsTotal": { "type": "integer" },
|
||||||
|
"recordsFiltered": { "type": "integer" },
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items":{
|
||||||
|
"required": ["DT_RowId", "1", "2", "3", "4", "recordEditUrl", "recordUpdateUrl", "recordInfoUrl"],
|
||||||
|
"properties": {
|
||||||
|
"DT_RowId": { "type": "integer" },
|
||||||
|
"1": { "type": "string" },
|
||||||
|
"2": { "type": "string" },
|
||||||
|
"3": { "type": "string" },
|
||||||
|
"4": { "type": "string" },
|
||||||
|
"recordEditUrl": { "type": "string" },
|
||||||
|
"recordUpdateUrl": { "type": "string" },
|
||||||
|
"recordInfoUrl": { "type": "string" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue