mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-11-10 17:36:33 +08:00
Merge pull request #7694 from rekonder/aj_SCI_10465
Add local storage backend [SCI-10465]
This commit is contained in:
commit
97d2513a04
14 changed files with 377 additions and 4 deletions
|
@ -0,0 +1,89 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class StorageLocationRepositoryRowsController < ApplicationController
|
||||
before_action :load_storage_location_repository_row, only: %i(update destroy)
|
||||
before_action :load_storage_location
|
||||
before_action :load_repository_row
|
||||
before_action :check_read_permissions, only: :index
|
||||
before_action :check_manage_permissions, except: :index
|
||||
|
||||
def index
|
||||
storage_location_repository_row = Lists::StorageLocationRepositoryRowsService.new(
|
||||
current_team, storage_location_repository_row_params
|
||||
).call
|
||||
render json: storage_location_repository_row,
|
||||
each_serializer: Lists::StorageLocationRepositoryRowSerializer,
|
||||
include: %i(repository_row)
|
||||
end
|
||||
|
||||
def update
|
||||
@storage_location_repository_row.update(storage_location_repository_row_params)
|
||||
|
||||
if @storage_location_repository_row.save
|
||||
render json: @storage_location_repository_row,
|
||||
serializer: Lists::StorageLocationRepositoryRowSerializer,
|
||||
include: :repository_row
|
||||
else
|
||||
render json: @storage_location_repository_row.errors, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
@storage_location_repository_row = StorageLocationRepositoryRow.new(
|
||||
repository_row: @repository_row,
|
||||
storage_location: @storage_location,
|
||||
metadata: storage_location_repository_row_params[:metadata] || {},
|
||||
created_by: current_user
|
||||
)
|
||||
|
||||
if @storage_location_repository_row.save
|
||||
render json: @storage_location_repository_row,
|
||||
serializer: Lists::StorageLocationRepositoryRowSerializer,
|
||||
include: :repository_row
|
||||
else
|
||||
render json: @storage_location_repository_row.errors, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
if @storage_location_repository_row.discard
|
||||
render json: {}
|
||||
else
|
||||
render json: { errors: @storage_location_repository_row.errors.full_messages }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_storage_location_repository_row
|
||||
@storage_location_repository_row = StorageLocationRepositoryRow.find(
|
||||
storage_location_repository_row_params[:id]
|
||||
)
|
||||
render_404 unless @storage_location_repository_row
|
||||
end
|
||||
|
||||
def load_storage_location
|
||||
@storage_location = StorageLocation.where(team: current_team).find(
|
||||
storage_location_repository_row_params[:storage_location_id]
|
||||
)
|
||||
render_404 unless @storage_location
|
||||
end
|
||||
|
||||
def load_repository_row
|
||||
@repository_row = RepositoryRow.find(storage_location_repository_row_params[:repository_row_id])
|
||||
render_404 unless @repository_row
|
||||
end
|
||||
|
||||
def storage_location_repository_row_params
|
||||
params.permit(:id, :storage_location_id, :repository_row_id,
|
||||
metadata: { position: [] })
|
||||
end
|
||||
|
||||
def check_read_permissions
|
||||
render_403 unless true
|
||||
end
|
||||
|
||||
def check_manage_permissions
|
||||
render_403 unless true
|
||||
end
|
||||
end
|
65
app/controllers/storage_locations_controller.rb
Normal file
65
app/controllers/storage_locations_controller.rb
Normal file
|
@ -0,0 +1,65 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class StorageLocationsController < ApplicationController
|
||||
before_action :load_storage_location, only: %i(update destroy)
|
||||
before_action :check_read_permissions, only: :index
|
||||
before_action :check_manage_permissions, except: :index
|
||||
|
||||
def index
|
||||
storage_locations = Lists::StorageLocationsService.new(current_team, storage_location_params).call
|
||||
render json: storage_locations, each_serializer: Lists::StorageLocationSerializer
|
||||
end
|
||||
|
||||
def update
|
||||
@storage_location.image.attach(storage_location_params[:signed_blob_id]) if storage_location_params[:signed_blob_id]
|
||||
@storage_location.update(storage_location_params)
|
||||
|
||||
if @storage_location.save
|
||||
render json: @storage_location, serializer: Lists::StorageLocationSerializer
|
||||
else
|
||||
render json: @storage_location.errors, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
@storage_location = StorageLocation.new(
|
||||
storage_location_params.merge({ team: current_team, created_by: current_user })
|
||||
)
|
||||
|
||||
@storage_location.image.attach(storage_location_params[:signed_blob_id]) if storage_location_params[:signed_blob_id]
|
||||
|
||||
if @storage_location.save
|
||||
render json: @storage_location, serializer: Lists::StorageLocationSerializer
|
||||
else
|
||||
render json: @storage_location.errors, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
if @storage_location.discard
|
||||
render json: {}
|
||||
else
|
||||
render json: { errors: @storage_location.errors.full_messages }, status: :unprocessable_entity
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def storage_location_params
|
||||
params.permit(:id, :parent_id, :name, :container, :signed_blob_id, :description,
|
||||
metadata: { dimensions: [], parent_coordinations: [], display_type: :string })
|
||||
end
|
||||
|
||||
def load_storage_location
|
||||
@storage_location = StorageLocation.where(team: current_team).find(storage_location_params[:id])
|
||||
render_404 unless @storage_location
|
||||
end
|
||||
|
||||
def check_read_permissions
|
||||
render_403 unless true
|
||||
end
|
||||
|
||||
def check_manage_permissions
|
||||
render_403 unless true
|
||||
end
|
||||
end
|
|
@ -98,6 +98,8 @@ class RepositoryRow < ApplicationRecord
|
|||
class_name: 'RepositoryRow',
|
||||
source: :parent,
|
||||
dependent: :destroy
|
||||
has_many :storage_location_repository_rows, inverse_of: :repository_row, dependent: :destroy
|
||||
has_many :storage_locations, through: :storage_location_repository_rows
|
||||
|
||||
auto_strip_attributes :name, nullify: false
|
||||
validates :name,
|
||||
|
|
25
app/models/storage_location.rb
Normal file
25
app/models/storage_location.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class StorageLocation < ApplicationRecord
|
||||
include Discard::Model
|
||||
ID_PREFIX = 'SL'
|
||||
include PrefixedIdModel
|
||||
|
||||
default_scope -> { kept }
|
||||
|
||||
has_one_attached :image
|
||||
|
||||
belongs_to :team
|
||||
belongs_to :parent, class_name: 'StorageLocation', optional: true
|
||||
belongs_to :created_by, class_name: 'User'
|
||||
|
||||
has_many :storage_location_repository_rows, inverse_of: :storage_location
|
||||
has_many :repository_rows, through: :storage_location_repository_row
|
||||
|
||||
validates :name, length: { maximum: Constants::NAME_MAX_LENGTH }
|
||||
|
||||
after_discard do
|
||||
StorageLocation.where(parent_id: id).find_each(&:discard)
|
||||
storage_location_repository_rows.each(&:discard)
|
||||
end
|
||||
end
|
30
app/models/storage_location_repository_row.rb
Normal file
30
app/models/storage_location_repository_row.rb
Normal file
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class StorageLocationRepositoryRow < ApplicationRecord
|
||||
include Discard::Model
|
||||
|
||||
default_scope -> { kept }
|
||||
|
||||
belongs_to :storage_location, inverse_of: :storage_location_repository_rows
|
||||
belongs_to :repository_row, inverse_of: :storage_location_repository_rows
|
||||
belongs_to :created_by, class_name: 'User'
|
||||
|
||||
with_options if: -> { storage_location.container && storage_location.metadata['type'] == 'grid' } do
|
||||
validate :position_must_be_present
|
||||
validate :ensure_uniq_position
|
||||
end
|
||||
|
||||
def position_must_be_present
|
||||
if metadata['position'].blank?
|
||||
errors.add(:base, I18n.t('activerecord.errors.models.storage_location.missing_position'))
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_uniq_position
|
||||
if StorageLocationRepositoryRow.where(storage_location: storage_location)
|
||||
.where('metadata @> ?', { position: metadata['position'] }.to_json)
|
||||
.where.not(id: id).exists?
|
||||
errors.add(:base, I18n.t('activerecord.errors.models.storage_location.not_uniq_position'))
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Lists
|
||||
class StorageLocationRepositoryRowSerializer < ActiveModel::Serializer
|
||||
attributes :created_by, :created_on, :position
|
||||
|
||||
belongs_to :repository_row, serializer: RepositoryRowSerializer
|
||||
|
||||
def created_by
|
||||
object.created_by.full_name
|
||||
end
|
||||
|
||||
def created_on
|
||||
I18n.l(object.created_at, format: :full)
|
||||
end
|
||||
|
||||
def position
|
||||
object.metadata['position']
|
||||
end
|
||||
end
|
||||
end
|
19
app/serializers/lists/storage_location_serializer.rb
Normal file
19
app/serializers/lists/storage_location_serializer.rb
Normal file
|
@ -0,0 +1,19 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Lists
|
||||
class StorageLocationSerializer < ActiveModel::Serializer
|
||||
attributes :id, :code, :name, :container, :description, :owned_by, :created_by, :created_on
|
||||
|
||||
def owned_by
|
||||
object.team.name
|
||||
end
|
||||
|
||||
def created_by
|
||||
object.created_by.full_name
|
||||
end
|
||||
|
||||
def created_on
|
||||
I18n.l(object.created_at, format: :full)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -29,7 +29,7 @@ module Lists
|
|||
end
|
||||
|
||||
def paginate_records
|
||||
@records = @records.page(@params[:page]).per(@params[:per_page])
|
||||
@records = @records.page(@params[:page]).per(@params[:per_page]) if @params[:page].present?
|
||||
end
|
||||
|
||||
def sort_direction(order_params)
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Lists
|
||||
class StorageLocationRepositoryRowsService < BaseService
|
||||
def initialize(team, params)
|
||||
@team = team
|
||||
@storage_location_id = params[:storage_location_id]
|
||||
@params = params
|
||||
end
|
||||
|
||||
def fetch_records
|
||||
@records = StorageLocationRepositoryRow.includes(:repository_row).where(storage_location_id: @storage_location_id)
|
||||
end
|
||||
|
||||
def filter_records; end
|
||||
end
|
||||
end
|
17
app/services/lists/storage_locations_service.rb
Normal file
17
app/services/lists/storage_locations_service.rb
Normal file
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Lists
|
||||
class StorageLocationsService < BaseService
|
||||
def initialize(team, params)
|
||||
@team = team
|
||||
@parent_id = params[:parent_id]
|
||||
@params = params
|
||||
end
|
||||
|
||||
def fetch_records
|
||||
@records = StorageLocation.where(team: @team, parent_id: @parent_id)
|
||||
end
|
||||
|
||||
def filter_records; end
|
||||
end
|
||||
end
|
|
@ -259,6 +259,9 @@ en:
|
|||
attributes:
|
||||
text: Text is too long
|
||||
position: "Position has already been taken by another item in the checklist"
|
||||
storage_location:
|
||||
missing_position: 'Missing position metadata'
|
||||
not_uniq_position: 'Position already taken'
|
||||
storage:
|
||||
limit_reached: "Storage limit has been reached."
|
||||
helpers:
|
||||
|
|
|
@ -807,6 +807,10 @@ Rails.application.routes.draw do
|
|||
|
||||
resources :connected_devices, controller: 'users/connected_devices', only: %i(destroy)
|
||||
|
||||
resources :storage_locations, only: %i(index create destroy update) do
|
||||
resources :storage_location_repository_rows, only: %i(index create destroy update)
|
||||
end
|
||||
|
||||
get 'search' => 'search#index'
|
||||
get 'search/new' => 'search#new', as: :new_search
|
||||
resource :search, only: [], controller: :search do
|
||||
|
|
38
db/migrate/20240705122903_add_storage_locations.rb
Normal file
38
db/migrate/20240705122903_add_storage_locations.rb
Normal file
|
@ -0,0 +1,38 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddStorageLocations < ActiveRecord::Migration[7.0]
|
||||
include DatabaseHelper
|
||||
|
||||
def up
|
||||
create_table :storage_locations do |t|
|
||||
t.string :name
|
||||
t.string :description
|
||||
t.references :parent, index: true, foreign_key: { to_table: :storage_locations }
|
||||
t.references :team, index: true, foreign_key: { to_table: :teams }
|
||||
t.references :created_by, foreign_key: { to_table: :users }
|
||||
t.boolean :container, default: false, null: false, index: true
|
||||
t.jsonb :metadata, null: false, default: {}
|
||||
t.datetime :discarded_at, index: true
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
create_table :storage_location_repository_rows do |t|
|
||||
t.references :repository_row, index: true, foreign_key: { to_table: :repository_rows }
|
||||
t.references :storage_location, index: true, foreign_key: { to_table: :storage_locations }
|
||||
t.references :created_by, foreign_key: { to_table: :users }
|
||||
t.jsonb :metadata, null: false, default: {}
|
||||
t.datetime :discarded_at, index: true
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
|
||||
add_gin_index_without_tags :storage_locations, :name
|
||||
add_gin_index_without_tags :storage_locations, :description
|
||||
end
|
||||
|
||||
def down
|
||||
drop_table :storage_location_repository_rows
|
||||
drop_table :storage_locations
|
||||
end
|
||||
end
|
49
db/schema.rb
49
db/schema.rb
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.0].define(version: 2024_06_26_113515) do
|
||||
ActiveRecord::Schema[7.0].define(version: 2024_07_05_122903) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "btree_gist"
|
||||
enable_extension "pg_trgm"
|
||||
|
@ -721,8 +721,8 @@ ActiveRecord::Schema[7.0].define(version: 2024_06_26_113515) do
|
|||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "type"
|
||||
t.datetime "start_time_dup"
|
||||
t.datetime "end_time_dup"
|
||||
t.datetime "start_time_dup", precision: nil
|
||||
t.datetime "end_time_dup", precision: nil
|
||||
t.index "((end_time)::date)", name: "index_repository_date_time_range_values_on_end_time_as_date", where: "((type)::text = 'RepositoryDateRangeValue'::text)"
|
||||
t.index "((end_time)::time without time zone)", name: "index_repository_date_time_range_values_on_end_time_as_time", where: "((type)::text = 'RepositoryTimeRangeValue'::text)"
|
||||
t.index "((start_time)::date)", name: "index_repository_date_time_range_values_on_start_time_as_date", where: "((type)::text = 'RepositoryDateRangeValue'::text)"
|
||||
|
@ -1083,6 +1083,40 @@ ActiveRecord::Schema[7.0].define(version: 2024_06_26_113515) do
|
|||
t.index ["user_id"], name: "index_steps_on_user_id"
|
||||
end
|
||||
|
||||
create_table "storage_location_repository_rows", force: :cascade do |t|
|
||||
t.bigint "repository_row_id"
|
||||
t.bigint "storage_location_id"
|
||||
t.bigint "created_by_id"
|
||||
t.jsonb "metadata", default: {}, null: false
|
||||
t.datetime "discarded_at"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index ["created_by_id"], name: "index_storage_location_repository_rows_on_created_by_id"
|
||||
t.index ["discarded_at"], name: "index_storage_location_repository_rows_on_discarded_at"
|
||||
t.index ["repository_row_id"], name: "index_storage_location_repository_rows_on_repository_row_id"
|
||||
t.index ["storage_location_id"], name: "index_storage_location_repository_rows_on_storage_location_id"
|
||||
end
|
||||
|
||||
create_table "storage_locations", force: :cascade do |t|
|
||||
t.string "name"
|
||||
t.string "description"
|
||||
t.bigint "parent_id"
|
||||
t.bigint "team_id"
|
||||
t.bigint "created_by_id"
|
||||
t.boolean "container", default: false, null: false
|
||||
t.jsonb "metadata", default: {}, null: false
|
||||
t.datetime "discarded_at"
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.index "trim_html_tags((description)::text) gin_trgm_ops", name: "index_storage_locations_on_description", using: :gin
|
||||
t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_storage_locations_on_name", using: :gin
|
||||
t.index ["container"], name: "index_storage_locations_on_container"
|
||||
t.index ["created_by_id"], name: "index_storage_locations_on_created_by_id"
|
||||
t.index ["discarded_at"], name: "index_storage_locations_on_discarded_at"
|
||||
t.index ["parent_id"], name: "index_storage_locations_on_parent_id"
|
||||
t.index ["team_id"], name: "index_storage_locations_on_team_id"
|
||||
end
|
||||
|
||||
create_table "tables", force: :cascade do |t|
|
||||
t.binary "contents", null: false
|
||||
t.datetime "created_at", precision: nil, null: false
|
||||
|
@ -1278,6 +1312,9 @@ ActiveRecord::Schema[7.0].define(version: 2024_06_26_113515) do
|
|||
t.integer "failed_attempts", default: 0, null: false
|
||||
t.datetime "locked_at", precision: nil
|
||||
t.string "unlock_token"
|
||||
t.string "api_key"
|
||||
t.datetime "api_key_expires_at", precision: nil
|
||||
t.datetime "api_key_created_at", precision: nil
|
||||
t.index "trim_html_tags((full_name)::text) gin_trgm_ops", name: "index_users_on_full_name", using: :gin
|
||||
t.index ["authentication_token"], name: "index_users_on_authentication_token", unique: true
|
||||
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
|
||||
|
@ -1502,6 +1539,12 @@ ActiveRecord::Schema[7.0].define(version: 2024_06_26_113515) do
|
|||
add_foreign_key "steps", "protocols"
|
||||
add_foreign_key "steps", "users"
|
||||
add_foreign_key "steps", "users", column: "last_modified_by_id"
|
||||
add_foreign_key "storage_location_repository_rows", "repository_rows"
|
||||
add_foreign_key "storage_location_repository_rows", "storage_locations"
|
||||
add_foreign_key "storage_location_repository_rows", "users", column: "created_by_id"
|
||||
add_foreign_key "storage_locations", "storage_locations", column: "parent_id"
|
||||
add_foreign_key "storage_locations", "teams"
|
||||
add_foreign_key "storage_locations", "users", column: "created_by_id"
|
||||
add_foreign_key "tables", "users", column: "created_by_id"
|
||||
add_foreign_key "tables", "users", column: "last_modified_by_id"
|
||||
add_foreign_key "tags", "projects"
|
||||
|
|
Loading…
Reference in a new issue