From 7880ac76ccf08c46770ddcfb2940aca6ba18a7b9 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sun, 2 Mar 2025 17:48:35 -0800 Subject: [PATCH] Add data ingestion endpoints (#195) * Update schema.rb * Add endpoints for importing game data This lets privileged users import canonical data for characters, weapons and summons directly from the game --- app/controllers/api/v1/import_controller.rb | 111 +++++++++++++++++++- config/routes.rb | 3 + db/schema.rb | 12 ++- 3 files changed, 122 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/v1/import_controller.rb b/app/controllers/api/v1/import_controller.rb index 928768f..84f9e07 100644 --- a/app/controllers/api/v1/import_controller.rb +++ b/app/controllers/api/v1/import_controller.rb @@ -27,6 +27,8 @@ module Api 6 => 5 }.freeze + before_action :ensure_admin_role, only: %i[weapons summons characters] + ## # Processes an import request. # @@ -49,9 +51,9 @@ module Api end unless raw_params['deck'].is_a?(Hash) && - raw_params['deck'].key?('pc') && - raw_params['deck'].key?('npc') - Rails.logger.error "[IMPORT] Deck data incomplete or missing." + raw_params['deck'].key?('pc') && + raw_params['deck'].key?('npc') + Rails.logger.error '[IMPORT] Deck data incomplete or missing.' return render json: { error: 'Invalid deck data' }, status: :unprocessable_content end @@ -68,8 +70,111 @@ module Api render json: { error: e.message }, status: :unprocessable_content end + def weapons + Rails.logger.info '[IMPORT] Checking weapon gamedata input...' + + body = parse_request_body + return unless body + + weapon = Weapon.find_by(granblue_id: body['id']) + unless weapon + Rails.logger.error "[IMPORT] Weapon not found with ID: #{body['id']}" + return render json: { error: 'Weapon not found' }, status: :not_found + end + + lang = params[:lang] + unless %w[en jp].include?(lang) + Rails.logger.error "[IMPORT] Invalid language: #{lang}" + return render json: { error: 'Invalid language' }, status: :unprocessable_content + end + + begin + weapon.update!( + "game_raw_#{lang}" => body.to_json + ) + render json: { message: 'Weapon gamedata updated successfully' }, status: :ok + rescue StandardError => e + Rails.logger.error "[IMPORT] Failed to update weapon gamedata: #{e.message}" + render json: { error: e.message }, status: :unprocessable_content + end + end + + def summons + Rails.logger.info '[IMPORT] Checking summon gamedata input...' + + body = parse_request_body + return unless body + + summon = Summon.find_by(granblue_id: body['id']) + unless summon + Rails.logger.error "[IMPORT] Summon not found with ID: #{body['id']}" + return render json: { error: 'Summon not found' }, status: :not_found + end + + lang = params[:lang] + unless %w[en jp].include?(lang) + Rails.logger.error "[IMPORT] Invalid language: #{lang}" + return render json: { error: 'Invalid language' }, status: :unprocessable_content + end + + begin + summon.update!( + "game_raw_#{lang}" => body.to_json + ) + render json: { message: 'Summon gamedata updated successfully' }, status: :ok + rescue StandardError => e + Rails.logger.error "[IMPORT] Failed to update summon gamedata: #{e.message}" + render json: { error: e.message }, status: :unprocessable_content + end + end + + ## + # Updates character gamedata from JSON blob. + # + # @return [void] Renders JSON response with success or error message. + def characters + Rails.logger.info '[IMPORT] Checking character gamedata input...' + + body = parse_request_body + return unless body + + character = Character.find_by(granblue_id: body['id']) + unless character + Rails.logger.error "[IMPORT] Character not found with ID: #{body['id']}" + return render json: { error: 'Character not found' }, status: :not_found + end + + lang = params[:lang] + unless %w[en jp].include?(lang) + Rails.logger.error "[IMPORT] Invalid language: #{lang}" + return render json: { error: 'Invalid language' }, status: :unprocessable_content + end + + begin + character.update!( + "game_raw_#{lang}" => body.to_json + ) + render json: { message: 'Character gamedata updated successfully' }, status: :ok + rescue StandardError => e + Rails.logger.error "[IMPORT] Failed to update character gamedata: #{e.message}" + render json: { error: e.message }, status: :unprocessable_content + end + end + private + ## + # Ensures the current user has admin role (role 9). + # Renders an error if the user is not an admin. + # + # @return [void] + def ensure_admin_role + return if current_user&.role == 9 + + Rails.logger.error "[IMPORT] Unauthorized access attempt by user #{current_user&.id}" + render json: { error: 'Unauthorized' }, status: :unauthorized + end + ## # Reads and parses the raw JSON request body. # diff --git a/config/routes.rb b/config/routes.rb index fafbf95..7523e2e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -20,6 +20,9 @@ Rails.application.routes.draw do get 'version', to: 'api#version' post 'import', to: 'import#create' + post 'import/weapons', to: 'import#weapons' + post 'import/summons', to: 'import#summons' + post 'import/characters', to: 'import#characters' get 'users/info/:id', to: 'users#info' diff --git a/db/schema.rb b/db/schema.rb index 80636d3..a69bf75 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_02_18_025315) do +ActiveRecord::Schema[8.0].define(version: 2025_03_01_143956) do # These are extensions that must be enabled in order to support this database enable_extension "btree_gin" enable_extension "pg_catalog.plpgsql" @@ -67,6 +67,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_18_025315) do t.string "kamigame", default: "" t.string "nicknames_en", default: [], null: false, array: true t.string "nicknames_jp", default: [], null: false, array: true + t.text "wiki_raw" + t.text "game_raw_en" + t.text "game_raw_jp" t.index ["granblue_id"], name: "index_characters_on_granblue_id" t.index ["name_en"], name: "index_characters_on_name_en", opclass: :gin_trgm_ops, using: :gin end @@ -420,6 +423,9 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_18_025315) do t.date "transcendence_date" t.string "nicknames_en", default: [], null: false, array: true t.string "nicknames_jp", default: [], null: false, array: true + t.text "wiki_raw" + t.text "game_raw_en" + t.text "game_raw_jp" t.index ["granblue_id"], name: "index_summons_on_granblue_id" t.index ["name_en"], name: "index_summons_on_name_en", opclass: :gin_trgm_ops, using: :gin end @@ -495,6 +501,10 @@ ActiveRecord::Schema[8.0].define(version: 2025_02_18_025315) do t.date "transcendence_date" t.string "recruits" t.integer "series" + t.integer "new_series" + t.text "wiki_raw" + t.text "game_raw_en" + t.text "game_raw_jp" t.index ["granblue_id"], name: "index_weapons_on_granblue_id" t.index ["name_en"], name: "index_weapons_on_name_en", opclass: :gin_trgm_ops, using: :gin t.index ["recruits"], name: "index_weapons_on_recruits"