add artifact controllers and routes

This commit is contained in:
Justin Edmund 2025-12-03 12:58:44 -08:00
parent 069118cbe9
commit cc7ac1956b
5 changed files with 343 additions and 0 deletions

View file

@ -0,0 +1,29 @@
# frozen_string_literal: true
module Api
module V1
class ArtifactSkillsController < Api::V1::ApiController
# GET /artifact_skills
def index
@skills = ArtifactSkill.all
@skills = @skills.where(skill_group: params[:group]) if params[:group].present?
@skills = @skills.where(polarity: params[:polarity]) if params[:polarity].present?
render json: ArtifactSkillBlueprint.render(@skills, root: :artifact_skills)
end
# GET /artifact_skills/for_slot/:slot
# Returns skills valid for a specific slot (1-4)
def for_slot
slot = params[:slot].to_i
unless (1..4).cover?(slot)
return render json: { error: 'Slot must be between 1 and 4' }, status: :unprocessable_entity
end
@skills = ArtifactSkill.for_slot(slot)
render json: ArtifactSkillBlueprint.render(@skills, root: :artifact_skills)
end
end
end
end

View file

@ -0,0 +1,31 @@
# frozen_string_literal: true
module Api
module V1
class ArtifactsController < Api::V1::ApiController
before_action :set_artifact, only: [:show]
# GET /artifacts
def index
@artifacts = Artifact.all
@artifacts = @artifacts.where(rarity: params[:rarity]) if params[:rarity].present?
@artifacts = @artifacts.where(proficiency: params[:proficiency]) if params[:proficiency].present?
render json: ArtifactBlueprint.render(@artifacts, root: :artifacts)
end
# GET /artifacts/:id
def show
render json: ArtifactBlueprint.render(@artifact)
end
private
def set_artifact
@artifact = Artifact.find(params[:id])
rescue ActiveRecord::RecordNotFound
render_not_found_response('artifact')
end
end
end
end

View file

@ -0,0 +1,147 @@
# frozen_string_literal: true
module Api
module V1
class CollectionArtifactsController < ApiController
# Read actions: look up user from params, check privacy
before_action :set_target_user, only: %i[index show]
before_action :check_collection_access, only: %i[index show]
before_action :set_collection_artifact_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_artifact_for_write, only: %i[update destroy]
def index
@collection_artifacts = @target_user.collection_artifacts.includes(:artifact)
@collection_artifacts = @collection_artifacts.where(artifact_id: params[:artifact_id]) if params[:artifact_id]
@collection_artifacts = @collection_artifacts.where(element: params[:element]) if params[:element]
@collection_artifacts = @collection_artifacts.paginate(page: params[:page], per_page: params[:limit] || 50)
render json: Api::V1::CollectionArtifactBlueprint.render(
@collection_artifacts,
root: :artifacts,
meta: pagination_meta(@collection_artifacts)
)
end
def show
render json: Api::V1::CollectionArtifactBlueprint.render(
@collection_artifact,
view: :full
)
end
def create
@collection_artifact = current_user.collection_artifacts.build(collection_artifact_params)
if @collection_artifact.save
render json: Api::V1::CollectionArtifactBlueprint.render(
@collection_artifact,
view: :full
), status: :created
else
render_validation_error_response(@collection_artifact)
end
end
def update
if @collection_artifact.update(collection_artifact_params)
render json: Api::V1::CollectionArtifactBlueprint.render(
@collection_artifact,
view: :full
)
else
render_validation_error_response(@collection_artifact)
end
end
def destroy
@collection_artifact.destroy
head :no_content
end
# POST /collection/artifacts/batch
# Creates multiple collection artifacts in a single request
def batch
items = batch_artifact_params[:collection_artifacts] || []
created = []
errors = []
ActiveRecord::Base.transaction do
items.each_with_index do |item_params, index|
collection_artifact = current_user.collection_artifacts.build(item_params)
if collection_artifact.save
created << collection_artifact
else
errors << {
index: index,
artifact_id: item_params[:artifact_id],
error: collection_artifact.errors.full_messages.join(', ')
}
end
end
end
status = errors.any? ? :multi_status : :created
render json: Api::V1::CollectionArtifactBlueprint.render(
created,
root: :artifacts,
meta: { created: created.size, errors: errors }
), status: status
end
private
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?
return if @target_user.collection_viewable_by?(current_user)
render json: { error: 'You do not have permission to view this collection' }, status: :forbidden
end
def set_collection_artifact_for_read
@collection_artifact = @target_user.collection_artifacts.find(params[:id])
rescue ActiveRecord::RecordNotFound
raise CollectionErrors::CollectionItemNotFound.new('artifact', params[:id])
end
def set_collection_artifact_for_write
@collection_artifact = current_user.collection_artifacts.find(params[:id])
rescue ActiveRecord::RecordNotFound
raise CollectionErrors::CollectionItemNotFound.new('artifact', params[:id])
end
def collection_artifact_params
params.require(:collection_artifact).permit(
:artifact_id, :element, :proficiency, :level, :nickname,
skill1: %i[modifier strength level],
skill2: %i[modifier strength level],
skill3: %i[modifier strength level],
skill4: %i[modifier strength level]
)
end
def batch_artifact_params
params.permit(collection_artifacts: [
:artifact_id, :element, :proficiency, :level, :nickname,
{ skill1: %i[modifier strength level] },
{ skill2: %i[modifier strength level] },
{ skill3: %i[modifier strength level] },
{ skill4: %i[modifier strength level] }
])
end
end
end
end

