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
This commit is contained in:
Justin Edmund 2025-03-02 17:48:35 -08:00 committed by GitHub
parent 28a6b1894e
commit 7880ac76cc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 122 additions and 4 deletions

View file

@ -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.
#

View file

@ -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'

View file

@ -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"