Add external IDs to repositories [SCI-5990]

This commit is contained in:
Oleksii Kriuchykhin 2021-08-13 14:07:01 +02:00 committed by Martin Artnik
parent 958ee92db7
commit 0b74f34de7
18 changed files with 136 additions and 48 deletions

View file

@ -27,7 +27,7 @@ class RepositoriesController < ApplicationController
def index def index
respond_to do |format| respond_to do |format|
format.html do format.html do
render 'empty_index' if Repository.accessible_by_teams(current_team).blank? render 'empty_index' if @repositories.blank?
end end
format.json do format.json do
render json: prepare_repositories_datatable(@repositories, current_team, params) render json: prepare_repositories_datatable(@repositories, current_team, params)

View file

@ -5,18 +5,15 @@ module RepositoryDatatableHelper
def prepare_row_columns(repository_rows, repository, columns_mappings, team, options = {}) def prepare_row_columns(repository_rows, repository, columns_mappings, team, options = {})
repository_rows.map do |record| repository_rows.map do |record|
row = { row = if repository.is_a?(LinkedRepository)
'DT_RowId': record.id, linked_repository_default_columns(record)
'DT_RowAttr': { 'data-state': row_style(record) }, else
'1': assigned_row(record), repository_default_columns(record)
'2': record.code, end
'3': escape_input(record.name),
'4': I18n.l(record.created_at, format: :full), row['DT_RowId'] = record.id
'5': escape_input(record.created_by.full_name), row['DT_RowAttr'] = { 'data-state': row_style(record) }
'6': (record.archived_on ? I18n.l(record.archived_on, format: :full) : ''), row['recordInfoUrl'] = Rails.application.routes.url_helpers.repository_repository_row_path(repository, record)
'7': escape_input(record.archived_by&.full_name),
'recordInfoUrl': Rails.application.routes.url_helpers.repository_repository_row_path(repository, record)
}
unless options[:view_mode] unless options[:view_mode]
row['recordUpdateUrl'] = row['recordUpdateUrl'] =
@ -84,20 +81,29 @@ module RepositoryDatatableHelper
can_manage_repository_rows?(repository) can_manage_repository_rows?(repository)
end end
def default_table_order_as_js_array def repository_default_columns(record)
Constants::REPOSITORY_TABLE_DEFAULT_STATE['order'].to_json {
'1': assigned_row(record),
'2': record.code,
'3': escape_input(record.name),
'4': I18n.l(record.created_at, format: :full),
'5': escape_input(record.created_by.full_name),
'6': (record.archived_on ? I18n.l(record.archived_on, format: :full) : ''),
'7': escape_input(record.archived_by&.full_name)
}
end end
def default_table_columns def linked_repository_default_columns(record)
Constants::REPOSITORY_TABLE_DEFAULT_STATE['columns'].to_json {
end '1': assigned_row(record),
'2': escape_input(record.external_id),
def default_snapshot_table_order_as_js_array '3': record.code,
Constants::REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE['order'].to_json '4': escape_input(record.name),
end '5': I18n.l(record.created_at, format: :full),
'6': escape_input(record.created_by.full_name),
def default_snapshot_table_columns '7': (record.archived_on ? I18n.l(record.archived_on, format: :full) : ''),
Constants::REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE['columns'].to_json '8': escape_input(record.archived_by&.full_name)
}
end end
def display_cell_value(cell, team) def display_cell_value(cell, team)

View file

@ -0,0 +1,11 @@
# frozen_string_literal: true
class LinkedRepository < Repository
def default_table_state
state = Constants::REPOSITORY_TABLE_DEFAULT_STATE.deep_dup
state['order'] = [[3, 'asc']]
state['ColReorder'] << state['ColReorder'].length
state['columns'].insert(1, Constants::REPOSITORY_TABLE_STATE_CUSTOM_COLUMN_TEMPLATE)
state
end
end

View file

@ -108,8 +108,8 @@ class Repository < RepositoryBase
end end
end end
def default_columns_count def default_table_state
Constants::REPOSITORY_TABLE_DEFAULT_STATE['columns'].length Constants::REPOSITORY_TABLE_DEFAULT_STATE
end end
def i_shared?(team) def i_shared?(team)

View file

