mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2024-12-29 11:45:18 +08:00
Merge pull request #1279 from okriuchykhin/ok_SCI_2687
Add inventory item CREATE endpoint [SCI-2687]
This commit is contained in:
commit
940b7b8519
19 changed files with 133 additions and 23 deletions
|
@ -5,12 +5,12 @@ module Api
|
||||||
class BaseController < ApiController
|
class BaseController < ApiController
|
||||||
rescue_from ActionController::ParameterMissing do |e|
|
rescue_from ActionController::ParameterMissing do |e|
|
||||||
logger.error e.message
|
logger.error e.message
|
||||||
render json: {}, status: :bad_request
|
render jsonapi: {}, status: :bad_request
|
||||||
end
|
end
|
||||||
|
|
||||||
rescue_from ActiveRecord::RecordNotFound do |e|
|
rescue_from ActiveRecord::RecordNotFound do |e|
|
||||||
logger.error e.message
|
logger.error e.message
|
||||||
render json: {}, status: :not_found
|
render jsonapi: {}, status: :not_found
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,18 +9,18 @@ module Api
|
||||||
def index
|
def index
|
||||||
inventories =
|
inventories =
|
||||||
@team.repositories.page(params[:page]).per(params[:page_size])
|
@team.repositories.page(params[:page]).per(params[:page_size])
|
||||||
render json: inventories, each_serializer: InventorySerializer
|
render jsonapi: inventories, each_serializer: InventorySerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render json: @inventory, serializer: InventorySerializer
|
render jsonapi: @inventory, serializer: InventorySerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def load_team
|
def load_team
|
||||||
@team = Team.find(params.require(:team_id))
|
@team = Team.find(params.require(:team_id))
|
||||||
return render json: {}, status: :forbidden unless can_read_team?(@team)
|
return render jsonapi: {}, status: :forbidden unless can_read_team?(@team)
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_inventory
|
def load_inventory
|
||||||
|
|
|
@ -5,6 +5,7 @@ module Api
|
||||||
class InventoryItemsController < BaseController
|
class InventoryItemsController < BaseController
|
||||||
before_action :load_team
|
before_action :load_team
|
||||||
before_action :load_inventory
|
before_action :load_inventory
|
||||||
|
before_action :check_manage_permissions, only: %i(create)
|
||||||
|
|
||||||
def index
|
def index
|
||||||
items =
|
items =
|
||||||
|
@ -12,23 +13,73 @@ module Api
|
||||||
.includes(repository_cells: :repository_column)
|
.includes(repository_cells: :repository_column)
|
||||||
.includes(
|
.includes(
|
||||||
repository_cells: Extends::REPOSITORY_SEARCH_INCLUDES
|
repository_cells: Extends::REPOSITORY_SEARCH_INCLUDES
|
||||||
).page(params[:page])
|
).page(params.dig(:page, :number))
|
||||||
.per(params[:page_size])
|
.per(params.dig(:page, :size))
|
||||||
render json: items,
|
render jsonapi: items,
|
||||||
each_serializer: InventoryItemSerializer,
|
each_serializer: InventoryItemSerializer,
|
||||||
include: :inventory_cells
|
include: :inventory_cells
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
attributes = inventory_item_params.merge(
|
||||||
|
created_by: current_user,
|
||||||
|
last_modified_by: current_user
|
||||||
|
)
|
||||||
|
if inventory_cells_params.present?
|
||||||
|
inventory_cells_params
|
||||||
|
.each { |p| p.require(:attributes).require(%i(column_id value)) }
|
||||||
|
item = @inventory.repository_rows.new(attributes)
|
||||||
|
item.transaction do
|
||||||
|
item.save!
|
||||||
|
inventory_cells_params.each do |cell_params|
|
||||||
|
cell_attributes = cell_params[:attributes]
|
||||||
|
column =
|
||||||
|
@inventory.repository_columns.find(cell_attributes[:column_id])
|
||||||
|
RepositoryCell.create_with_value(
|
||||||
|
item, column, cell_attributes[:value], current_user
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
item = @inventory.repository_rows.create!(attributes)
|
||||||
|
end
|
||||||
|
render jsonapi: item,
|
||||||
|
serializer: InventoryItemSerializer,
|
||||||
|
include: :inventory_cells,
|
||||||
|
status: :created
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def load_team
|
def load_team
|
||||||
@team = Team.find(params.require(:team_id))
|
@team = Team.find(params.require(:team_id))
|
||||||
return render json: {}, status: :forbidden unless can_read_team?(@team)
|
return render jsonapi: {}, status: :forbidden unless can_read_team?(@team)
|
||||||
end
|
end
|
||||||
|
|
||||||
def load_inventory
|
def load_inventory
|
||||||
@inventory = @team.repositories.find(params.require(:inventory_id))
|
@inventory = @team.repositories.find(params.require(:inventory_id))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def check_manage_permissions
|
||||||
|
unless can_manage_repository_rows?(@team)
|
||||||
|
render body: nil, status: :forbidden
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def inventory_item_params
|
||||||
|
unless params.require(:data).require(:type) == 'inventory_items'
|
||||||
|
raise ActionController::BadRequest,
|
||||||
|
'Wrong object type within parameters'
|
||||||
|
end
|
||||||
|
params.require(:data).require(:attributes)
|
||||||
|
params.permit(data: { attributes: %i(name uid) })[:data]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Partially implement sideposting draft
|
||||||
|
# https://github.com/json-api/json-api/pull/1197
|
||||||
|
def inventory_cells_params
|
||||||
|
params[:included]&.select { |el| el[:type] == 'inventory_cells' }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,18 +5,18 @@ module Api
|
||||||
|
|
||||||
def index
|
def index
|
||||||
teams = current_user.teams.page(params[:page]).per(params[:page_size])
|
teams = current_user.teams.page(params[:page]).per(params[:page_size])
|
||||||
render json: teams, each_serializer: TeamSerializer
|
render jsonapi: teams, each_serializer: TeamSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
render json: @team, serializer: TeamSerializer
|
render jsonapi: @team, serializer: TeamSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def load_team
|
def load_team
|
||||||
@team = Team.find(params.require(:id))
|
@team = Team.find(params.require(:id))
|
||||||
return render json: {}, status: :forbidden unless can_read_team?(@team)
|
return render jsonapi: {}, status: :forbidden unless can_read_team?(@team)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,4 +22,19 @@ class RepositoryAssetValue < ApplicationRecord
|
||||||
def data
|
def data
|
||||||
asset.file_file_name
|
asset.file_file_name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.new_with_payload(payload, attributes)
|
||||||
|
value = new(attributes)
|
||||||
|
team = value.repository_cell.repository_column.repository.team
|
||||||
|
file = Paperclip.io_adapters.for(payload[:file_data])
|
||||||
|
file.original_filename = payload[:file_name]
|
||||||
|
value.asset = Asset.create!(
|
||||||
|
file: file,
|
||||||
|
created_by: value.created_by,
|
||||||
|
last_modified_by: value.created_by,
|
||||||
|
team: team
|
||||||
|
)
|
||||||
|
value.asset.post_process_file(team)
|
||||||
|
value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,12 +31,28 @@ class RepositoryCell < ActiveRecord::Base
|
||||||
end),
|
end),
|
||||||
optional: true, foreign_key: :value_id
|
optional: true, foreign_key: :value_id
|
||||||
|
|
||||||
|
validates_inclusion_of :repository_column,
|
||||||
|
in: (lambda do |cell|
|
||||||
|
cell.repository_row.repository.repository_columns
|
||||||
|
end)
|
||||||
validates :repository_column, presence: true
|
validates :repository_column, presence: true
|
||||||
validate :repository_column_data_type
|
validate :repository_column_data_type
|
||||||
validates :repository_row,
|
validates :repository_row,
|
||||||
uniqueness: { scope: :repository_column },
|
uniqueness: { scope: :repository_column },
|
||||||
unless: :importing
|
unless: :importing
|
||||||
|
|
||||||
|
def self.create_with_value(row, column, data, user)
|
||||||
|
cell = new(repository_row: row, repository_column: column)
|
||||||
|
cell.transaction do
|
||||||
|
value_klass = column.data_type.constantize
|
||||||
|
value = value_klass.new_with_payload(data, repository_cell: cell,
|
||||||
|
created_by: user,
|
||||||
|
last_modified_by: user)
|
||||||
|
cell.value = value
|
||||||
|
value.save!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def repository_column_data_type
|
def repository_column_data_type
|
||||||
|
|
|
@ -17,4 +17,10 @@ class RepositoryDateValue < ApplicationRecord
|
||||||
def formatted
|
def formatted
|
||||||
data
|
data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.new_with_payload(payload, attributes)
|
||||||
|
value = new(attributes)
|
||||||
|
value.data = payload
|
||||||
|
value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,4 +20,13 @@ class RepositoryListValue < ApplicationRecord
|
||||||
return nil unless repository_list_item
|
return nil unless repository_list_item
|
||||||
repository_list_item.data
|
repository_list_item.data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.new_with_payload(payload, attributes)
|
||||||
|
value = new(attributes)
|
||||||
|
value.repository_list_item = value.repository_cell
|
||||||
|
.repository_column
|
||||||
|
.repository_list_items
|
||||||
|
.find(payload)
|
||||||
|
value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,4 +18,10 @@ class RepositoryTextValue < ApplicationRecord
|
||||||
def formatted
|
def formatted
|
||||||
data
|
data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.new_with_payload(payload, attributes)
|
||||||
|
value = new(attributes)
|
||||||
|
value.data = payload
|
||||||
|
value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,16 +4,16 @@ module Api
|
||||||
module V1
|
module V1
|
||||||
class InventoryCellSerializer < ActiveModel::Serializer
|
class InventoryCellSerializer < ActiveModel::Serializer
|
||||||
type :inventory_cells
|
type :inventory_cells
|
||||||
attributes :id, :data_type, :data
|
attributes :id, :value_type, :value
|
||||||
attribute :repository_column_id, key: :column_id
|
attribute :repository_column_id, key: :column_id
|
||||||
|
|
||||||
def data_type
|
def value_type
|
||||||
type_id = RepositoryColumn
|
type_id = RepositoryColumn
|
||||||
.data_types[object.repository_column.data_type]
|
.data_types[object.repository_column.data_type]
|
||||||
I18n.t("api.v1.inventory_data_types.t#{type_id}")
|
I18n.t("api.v1.inventory_data_types.t#{type_id}")
|
||||||
end
|
end
|
||||||
|
|
||||||
def data
|
def value
|
||||||
value =
|
value =
|
||||||
case object.value_type
|
case object.value_type
|
||||||
when 'RepositoryTextValue'
|
when 'RepositoryTextValue'
|
||||||
|
@ -23,7 +23,7 @@ module Api
|
||||||
when 'RepositoryListValue'
|
when 'RepositoryListValue'
|
||||||
object.repository_list_value
|
object.repository_list_value
|
||||||
when 'RepositoryAssetValue'
|
when 'RepositoryAssetValue'
|
||||||
object.repository_list_value
|
object.repository_asset_value
|
||||||
end
|
end
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
ActiveModelSerializers::SerializableResource.new(
|
||||||
value,
|
value,
|
||||||
|
|
|
@ -4,7 +4,7 @@ module Api
|
||||||
module V1
|
module V1
|
||||||
class InventoryItemSerializer < ActiveModel::Serializer
|
class InventoryItemSerializer < ActiveModel::Serializer
|
||||||
type :inventory_items
|
type :inventory_items
|
||||||
attributes :id, :name
|
attributes :name
|
||||||
has_many :repository_cells, key: :inventory_cells,
|
has_many :repository_cells, key: :inventory_cells,
|
||||||
serializer: InventoryCellSerializer,
|
serializer: InventoryCellSerializer,
|
||||||
class_name: 'RepositoryCell'
|
class_name: 'RepositoryCell'
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
module Api
|
module Api
|
||||||
module V1
|
module V1
|
||||||
class RepositoryListValueSerializer < ActiveModel::Serializer
|
class RepositoryListValueSerializer < ActiveModel::Serializer
|
||||||
attribute :formatted, key: :value
|
|
||||||
attribute :repository_list_item_id, key: :inventory_list_item_id
|
attribute :repository_list_item_id, key: :inventory_list_item_id
|
||||||
|
attribute :formatted, key: :inventory_list_item_name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
module Api
|
module Api
|
||||||
module V1
|
module V1
|
||||||
class RepositoryTextValueSerializer < ActiveModel::Serializer
|
class RepositoryTextValueSerializer < ActiveModel::Serializer
|
||||||
attribute :formatted, key: :value
|
attribute :formatted, key: :text
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'active_model_serializers/register_jsonapi_renderer'
|
||||||
|
|
||||||
ActiveModelSerializers.config.adapter = :json_api
|
ActiveModelSerializers.config.adapter = :json_api
|
||||||
ActiveModelSerializers.config.key_transform = :unaltered
|
ActiveModelSerializers.config.key_transform = :unaltered
|
||||||
|
|
|
@ -10,6 +10,7 @@ Api.configure do |config|
|
||||||
end
|
end
|
||||||
|
|
||||||
config.core_api_v1_preview = true if ENV['CORE_API_V1_PREVIEW']
|
config.core_api_v1_preview = true if ENV['CORE_API_V1_PREVIEW']
|
||||||
|
Paperclip::DataUriAdapter.register if ENV['CORE_API_V1_PREVIEW']
|
||||||
|
|
||||||
vars = ENV.select { |name, _| name =~ /^[[:alnum:]]*_AZURE_AD_APP_ID/ }
|
vars = ENV.select { |name, _| name =~ /^[[:alnum:]]*_AZURE_AD_APP_ID/ }
|
||||||
vars.each do |name, value|
|
vars.each do |name, value|
|
||||||
|
|
|
@ -2,4 +2,3 @@
|
||||||
|
|
||||||
# Add new mime types for use in respond_to blocks:
|
# Add new mime types for use in respond_to blocks:
|
||||||
# Mime::Type.register "text/richtext", :rtf
|
# Mime::Type.register "text/richtext", :rtf
|
||||||
Mime::Type.register 'application/vnd.api+json', :json
|
|
||||||
|
|
|
@ -545,11 +545,15 @@ Rails.application.routes.draw do
|
||||||
resources :teams, only: %i(index show) do
|
resources :teams, only: %i(index show) do
|
||||||
resources :inventories, only: %i(index show) do
|
resources :inventories, only: %i(index show) do
|
||||||
get 'columns', to: 'inventory_columns#index'
|
get 'columns', to: 'inventory_columns#index'
|
||||||
get 'items', to: 'inventory_items#index'
|
resources :inventory_items,
|
||||||
|
only: %i(index create),
|
||||||
|
path: 'items',
|
||||||
|
as: :items
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
resources :users, only: %i(show) do
|
resources :users, only: %i(show) do
|
||||||
resources :user_identities, only: %i(index create show update destroy)
|
resources :user_identities,
|
||||||
|
only: %i(index create show update destroy)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,7 @@ FactoryBot.define do
|
||||||
factory :repository_row do
|
factory :repository_row do
|
||||||
name 'Custom row'
|
name 'Custom row'
|
||||||
created_by { User.first || association(:user) }
|
created_by { User.first || association(:user) }
|
||||||
|
repository { Repository.first || create(:repository) }
|
||||||
last_modified_by { User.first || association(:user) }
|
last_modified_by { User.first || association(:user) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -75,7 +75,7 @@ RSpec.describe 'Api::V1::InventoryItemsController', type: :request do
|
||||||
get api_v1_team_inventory_items_path(
|
get api_v1_team_inventory_items_path(
|
||||||
team_id: @teams.first.id,
|
team_id: @teams.first.id,
|
||||||
inventory_id: @teams.first.repositories.first.id
|
inventory_id: @teams.first.repositories.first.id
|
||||||
), params: { page_size: 100 }, headers: @valid_headers
|
), params: { page: { size: 100 } }, headers: @valid_headers
|
||||||
expect { hash_body = json }.not_to raise_exception
|
expect { hash_body = json }.not_to raise_exception
|
||||||
expect(hash_body[:data]).to match(
|
expect(hash_body[:data]).to match(
|
||||||
ActiveModelSerializers::SerializableResource
|
ActiveModelSerializers::SerializableResource
|
||||||
|
|
Loading…
Reference in a new issue