From bd5f1b024075f3ca27cc40e8439a712647e6f84f Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 3 Dec 2025 10:45:57 -0800 Subject: [PATCH] add weapon_series API endpoints and update blueprints --- .../api/v1/grid_weapon_blueprint.rb | 4 +- app/blueprints/api/v1/weapon_blueprint.rb | 19 ++++- .../api/v1/weapon_series_blueprint.rb | 25 ++++++ .../api/v1/weapon_series_controller.rb | 76 +++++++++++++++++++ config/routes.rb | 2 + 5 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 app/blueprints/api/v1/weapon_series_blueprint.rb create mode 100644 app/controllers/api/v1/weapon_series_controller.rb diff --git a/app/blueprints/api/v1/grid_weapon_blueprint.rb b/app/blueprints/api/v1/grid_weapon_blueprint.rb index 861b10e..11042e9 100644 --- a/app/blueprints/api/v1/grid_weapon_blueprint.rb +++ b/app/blueprints/api/v1/grid_weapon_blueprint.rb @@ -31,8 +31,8 @@ module Api blueprint: WeaponKeyBlueprint, if: ->(_field_name, w, _options) { w.weapon.present? && - w.weapon.series.present? && - [2, 3, 17, 24, 34].include?(w.weapon.series) + w.weapon.weapon_series.present? && + w.weapon.weapon_series.has_weapon_keys } end diff --git a/app/blueprints/api/v1/weapon_blueprint.rb b/app/blueprints/api/v1/weapon_blueprint.rb index b439d6d..b8cb630 100644 --- a/app/blueprints/api/v1/weapon_blueprint.rb +++ b/app/blueprints/api/v1/weapon_blueprint.rb @@ -13,7 +13,24 @@ module Api # Primary information fields :granblue_id, :element, :proficiency, :max_level, :max_skill_level, :max_awakening_level, :limit, :rarity, - :series, :ax, :ax_type, :promotions + :ax, :ax_type, :promotions + + # Series - returns full object if weapon_series is present, fallback to legacy integer + field :series do |w| + if w.weapon_series.present? + { + id: w.weapon_series_id, + slug: w.weapon_series.slug, + name: { + en: w.weapon_series.name_en, + ja: w.weapon_series.name_jp + } + } + else + # Legacy fallback for backwards compatibility + w.series + end + end field :promotion_names do |w| w.promotion_names diff --git a/app/blueprints/api/v1/weapon_series_blueprint.rb b/app/blueprints/api/v1/weapon_series_blueprint.rb new file mode 100644 index 0000000..eaf7dfe --- /dev/null +++ b/app/blueprints/api/v1/weapon_series_blueprint.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module Api + module V1 + class WeaponSeriesBlueprint < ApiBlueprint + field :name do |ws| + { + en: ws.name_en, + ja: ws.name_jp + } + end + + fields :slug, :order + + view :full do + fields :extra, :element_changeable, :has_weapon_keys, + :has_awakening, :has_ax_skills + + field :weapon_count do |ws| + ws.weapons.count + end + end + end + end +end diff --git a/app/controllers/api/v1/weapon_series_controller.rb b/app/controllers/api/v1/weapon_series_controller.rb new file mode 100644 index 0000000..f29bdd6 --- /dev/null +++ b/app/controllers/api/v1/weapon_series_controller.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +module Api + module V1 + class WeaponSeriesController < Api::V1::ApiController + before_action :set_weapon_series, only: %i[show update destroy] + before_action :ensure_editor_role, only: %i[create update destroy] + + # GET /weapon_series + def index + weapon_series = WeaponSeries.ordered + render json: WeaponSeriesBlueprint.render(weapon_series) + end + + # GET /weapon_series/:id + def show + render json: WeaponSeriesBlueprint.render(@weapon_series, view: :full) + end + + # POST /weapon_series + def create + weapon_series = WeaponSeries.new(weapon_series_params) + + if weapon_series.save + render json: WeaponSeriesBlueprint.render(weapon_series, view: :full), status: :created + else + render_validation_error_response(weapon_series) + end + end + + # PATCH/PUT /weapon_series/:id + def update + if @weapon_series.update(weapon_series_params) + render json: WeaponSeriesBlueprint.render(@weapon_series, view: :full) + else + render_validation_error_response(@weapon_series) + end + end + + # DELETE /weapon_series/:id + def destroy + if @weapon_series.weapons.exists? + render json: ErrorBlueprint.render(nil, error: { + message: 'Cannot delete series with associated weapons', + code: 'has_dependencies' + }), status: :unprocessable_entity + else + @weapon_series.destroy! + head :no_content + end + end + + private + + def set_weapon_series + # Support lookup by slug or UUID + @weapon_series = WeaponSeries.find_by(slug: params[:id]) || WeaponSeries.find(params[:id]) + end + + def ensure_editor_role + return if current_user&.role && current_user.role >= 7 + + Rails.logger.warn "[WEAPON_SERIES] Unauthorized access attempt by user #{current_user&.id}" + render json: { error: 'Unauthorized - Editor role required' }, status: :unauthorized + end + + def weapon_series_params + params.require(:weapon_series).permit( + :name_en, :name_jp, :slug, :order, + :extra, :element_changeable, :has_weapon_keys, + :has_awakening, :has_ax_skills + ) + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 4e2c9da..db4665e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -99,6 +99,8 @@ Rails.application.routes.draw do get 'raids/:id', to: 'raids#show' get 'weapon_keys', to: 'weapon_keys#all' + resources :weapon_series, only: %i[index show 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'