@ -1,6 +1,9 @@
# frozen_string_literal: true # frozen_string_literal: true
class RepositoryBase < ApplicationRecord class RepositoryBase < ApplicationRecord
require 'sti_preload'
include StiPreload
include Discard::Model include Discard::Model
self.table_name = 'repositories' self.table_name = 'repositories'
@ -32,6 +35,22 @@ class RepositoryBase < ApplicationRecord
cell_includes cell_includes
end end
def default_table_state
raise NotImplementedError
end
def default_table_columns
default_table_state['columns'].to_json
end
def default_columns_count
default_table_state['columns'].length
end
def default_table_order_as_js_array
default_table_state['order'].to_json
end
def destroy_discarded(discarded_by_id = nil) def destroy_discarded(discarded_by_id = nil)
self.discarded_by_id = discarded_by_id self.discarded_by_id = discarded_by_id
destroy destroy

View file

@ -55,7 +55,7 @@ class RepositoryColumn < ApplicationRecord
# Calculate old_column_index - this can only be done before # Calculate old_column_index - this can only be done before
# record is deleted when we still have its index # record is deleted when we still have its index
old_column_index = ( old_column_index = (
Constants::REPOSITORY_TABLE_DEFAULT_STATE['columns'].length + repository.default_columns_count +
repository.repository_columns repository.repository_columns
.order(id: :asc) .order(id: :asc)
.pluck(:id) .pluck(:id)

View file

@ -45,8 +45,8 @@ class RepositorySnapshot < RepositoryBase
repository_snapshot.reload repository_snapshot.reload
end end
def default_columns_count def default_table_state
Constants::REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE['columns'].length Constants::REPOSITORY_SNAPSHOT_TABLE_DEFAULT_STATE
end end
def assigned_rows(my_module) def assigned_rows(my_module)

View file

@ -78,11 +78,10 @@ class RepositoryDatatableService
if search_value.present? if search_value.present?
matched_by_user = repository_rows.joins(:created_by).where_attributes_like('users.full_name', search_value) matched_by_user = repository_rows.joins(:created_by).where_attributes_like('users.full_name', search_value)
repository_row_matches = repository_rows repository_row_search_fileds = ['repository_rows.name', RepositoryRow::PREFIXED_ID_SQL]
.where_attributes_like( repository_row_search_fileds << 'repository_rows.external_id' if @repository.is_a?(LinkedRepository)
['repository_rows.name', RepositoryRow::PREFIXED_ID_SQL],
search_value repository_row_matches = repository_rows.where_attributes_like(repository_row_search_fileds, search_value)
)
results = repository_rows.where(id: repository_row_matches) results = repository_rows.where(id: repository_row_matches)
results = results.or(repository_rows.where(id: matched_by_user)) results = results.or(repository_rows.where(id: matched_by_user))
@ -121,8 +120,9 @@ class RepositoryDatatableService
'repository_rows.created_at', 'repository_rows.created_at',
'users.full_name', 'users.full_name',
'repository_rows.archived_on', 'repository_rows.archived_on',
'archived_bies_repository_rows.full_name', 'archived_bies_repository_rows.full_name'
] ]
array.insert(1, 'repository_rows.external_id') if @repository.is_a?(LinkedRepository)
@repository.repository_columns.count.times do @repository.repository_columns.count.times do
array << 'repository_cell.value' array << 'repository_cell.value'
end end

View file

@ -9,7 +9,7 @@ class RepositoryTableStateColumnUpdateService
def update_states_with_new_column(repository) def update_states_with_new_column(repository)
raise ArgumentError, 'repository is empty' if repository.blank? raise ArgumentError, 'repository is empty' if repository.blank?
columns_num = Constants::REPOSITORY_TABLE_DEFAULT_STATE['columns'].length + repository.repository_columns.count columns_num = repository.default_columns_count + repository.repository_columns.count
RepositoryTableState.where( RepositoryTableState.where(
repository: repository repository: repository
).find_each do |table_state| ).find_each do |table_state|
@ -49,7 +49,7 @@ class RepositoryTableStateColumnUpdateService
if state.dig('order', 0, 0).to_i == old_column_index if state.dig('order', 0, 0).to_i == old_column_index
# Fallback to default order if user had table ordered by # Fallback to default order if user had table ordered by
# the deleted column # the deleted column
state['order'] = Constants::REPOSITORY_TABLE_DEFAULT_STATE['order'] state['order'] = repository.default_table_state['order']
elsif state.dig('order', 0, 0).to_i > old_column_index elsif state.dig('order', 0, 0).to_i > old_column_index
state['order'][0][0] -= 1 state['order'][0][0] -= 1
end end

