add batch endpoints for collection items
POST /collection/{characters,weapons,summons}/batch
This commit is contained in:
parent
4a471dd273
commit
99292f20ef
4 changed files with 210 additions and 15 deletions
|
|
@ -7,7 +7,7 @@ module Api
|
||||||
before_action :set_collection_character_for_read, only: %i[show]
|
before_action :set_collection_character_for_read, only: %i[show]
|
||||||
|
|
||||||
# Write actions: require auth, use current_user
|
# Write actions: require auth, use current_user
|
||||||
before_action :restrict_access, only: %i[create update destroy]
|
before_action :restrict_access, only: %i[create update destroy batch]
|
||||||
before_action :set_collection_character_for_write, only: %i[update destroy]
|
before_action :set_collection_character_for_write, only: %i[update destroy]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
|
|
@ -74,6 +74,45 @@ module Api
|
||||||
head :no_content
|
head :no_content
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# POST /collection/characters/batch
|
||||||
|
# Creates multiple collection characters in a single request
|
||||||
|
def batch
|
||||||
|
items = batch_character_params[:collection_characters] || []
|
||||||
|
created = []
|
||||||
|
skipped = []
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
items.each_with_index do |item_params, index|
|
||||||
|
# Check if already exists (skip duplicates)
|
||||||
|
if current_user.collection_characters.exists?(character_id: item_params[:character_id])
|
||||||
|
skipped << { index: index, character_id: item_params[:character_id], reason: 'already_exists' }
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
collection_character = current_user.collection_characters.build(item_params)
|
||||||
|
|
||||||
|
if collection_character.save
|
||||||
|
created << collection_character
|
||||||
|
else
|
||||||
|
errors << {
|
||||||
|
index: index,
|
||||||
|
character_id: item_params[:character_id],
|
||||||
|
error: collection_character.errors.full_messages.join(', ')
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
status = errors.any? ? :multi_status : :created
|
||||||
|
|
||||||
|
render json: Api::V1::CollectionCharacterBlueprint.render(
|
||||||
|
created,
|
||||||
|
root: :characters,
|
||||||
|
meta: { created: created.size, skipped: skipped.size, skipped_items: skipped, errors: errors }
|
||||||
|
), status: status
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_target_user
|
def set_target_user
|
||||||
|
|
@ -112,6 +151,18 @@ module Api
|
||||||
earring: %i[modifier strength]
|
earring: %i[modifier strength]
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def batch_character_params
|
||||||
|
params.permit(collection_characters: [
|
||||||
|
:character_id, :uncap_level, :transcendence_step, :perpetuity,
|
||||||
|
:awakening_id, :awakening_level,
|
||||||
|
ring1: %i[modifier strength],
|
||||||
|
ring2: %i[modifier strength],
|
||||||
|
ring3: %i[modifier strength],
|
||||||
|
ring4: %i[modifier strength],
|
||||||
|
earring: %i[modifier strength]
|
||||||
|
])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,17 @@
|
||||||
module Api
|
module Api
|
||||||
module V1
|
module V1
|
||||||
class CollectionSummonsController < ApiController
|
class CollectionSummonsController < ApiController
|
||||||
before_action :restrict_access
|
# Read actions: look up user from params, check privacy
|
||||||
before_action :set_collection_summon, only: %i[show update destroy]
|
before_action :set_target_user, only: %i[index show]
|
||||||
|
before_action :check_collection_access, only: %i[index show]
|
||||||
|
before_action :set_collection_summon_for_read, only: %i[show]
|
||||||
|
|
||||||
|
# Write actions: require auth, use current_user
|
||||||
|
before_action :restrict_access, only: %i[create update destroy batch]
|
||||||
|
before_action :set_collection_summon_for_write, only: %i[update destroy]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@collection_summons = current_user.collection_summons
|
@collection_summons = @target_user.collection_summons
|
||||||
.includes(:summon)
|
.includes(:summon)
|
||||||
|
|
||||||
@collection_summons = @collection_summons.by_summon(params[:summon_id]) if params[:summon_id]
|
@collection_summons = @collection_summons.by_summon(params[:summon_id]) if params[:summon_id]
|
||||||
|
|
@ -16,7 +22,7 @@ module Api
|
||||||
|
|
||||||
render json: Api::V1::CollectionSummonBlueprint.render(
|
render json: Api::V1::CollectionSummonBlueprint.render(
|
||||||
@collection_summons,
|
@collection_summons,
|
||||||
root: :collection_summons,
|
root: :summons,
|
||||||
meta: pagination_meta(@collection_summons)
|
meta: pagination_meta(@collection_summons)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
@ -57,9 +63,61 @@ module Api
|
||||||
head :no_content
|
head :no_content
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# POST /collection/summons/batch
|
||||||
|
# Creates multiple collection summons in a single request
|
||||||
|
# Unlike characters, summons can have duplicates (user can own multiple copies)
|
||||||
|
def batch
|
||||||
|
items = batch_summon_params[:collection_summons] || []
|
||||||
|
created = []
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
items.each_with_index do |item_params, index|
|
||||||
|
collection_summon = current_user.collection_summons.build(item_params)
|
||||||
|
|
||||||
|
if collection_summon.save
|
||||||
|
created << collection_summon
|
||||||
|
else
|
||||||
|
errors << {
|
||||||
|
index: index,
|
||||||
|
summon_id: item_params[:summon_id],
|
||||||
|
error: collection_summon.errors.full_messages.join(', ')
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
status = errors.any? ? :multi_status : :created
|
||||||
|
|
||||||
|
render json: Api::V1::CollectionSummonBlueprint.render(
|
||||||
|
created,
|
||||||
|
root: :summons,
|
||||||
|
meta: { created: created.size, errors: errors }
|
||||||
|
), status: status
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_collection_summon
|
def set_target_user
|
||||||
|
@target_user = User.find(params[:user_id])
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
render json: { error: "User not found" }, status: :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_collection_access
|
||||||
|
return if @target_user.nil? # Already handled by set_target_user
|
||||||
|
unless @target_user.collection_viewable_by?(current_user)
|
||||||
|
render json: { error: "You do not have permission to view this collection" }, status: :forbidden
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_collection_summon_for_read
|
||||||
|
@collection_summon = @target_user.collection_summons.find(params[:id])
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
raise CollectionErrors::CollectionItemNotFound.new('summon', params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_collection_summon_for_write
|
||||||
@collection_summon = current_user.collection_summons.find(params[:id])
|
@collection_summon = current_user.collection_summons.find(params[:id])
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
raise CollectionErrors::CollectionItemNotFound.new('summon', params[:id])
|
raise CollectionErrors::CollectionItemNotFound.new('summon', params[:id])
|
||||||
|
|
@ -70,6 +128,12 @@ module Api
|
||||||
:summon_id, :uncap_level, :transcendence_step
|
:summon_id, :uncap_level, :transcendence_step
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def batch_summon_params
|
||||||
|
params.permit(collection_summons: [
|
||||||
|
:summon_id, :uncap_level, :transcendence_step
|
||||||
|
])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,17 @@
|
||||||
module Api
|
module Api
|
||||||
module V1
|
module V1
|
||||||
class CollectionWeaponsController < ApiController
|
class CollectionWeaponsController < ApiController
|
||||||
before_action :restrict_access
|
# Read actions: look up user from params, check privacy
|
||||||
before_action :set_collection_weapon, only: [:show, :update, :destroy]
|
before_action :set_target_user, only: %i[index show]
|
||||||
|
before_action :check_collection_access, only: %i[index show]
|
||||||
|
before_action :set_collection_weapon_for_read, only: %i[show]
|
||||||
|
|
||||||
|
# Write actions: require auth, use current_user
|
||||||
|
before_action :restrict_access, only: %i[create update destroy batch]
|
||||||
|
before_action :set_collection_weapon_for_write, only: %i[update destroy]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@collection_weapons = current_user.collection_weapons
|
@collection_weapons = @target_user.collection_weapons
|
||||||
.includes(:weapon, :awakening,
|
.includes(:weapon, :awakening,
|
||||||
:weapon_key1, :weapon_key2,
|
:weapon_key1, :weapon_key2,
|
||||||
:weapon_key3, :weapon_key4)
|
:weapon_key3, :weapon_key4)
|
||||||
|
|
@ -18,7 +24,7 @@ module Api
|
||||||
|
|
||||||
render json: Api::V1::CollectionWeaponBlueprint.render(
|
render json: Api::V1::CollectionWeaponBlueprint.render(
|
||||||
@collection_weapons,
|
@collection_weapons,
|
||||||
root: :collection_weapons,
|
root: :weapons,
|
||||||
meta: pagination_meta(@collection_weapons)
|
meta: pagination_meta(@collection_weapons)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
@ -59,9 +65,61 @@ module Api
|
||||||
head :no_content
|
head :no_content
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# POST /collection/weapons/batch
|
||||||
|
# Creates multiple collection weapons in a single request
|
||||||
|
# Unlike characters, weapons can have duplicates (user can own multiple copies)
|
||||||
|
def batch
|
||||||
|
items = batch_weapon_params[:collection_weapons] || []
|
||||||
|
created = []
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
items.each_with_index do |item_params, index|
|
||||||
|
collection_weapon = current_user.collection_weapons.build(item_params)
|
||||||
|
|
||||||
|
if collection_weapon.save
|
||||||
|
created << collection_weapon
|
||||||
|
else
|
||||||
|
errors << {
|
||||||
|
index: index,
|
||||||
|
weapon_id: item_params[:weapon_id],
|
||||||
|
error: collection_weapon.errors.full_messages.join(', ')
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
status = errors.any? ? :multi_status : :created
|
||||||
|
|
||||||
|
render json: Api::V1::CollectionWeaponBlueprint.render(
|
||||||
|
created,
|
||||||
|
root: :weapons,
|
||||||
|
meta: { created: created.size, errors: errors }
|
||||||
|
), status: status
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_collection_weapon
|
def set_target_user
|
||||||
|
@target_user = User.find(params[:user_id])
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
render json: { error: "User not found" }, status: :not_found
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_collection_access
|
||||||
|
return if @target_user.nil? # Already handled by set_target_user
|
||||||
|
unless @target_user.collection_viewable_by?(current_user)
|
||||||
|
render json: { error: "You do not have permission to view this collection" }, status: :forbidden
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_collection_weapon_for_read
|
||||||
|
@collection_weapon = @target_user.collection_weapons.find(params[:id])
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
raise CollectionErrors::CollectionItemNotFound.new('weapon', params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_collection_weapon_for_write
|
||||||
@collection_weapon = current_user.collection_weapons.find(params[:id])
|
@collection_weapon = current_user.collection_weapons.find(params[:id])
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
raise CollectionErrors::CollectionItemNotFound.new('weapon', params[:id])
|
raise CollectionErrors::CollectionItemNotFound.new('weapon', params[:id])
|
||||||
|
|
@ -76,6 +134,16 @@ module Api
|
||||||
:element
|
:element
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def batch_weapon_params
|
||||||
|
params.permit(collection_weapons: [
|
||||||
|
:weapon_id, :uncap_level, :transcendence_step,
|
||||||
|
:weapon_key1_id, :weapon_key2_id, :weapon_key3_id, :weapon_key4_id,
|
||||||
|
:awakening_id, :awakening_level,
|
||||||
|
:ax_modifier1, :ax_strength1, :ax_modifier2, :ax_strength2,
|
||||||
|
:element
|
||||||
|
])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -137,9 +137,21 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
# Writing to collections - requires auth, operates on current_user
|
# Writing to collections - requires auth, operates on current_user
|
||||||
namespace :collection do
|
namespace :collection do
|
||||||
resources :characters, only: [:create, :update, :destroy], controller: '/api/v1/collection_characters'
|
resources :characters, only: [:create, :update, :destroy], controller: '/api/v1/collection_characters' do
|
||||||
resources :weapons, only: [:create, :update, :destroy], controller: '/api/v1/collection_weapons'
|
collection do
|
||||||
resources :summons, only: [:create, :update, :destroy], controller: '/api/v1/collection_summons'
|
post :batch
|
||||||
|
end
|
||||||
|
end
|
||||||
|
resources :weapons, only: [:create, :update, :destroy], controller: '/api/v1/collection_weapons' do
|
||||||
|
collection do
|
||||||
|
post :batch
|
||||||
|
end
|
||||||
|
end
|
||||||
|
resources :summons, only: [:create, :update, :destroy], controller: '/api/v1/collection_summons' do
|
||||||
|
collection do
|
||||||
|
post :batch
|
||||||
|
end
|
||||||
|
end
|
||||||
resources :job_accessories, controller: '/api/v1/collection_job_accessories',
|
resources :job_accessories, controller: '/api/v1/collection_job_accessories',
|
||||||
only: [:index, :show, :create, :destroy]
|
only: [:index, :show, :create, :destroy]
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue