hensei-api/app/controllers/api/v1/users_controller.rb
Justin Edmund a042847aab
Migrate to Query Builder (#179)
* Moved queries into PartyQueryBuilder service

PartyQueryBuilder supersedes PartyQueryingConcern as it is also used for UsersController (and is our fix for profiles being broken)

* Implement PartyQueryBuilder in controllers

* Update summon_transformer.rb

This should fix the transformer so that we properly capture summons and subaura summons

* Update parties_controller_spec.rb

* Add NewRelic license key

* Add Sentry

Why not?
2025-02-12 23:43:02 -08:00

240 lines
8.4 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# frozen_string_literal: true
module Api
module V1
class UsersController < Api::V1::ApiController
class ForbiddenError < StandardError; end
before_action :set, except: %w[create check_email check_username]
before_action :set_by_id, only: %w[info update]
MAX_CHARACTERS = 5
MAX_SUMMONS = 8
MAX_WEAPONS = 13
DEFAULT_MIN_CHARACTERS = 0
DEFAULT_MIN_SUMMONS = 0
DEFAULT_MIN_WEAPONS = 0
DEFAULT_MAX_CLEAR_TIME = 5400
def create
user = User.new(user_params)
if user.save!
token = Doorkeeper::AccessToken.create!(
application_id: nil,
resource_owner_id: user.id,
expires_in: 30.days,
scopes: 'public'
).token
return render json: UserBlueprint.render({
id: user.id,
username: user.username,
token: token
},
view: :token),
status: :created
end
render_validation_error_response(@user)
end
# TODO: Allow admins to update other users
def update
render json: UserBlueprint.render(@user, view: :minimal) if @user.update(user_params)
end
def info
render json: UserBlueprint.render(@user, view: :minimal)
end
def show
if @user.nil?
render_not_found_response('user')
else
base_query = Party.includes(
{ raid: :group },
:job,
:user,
:skill0,
:skill1,
:skill2,
:skill3,
:guidebook1,
:guidebook2,
:guidebook3,
{ characters: :character },
{ weapons: :weapon },
{ summons: :summon }
)
# Restrict to parties belonging to the profile owner
base_query = base_query.where(user_id: @user.id)
skip_privacy = (current_user&.id == @user.id)
query = PartyQueryBuilder.new(
base_query,
params: params,
current_user: current_user,
options: { skip_privacy: skip_privacy }
).build
parties = query.paginate(page: params[:page], per_page: PartyConstants::COLLECTION_PER_PAGE)
count = query.count
render json: UserBlueprint.render(@user,
view: :profile,
root: 'profile',
parties: parties,
meta: { count: count, total_pages: (count.to_f / PartyConstants::COLLECTION_PER_PAGE).ceil, per_page: PartyConstants::COLLECTION_PER_PAGE },
current_user: current_user
)
end
end
def check_email
render json: EmptyBlueprint.render_as_json(nil, email: params[:email], availability: true)
end
def check_username
render json: EmptyBlueprint.render_as_json(nil, username: params[:username], availability: true)
end
def destroy; end
private
def build_profile_query(profile_user)
query = Party.includes(
{ raid: :group },
:job,
:user,
:skill0,
:skill1,
:skill2,
:skill3,
:guidebook1,
:guidebook2,
:guidebook3,
{ characters: :character },
{ weapons: :weapon },
{ summons: :summon }
)
# Restrict to parties belonging to the profiles owner.
query = query.where(user_id: profile_user.id)
# Then apply the additional filters that we normally use:
query = query.where(name_quality)
.where(user_quality)
.where(original)
.where(privacy)
# And if there are any request-supplied filters, includes, or excludes:
query = apply_filters(query) if params[:filters].present?
query = apply_includes(query, params[:includes]) if params[:includes].present?
query = apply_excludes(query, params[:excludes]) if params[:excludes].present?
query.order(created_at: :desc)
end
def build_conditions
params = request.params
unless params['recency'].blank?
start_time = (DateTime.current - params['recency'].to_i.seconds)
.to_datetime.beginning_of_day
end
min_characters_count = params['characters_count'].blank? ? DEFAULT_MIN_CHARACTERS : params['characters_count'].to_i
min_summons_count = params['summons_count'].blank? ? DEFAULT_MIN_SUMMONS : params['summons_count'].to_i
min_weapons_count = params['weapons_count'].blank? ? DEFAULT_MIN_WEAPONS : params['weapons_count'].to_i
max_clear_time = params['max_clear_time'].blank? ? DEFAULT_MAX_CLEAR_TIME : params['max_clear_time'].to_i
{}.tap do |hash|
# Basic filters
hash[:element] = params['element'].to_i unless params['element'].blank?
hash[:raid] = params['raid'] unless params['raid'].blank?
hash[:created_at] = start_time..DateTime.current unless params['recency'].blank?
# Advanced filters: Team parameters
unless params['full_auto'].blank? || params['full_auto'].to_i == -1
hash[:full_auto] =
params['full_auto'].to_i
end
unless params['auto_guard'].blank? || params['auto_guard'].to_i == -1
hash[:auto_guard] =
params['auto_guard'].to_i
end
unless params['charge_attack'].blank? || params['charge_attack'].to_i == -1
hash[:charge_attack] =
params['charge_attack'].to_i
end
# Turn count of 0 will not be displayed, so disallow on the frontend or set default to 1
# How do we do the same for button count since that can reasonably be 1?
# hash[:turn_count] = params['turn_count'].to_i unless params['turn_count'].blank? || params['turn_count'].to_i <= 0
# hash[:button_count] = params['button_count'].to_i unless params['button_count'].blank?
# hash[:clear_time] = 0..max_clear_time
# Advanced filters: Object counts
hash[:characters_count] = min_characters_count..MAX_CHARACTERS
hash[:summons_count] = min_summons_count..MAX_SUMMONS
hash[:weapons_count] = min_weapons_count..MAX_WEAPONS
end
end
def original
return if params.key?('original') || params['original'].blank? || params['original'] == '0'
'source_party_id IS NULL'
end
def user_quality
return if params.key?('user_quality') || params[:user_quality].nil? || params[:user_quality] == '0'
'user_id IS NOT NULL'
end
def name_quality
low_quality = [
'Untitled',
'Remix of Untitled',
'Remix of Remix of Untitled',
'Remix of Remix of Remix of Untitled',
'Remix of Remix of Remix of Remix of Untitled',
'Remix of Remix of Remix of Remix of Remix of Untitled',
'無題',
'無題のリミックス',
'無題のリミックスのリミックス',
'無題のリミックスのリミックスのリミックス',
'無題のリミックスのリミックスのリミックスのリミックス',
'無題のリミックスのリミックスのリミックスのリミックスのリミックス'
]
joined_names = low_quality.map { |name| "'#{name}'" }.join(',')
return if params.key?('name_quality') || params[:name_quality].nil? || params[:name_quality] == '0'
"name NOT IN (#{joined_names})"
end
def privacy
return if admin_mode
'visibility = 1' if current_user != @user
end
# Specify whitelisted properties that can be modified.
def set
@user = User.find_by('lower(username) = ?', params[:id].downcase)
end
def set_by_id
@user = User.find_by('id = ?', params[:id])
end
def user_params
params.require(:user).permit(
:username, :email, :password, :password_confirmation,
:granblue_id, :picture, :element, :language, :gender, :private, :theme
)
end
end
end
end