View file

@ -26,7 +26,7 @@ class RepositoryTableStateService
def update_state(state) def update_state(state)
saved_state = load_state saved_state = load_state
state['order'] = Constants::REPOSITORY_TABLE_DEFAULT_STATE['order'] if state.dig('order', 0, 0).to_i < 1 state['order'] = @repository.default_table_state['order'] if state.dig('order', 0, 0).to_i < 1
return if saved_state.state.except('time') == state.except('time') return if saved_state.state.except('time') == state.except('time')
@ -47,10 +47,10 @@ class RepositoryTableStateService
private private
def generate_default_state def generate_default_state
default_columns_num = Constants::REPOSITORY_TABLE_DEFAULT_STATE['columns'].length default_columns_num = @repository.default_columns_count
# This state should be strings-only # This state should be strings-only
state = Constants::REPOSITORY_TABLE_DEFAULT_STATE.deep_dup state = @repository.default_table_state.deep_dup
@repository.repository_columns.each_with_index do |_, index| @repository.repository_columns.each_with_index do |_, index|
real_index = default_columns_num + index real_index = default_columns_num + index
state['columns'][real_index] = Constants::REPOSITORY_TABLE_STATE_CUSTOM_COLUMN_TEMPLATE state['columns'][real_index] = Constants::REPOSITORY_TABLE_STATE_CUSTOM_COLUMN_TEMPLATE

View file

@ -5,8 +5,8 @@
data-repository-name="<%= @repository_snapshot.name %>" data-repository-name="<%= @repository_snapshot.name %>"
data-repository-snapshot-created="<%= l(@repository_snapshot.created_at, format: :full) %>" data-repository-snapshot-created="<%= l(@repository_snapshot.created_at, format: :full) %>"
data-assigned-items-count="<%= @repository_snapshot.repository_rows.count %>" data-assigned-items-count="<%= @repository_snapshot.repository_rows.count %>"
data-default-order="<%= default_snapshot_table_order_as_js_array %>" data-default-order="<%= @repository_snapshot.default_table_order_as_js_array %>"
data-default-table-columns="<%= default_snapshot_table_columns %>" data-default-table-columns="<%= @repository_snapshot.default_table_columns %>"
data-load-state-url="<%= repository_load_table_state_path(@repository_snapshot) %>" data-load-state-url="<%= repository_load_table_state_path(@repository_snapshot) %>"
data-export-url="<%= export_repository_snapshot_my_module_repository_snapshot_path(@my_module, @repository_snapshot) %>" data-export-url="<%= export_repository_snapshot_my_module_repository_snapshot_path(@my_module, @repository_snapshot) %>"
data-versions-sidebar-url="<%= full_view_sidebar_my_module_repository_snapshots_path(@my_module, @repository_snapshot.parent_id) %>" data-versions-sidebar-url="<%= full_view_sidebar_my_module_repository_snapshots_path(@my_module, @repository_snapshot.parent_id) %>"

View file

@ -2,8 +2,8 @@
data-id="<%= @repository.id %>" data-id="<%= @repository.id %>"
data-type="live" data-type="live"
data-source="<%= index_dt_my_module_repository_path(@my_module, @repository) %>" data-source="<%= index_dt_my_module_repository_path(@my_module, @repository) %>"
data-default-order="<%= default_table_order_as_js_array %>" data-default-order="<%= @repository.default_table_order_as_js_array %>"
data-default-table-columns="<%= default_table_columns %>" data-default-table-columns="<%= @repository.default_table_columns %>"
data-repository-name="<%= @repository.name %>" data-repository-name="<%= @repository.name %>"
data-assigned-items-count="<%= @my_module.repository_rows_count(@repository) %>" data-assigned-items-count="<%= @my_module.repository_rows_count(@repository) %>"
data-load-state-url="<%= repository_load_table_state_path(@repository) %>" data-load-state-url="<%= repository_load_table_state_path(@repository) %>"

View file

