unify collection api: single endpoint for all users
- restructure routes: read via /users/:id/collection/*, write via /collection/* - add user lookup + privacy check to collection_characters_controller - add race, proficiency, gender scopes to model - delete old collection_controller
This commit is contained in:
parent
301f323ee1
commit
5bc179afa8
4 changed files with 51 additions and 89 deletions
|
|
@ -1,16 +1,25 @@
|
||||||
module Api
|
module Api
|
||||||
module V1
|
module V1
|
||||||
class CollectionCharactersController < ApiController
|
class CollectionCharactersController < ApiController
|
||||||
before_action :restrict_access
|
# Read actions: look up user from params, check privacy
|
||||||
before_action :set_collection_character, 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_character_for_read, only: %i[show]
|
||||||
|
|
||||||
|
# Write actions: require auth, use current_user
|
||||||
|
before_action :restrict_access, only: %i[create update destroy]
|
||||||
|
before_action :set_collection_character_for_write, only: %i[update destroy]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@collection_characters = current_user.collection_characters
|
@collection_characters = @target_user.collection_characters
|
||||||
.includes(:character, :awakening)
|
.includes(:character, :awakening)
|
||||||
|
|
||||||
# Apply filters
|
# Apply filters
|
||||||
@collection_characters = @collection_characters.by_element(params[:element]) if params[:element]
|
@collection_characters = @collection_characters.by_element(params[:element]) if params[:element]
|
||||||
@collection_characters = @collection_characters.by_rarity(params[:rarity]) if params[:rarity]
|
@collection_characters = @collection_characters.by_rarity(params[:rarity]) if params[:rarity]
|
||||||
|
@collection_characters = @collection_characters.by_race(params[:race]) if params[:race]
|
||||||
|
@collection_characters = @collection_characters.by_proficiency(params[:proficiency]) if params[:proficiency]
|
||||||
|
@collection_characters = @collection_characters.by_gender(params[:gender]) if params[:gender]
|
||||||
|
|
||||||
# Apply pagination
|
# Apply pagination
|
||||||
@collection_characters = @collection_characters.paginate(page: params[:page], per_page: params[:limit] || 50)
|
@collection_characters = @collection_characters.paginate(page: params[:page], per_page: params[:limit] || 50)
|
||||||
|
|
@ -64,7 +73,26 @@ module Api
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def set_collection_character
|
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_character_for_read
|
||||||
|
@collection_character = @target_user.collection_characters.find(params[:id])
|
||||||
|
rescue ActiveRecord::RecordNotFound
|
||||||
|
raise CollectionErrors::CollectionItemNotFound.new('character', params[:id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_collection_character_for_write
|
||||||
@collection_character = current_user.collection_characters.find(params[:id])
|
@collection_character = current_user.collection_characters.find(params[:id])
|
||||||
rescue ActiveRecord::RecordNotFound
|
rescue ActiveRecord::RecordNotFound
|
||||||
raise CollectionErrors::CollectionItemNotFound.new('character', params[:id])
|
raise CollectionErrors::CollectionItemNotFound.new('character', params[:id])
|
||||||
|
|
|
||||||
|
|
@ -1,79 +0,0 @@
|
||||||
module Api
|
|
||||||
module V1
|
|
||||||
class CollectionController < ApiController
|
|
||||||
before_action :set_user
|
|
||||||
before_action :check_collection_access
|
|
||||||
|
|
||||||
# GET /api/v1/users/:user_id/collection
|
|
||||||
# GET /api/v1/users/:user_id/collection?type=weapons
|
|
||||||
def show
|
|
||||||
collection = case params[:type]
|
|
||||||
when 'characters'
|
|
||||||
{
|
|
||||||
characters: Api::V1::CollectionCharacterBlueprint.render_as_hash(
|
|
||||||
@user.collection_characters.includes(:character, :awakening),
|
|
||||||
view: :full
|
|
||||||
)
|
|
||||||
}
|
|
||||||
when 'weapons'
|
|
||||||
{
|
|
||||||
weapons: Api::V1::CollectionWeaponBlueprint.render_as_hash(
|
|
||||||
@user.collection_weapons.includes(:weapon, :awakening, :weapon_key1,
|
|
||||||
:weapon_key2, :weapon_key3, :weapon_key4),
|
|
||||||
view: :full
|
|
||||||
)
|
|
||||||
}
|
|
||||||
when 'summons'
|
|
||||||
{
|
|
||||||
summons: Api::V1::CollectionSummonBlueprint.render_as_hash(
|
|
||||||
@user.collection_summons.includes(:summon),
|
|
||||||
view: :full
|
|
||||||
)
|
|
||||||
}
|
|
||||||
when 'job_accessories'
|
|
||||||
{
|
|
||||||
job_accessories: Api::V1::CollectionJobAccessoryBlueprint.render_as_hash(
|
|
||||||
@user.collection_job_accessories.includes(job_accessory: :job)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
# Return complete collection
|
|
||||||
{
|
|
||||||
characters: Api::V1::CollectionCharacterBlueprint.render_as_hash(
|
|
||||||
@user.collection_characters.includes(:character, :awakening),
|
|
||||||
view: :full
|
|
||||||
),
|
|
||||||
weapons: Api::V1::CollectionWeaponBlueprint.render_as_hash(
|
|
||||||
@user.collection_weapons.includes(:weapon, :awakening, :weapon_key1,
|
|
||||||
:weapon_key2, :weapon_key3, :weapon_key4),
|
|
||||||
view: :full
|
|
||||||
),
|
|
||||||
summons: Api::V1::CollectionSummonBlueprint.render_as_hash(
|
|
||||||
@user.collection_summons.includes(:summon),
|
|
||||||
view: :full
|
|
||||||
),
|
|
||||||
job_accessories: Api::V1::CollectionJobAccessoryBlueprint.render_as_hash(
|
|
||||||
@user.collection_job_accessories.includes(job_accessory: :job)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
render json: collection
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_user
|
|
||||||
@user = User.find(params[:user_id])
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
render json: { error: "User not found" }, status: :not_found
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_collection_access
|
|
||||||
unless @user.collection_viewable_by?(current_user)
|
|
||||||
render json: { error: "You do not have permission to view this collection" }, status: :forbidden
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -16,6 +16,13 @@ class CollectionCharacter < ApplicationRecord
|
||||||
|
|
||||||
scope :by_element, ->(element) { joins(:character).where(characters: { element: element }) }
|
scope :by_element, ->(element) { joins(:character).where(characters: { element: element }) }
|
||||||
scope :by_rarity, ->(rarity) { joins(:character).where(characters: { rarity: rarity }) }
|
scope :by_rarity, ->(rarity) { joins(:character).where(characters: { rarity: rarity }) }
|
||||||
|
scope :by_race, ->(races) {
|
||||||
|
joins(:character).where('characters.race1 IN (?) OR characters.race2 IN (?)', races, races)
|
||||||
|
}
|
||||||
|
scope :by_proficiency, ->(proficiencies) {
|
||||||
|
joins(:character).where('characters.proficiency1 IN (?) OR characters.proficiency2 IN (?)', proficiencies, proficiencies)
|
||||||
|
}
|
||||||
|
scope :by_gender, ->(genders) { joins(:character).where(characters: { gender: genders }) }
|
||||||
scope :transcended, -> { where('transcendence_step > 0') }
|
scope :transcended, -> { where('transcendence_step > 0') }
|
||||||
scope :with_awakening, -> { where.not(awakening_id: nil) }
|
scope :with_awakening, -> { where.not(awakening_id: nil) }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -126,14 +126,20 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
delete 'favorites', to: 'favorites#destroy'
|
delete 'favorites', to: 'favorites#destroy'
|
||||||
|
|
||||||
# User collection viewing (respects privacy settings)
|
# Reading collections - works for any user with privacy check
|
||||||
get 'users/:user_id/collection', to: 'collection#show'
|
scope 'users/:user_id' do
|
||||||
|
namespace :collection do
|
||||||
|
resources :characters, only: [:index, :show], controller: '/api/v1/collection_characters'
|
||||||
|
resources :weapons, only: [:index, :show], controller: '/api/v1/collection_weapons'
|
||||||
|
resources :summons, only: [:index, :show], controller: '/api/v1/collection_summons'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Collection management for current user
|
# Writing to collections - requires auth, operates on current_user
|
||||||
namespace :collection do
|
namespace :collection do
|
||||||
resources :characters, controller: '/api/v1/collection_characters'
|
resources :characters, only: [:create, :update, :destroy], controller: '/api/v1/collection_characters'
|
||||||
resources :weapons, controller: '/api/v1/collection_weapons'
|
resources :weapons, only: [:create, :update, :destroy], controller: '/api/v1/collection_weapons'
|
||||||
resources :summons, controller: '/api/v1/collection_summons'
|
resources :summons, only: [:create, :update, :destroy], controller: '/api/v1/collection_summons'
|
||||||
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