View file

@ -0,0 +1,119 @@
# frozen_string_literal: true
module Api
module V1
class GridArtifactsController < Api::V1::ApiController
before_action :find_grid_artifact, only: %i[update destroy]
before_action :find_party, only: %i[create update destroy]
before_action :find_grid_character, only: %i[create]
before_action :find_artifact, only: %i[create]
before_action :authorize_party_edit!, only: %i[create update destroy]
# POST /grid_artifacts
def create
# Check if grid_character already has an artifact
if @grid_character.grid_artifact.present?
@grid_character.grid_artifact.destroy
end
@grid_artifact = GridArtifact.new(
grid_artifact_params.merge(
grid_character_id: @grid_character.id,
artifact_id: @artifact.id
)
)
if @grid_artifact.save
render json: GridArtifactBlueprint.render(@grid_artifact, view: :nested, root: :grid_artifact), status: :created
else
render_validation_error_response(@grid_artifact)
end
end
# PATCH/PUT /grid_artifacts/:id
def update
if @grid_artifact.update(grid_artifact_params)
render json: GridArtifactBlueprint.render(@grid_artifact, view: :nested, root: :grid_artifact), status: :ok
else
render_validation_error_response(@grid_artifact)
end
end
# DELETE /grid_artifacts/:id
def destroy
if @grid_artifact.destroy
render json: GridArtifactBlueprint.render(@grid_artifact, view: :destroyed), status: :ok
else
render_unprocessable_entity_response(
Api::V1::GranblueError.new(@grid_artifact.errors.full_messages.join(', '))
)
end
end
private
def find_grid_artifact
@grid_artifact = GridArtifact.find_by(id: params[:id])
render_not_found_response('grid_artifact') unless @grid_artifact
end
def find_party
@party = if @grid_artifact
@grid_artifact.grid_character.party
else
Party.find_by(id: params[:party_id])
end
render_not_found_response('party') unless @party
end
def find_grid_character
@grid_character = GridCharacter.find_by(id: params.dig(:grid_artifact, :grid_character_id))
render_not_found_response('grid_character') unless @grid_character
end
def find_artifact
artifact_id = params.dig(:grid_artifact, :artifact_id)
@artifact = Artifact.find_by(id: artifact_id)
render_not_found_response('artifact') unless @artifact
end
def authorize_party_edit!
if @party.user.present?
authorize_user_party
else
authorize_anonymous_party
end
end
def authorize_user_party
return if current_user.present? && @party.user == current_user
render_unauthorized_response
end
def authorize_anonymous_party
provided_edit_key = edit_key.to_s.strip.force_encoding('UTF-8')
party_edit_key = @party.edit_key.to_s.strip.force_encoding('UTF-8')
return if valid_edit_key?(provided_edit_key, party_edit_key)
render_unauthorized_response
end
def valid_edit_key?(provided_edit_key, party_edit_key)
provided_edit_key.present? &&
provided_edit_key.bytesize == party_edit_key.bytesize &&
ActiveSupport::SecurityUtils.secure_compare(provided_edit_key, party_edit_key)
end
def grid_artifact_params
params.require(:grid_artifact).permit(
:grid_character_id, :artifact_id, :element, :proficiency, :level,
skill1: %i[modifier strength level],
skill2: %i[modifier strength level],
skill3: %i[modifier strength level],
skill4: %i[modifier strength level]
)
end
end
end
end

View file

@ -101,6 +101,17 @@ Rails.application.routes.draw do
resources :weapon_series, only: %i[index show create update destroy]
# Artifacts (read-only reference data)
resources :artifacts, only: %i[index show]
resources :artifact_skills, only: %i[index] do
collection do
get 'for_slot/:slot', action: :for_slot, as: :for_slot
end
end
# Grid artifacts
resources :grid_artifacts, only: %i[create update destroy]
# Grid endpoints - new prefixed versions
post 'grid_characters/resolve', to: 'grid_characters#resolve'
post 'grid_characters/update_uncap', to: 'grid_characters#update_uncap_level'
@ -134,6 +145,7 @@ Rails.application.routes.draw 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'
resources :artifacts, only: [:index, :show], controller: '/api/v1/collection_artifacts'
end
end
@ -156,6 +168,11 @@ Rails.application.routes.draw do
end
resources :job_accessories, controller: '/api/v1/collection_job_accessories',
only: [:index, :show, :create, :destroy]
resources :artifacts, only: [:create, :update, :destroy], controller: '/api/v1/collection_artifacts' do
collection do
post :batch
end
end
end
end