@ -20,8 +20,8 @@
data-columns-delete-text="<%= I18n.t('repositories.columns_delete') %>" data-columns-delete-text="<%= I18n.t('repositories.columns_delete') %>"
data-available-columns="<%= repository_available_columns_path(repository) %>" data-available-columns="<%= repository_available_columns_path(repository) %>"
data-columns-changed="<%= I18n.t('repositories.columns_changed') %>" data-columns-changed="<%= I18n.t('repositories.columns_changed') %>"
data-default-order="<%= default_table_order_as_js_array %>" data-default-order="<%= @repository.default_table_order_as_js_array %>"
data-default-table-columns="<%= default_table_columns %>" data-default-table-columns="<%= @repository.default_table_columns %>"
data-editable="<%= can_manage_repository_rows?(repository) %>" data-editable="<%= can_manage_repository_rows?(repository) %>"
data-snapshot-provisioning="<%= @snapshot_provisioning %>" data-snapshot-provisioning="<%= @snapshot_provisioning %>"
data-status-url="<%= repository_status_path(@repository) %>"> data-status-url="<%= repository_status_path(@repository) %>">
@ -32,6 +32,9 @@
<span class="sci-checkbox-label"></span> <span class="sci-checkbox-label"></span>
</th> </th>
<th id="assigned" data-unmanageable="true"><%= t("repositories.table.assigned") %></th> <th id="assigned" data-unmanageable="true"><%= t("repositories.table.assigned") %></th>
<% if @repository.is_a?(LinkedRepository) %>
<th id="row-external-id"><%= t('repositories.table.external_id') %></th>
<% end %>
<th id="row-id"><%= t("repositories.table.id") %></th> <th id="row-id"><%= t("repositories.table.id") %></th>
<th id="row-name"><%= t("repositories.table.row_name") %></th> <th id="row-name"><%= t("repositories.table.row_name") %></th>
<th id="added-on" ><%= t("repositories.table.added_on") %></th> <th id="added-on" ><%= t("repositories.table.added_on") %></th>

View file

@ -386,4 +386,6 @@ class Extends
delete_individual_signature_request delete_individual_signature_request
delete_group_signature_request delete_group_signature_request
) )
STI_PRELOAD_CLASSES = %w(LinkedRepository)
end end

View file

@ -1371,6 +1371,7 @@ en:
apply: "Apply" apply: "Apply"
table: table:
id: 'ID' id: 'ID'
external_id: 'External ID'
assigned: "Assigned" assigned: "Assigned"
assigned_search: 'Search...' assigned_search: 'Search...'
assigned_tooltip: "%{tasks} tasks in &#10;%{experiments} experiments,&#10;%{projects} projects" assigned_tooltip: "%{tasks} tasks in &#10;%{experiments} experiments,&#10;%{projects} projects"

View file

@ -0,0 +1,19 @@
# frozen_string_literal: true
require File.expand_path('app/helpers/database_helper')
class AddExternalIdToRepositories < ActiveRecord::Migration[6.1]
include DatabaseHelper
def up
add_column :repository_rows, :external_id, :string, null: true
add_index :repository_rows, :external_id, unique: true, name: 'unique_index_repository_rows_on_external_id'
add_gin_index_without_tags(:repository_rows, :external_id)
end
def down
remove_index :repository_rows, name: 'index_repository_rows_on_external_id'
remove_index :repository_rows, :external_id, unique: true, name: 'unique_index_repository_rows_on_external_id'
remove_column :repository_rows, :external_id, :string, null: true
end
end

27
lib/sti_preload.rb Normal file
View file

@ -0,0 +1,27 @@
# frozen_string_literal: true
module StiPreload
unless Rails.application.config.eager_load
extend ActiveSupport::Concern
included do
cattr_accessor :preloaded, instance_accessor: false
end
class_methods do
def descendants
preload_sti unless preloaded
super
end
def preload_sti
Extends::STI_PRELOAD_CLASSES.each do |type|
logger.debug("Preloading STI type #{type}")
type.constantize
end
self.preloaded = true
end
end
end
end

View file

@ -13,7 +13,7 @@ describe RepositoryRowsController, type: :controller do
RepositoryTableState.create( RepositoryTableState.create(
repository: repository, repository: repository,
user: user, user: user,
state: Constants::REPOSITORY_TABLE_DEFAULT_STATE state: repository.default_teble_state
) )
end end
let!(:repository_row) do let!(:repository_row) do