From ec3b42101b10e1d310e9b55fa23391c4b068055c Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 3 Dec 2022 11:45:00 -0800 Subject: [PATCH] Move logic to JobsController It was getting too heavy to keep in PartiesController --- app/controllers/api/v1/jobs_controller.rb | 152 ++++++++++++++++++- app/controllers/api/v1/parties_controller.rb | 97 ++---------- app/views/api/v1/jobs/update.json.rabl | 20 +++ config/routes.rb | 105 ++++++------- 4 files changed, 232 insertions(+), 142 deletions(-) create mode 100644 app/views/api/v1/jobs/update.json.rabl diff --git a/app/controllers/api/v1/jobs_controller.rb b/app/controllers/api/v1/jobs_controller.rb index c6fbabf..3191380 100644 --- a/app/controllers/api/v1/jobs_controller.rb +++ b/app/controllers/api/v1/jobs_controller.rb @@ -1,6 +1,152 @@ class Api::V1::JobsController < Api::V1::ApiController - def all - @jobs = Job.all() - render :all, status: :ok + before_action :set, only: %w[update_job update_job_skills] + + def all + @jobs = Job.all() + render :all, status: :ok + end + + def update_job + raise NoJobProvidedError unless job_params[:job_id].present? + + # Extract job and find its main skills + job = Job.find(job_params[:job_id]) + + main_skills = JobSkill.where(job: job.id, main: true) + + # Update the party + @party.job = job + main_skills.each_with_index do |skill, index| + @party["skill#{index}_id"] = skill.id end + + # Check for incompatible Base and EMP skills + %w[skill1_id skill2_id skill3_id].each do |key| + @party[key] = nil if mismatched_skill(@party.job, JobSkill.find(@party[key])) + end + + render :update, status: :ok if @party.save! + end + + def update_job_skills + throw NoJobSkillProvidedError unless job_params[:skill1_id] || job_params[:skill2_id] || job_params[:skill3_id] + + # Determine which incoming keys contain new skills + skill_keys = %w[skill1_id skill2_id skill3_id] + new_skill_keys = job_params.keys.select { |key| skill_keys.include?(key) } + + # If there are new skills, merge them with the existing skills + unless new_skill_keys.empty? + existing_skills = { + 1 => @party.skill1, + 2 => @party.skill2, + 3 => @party.skill3 + } + new_skill_ids = new_skill_keys.map { |key| job_params[key] } + new_skill_ids.map do |id| + skill = JobSkill.find(id) + if mismatched_skill(@party.job, skill) + raise Api::V1::IncompatibleSkillError.new(job: @party.job, skill: skill) + end + end + + positions = extract_positions_from_keys(new_skill_keys) + new_skills = merge_skills_with_existing_skills(existing_skills, new_skill_ids, positions) + + new_skill_ids = new_skills.each_with_object({}) do |(index, skill), memo| + memo["skill#{index}_id"] = skill.id + end + + @party.attributes = new_skill_ids + end + + render :update, status: :ok if @party.save! + end + + private + + def merge_skills_with_existing_skills( + existing_skills, + new_skill_ids, + positions + ) + new_skills = new_skill_ids.map { |id| JobSkill.find(id) } + + new_skills.each_with_index do |skill, index| + existing_skills = place_skill_in_existing_skills(existing_skills, skill, positions[index]) + end + + existing_skills + end + + def place_skill_in_existing_skills(existing_skills, skill, position) + old_position = existing_skills.key(existing_skills.detect { |_, value| value.id == skill.id }) + + if old_position + existing_skills = swap_skills_at_position(existing_skills, skill, position, old_position[0]) + else + # Test if skill will exceed allowances of skill types + skill_type = skill.sub ? 'sub' : 'emp' + + unless can_add_skill_of_type(existing_skills, position, skill_type) + raise Api::V1::TooManySkillsOfTypeError.new(skill_type: skill_type) + end + + existing_skills[position] = skill + end + + existing_skills + end + + def swap_skills_at_position(skills, new_skill, position1, position2) + # Check desired position for a skill + displaced_skill = skills[position1] if skills[position1].present? + + # Put skill in new position + skills[position1] = new_skill + skills[position2] = displaced_skill + + skills + end + + def extract_positions_from_keys(keys) + # Subtract by 1 because we won't operate on the 0th skill, so we don't pass it + keys.map { |key| key['skill'.length].to_i } + end + + def can_add_skill_of_type(skills, position, type) + max_skill_of_type = 2 + skills_to_check = skills.reject { |key, _| key == position } + + sum = skills_to_check.values.count { |value| value.send(type) } + + sum + 1 <= max_skill_of_type + end + + def mismatched_skill(job, skill) + mismatched_emp = (skill.job.id != job.id) && skill.emp + mismatched_base = (job.row != 'ex2' || skill.job.base_job.id != job.base_job.id) && skill.base + + if %w[4 5 ex2].include?(job.row) + true if mismatched_emp || mismatched_base + elsif mismatched_emp + true + else + false + end + end + + def set + @party = Party.where('id = ?', params[:id]).first + end + + def job_params + params.require(:party).permit( + :job_id, + :skill0_id, + :skill1_id, + :skill2_id, + :skill3_id + ) + end end diff --git a/app/controllers/api/v1/parties_controller.rb b/app/controllers/api/v1/parties_controller.rb index cdd67c2..bf3361e 100644 --- a/app/controllers/api/v1/parties_controller.rb +++ b/app/controllers/api/v1/parties_controller.rb @@ -5,7 +5,13 @@ class Api::V1::PartiesController < Api::V1::ApiController def create @party = Party.new(shortcode: random_string) - @party.extra = party_params["extra"] + @party.extra = party_params['extra'] + + job = Job.find(party_params['job_id']) if party_params['job_id'].present? + job_skills = JobSkill.where(job: job.id, main: true) + job_skills.each_with_index do |skill, index| + @party["skill#{index}_id"] = skill.id + end @party.user = current_user if current_user @@ -21,28 +27,6 @@ class Api::V1::PartiesController < Api::V1::ApiController render_unauthorized_response else @party.attributes = party_params.except(:skill1_id, :skill2_id, :skill3_id) - ap party_params - # Determine which incoming keys contain new skills - skill_keys = %w[skill1_id skill2_id skill3_id] - if (party_params.keys & skill_keys).any? - new_skill_keys = party_params.keys - skill_keys - - # If there are new skills, merge them with the existing skills - unless new_skill_keys.empty? - existing_skills = [@party.skill1, @party.skill2, @party.skill3] - new_skill_ids = new_skill_keys.map { |key| party_params[key] } - positions = extract_positions_from_keys(new_skill_keys) - - new_skills = merge_skills_with_existing_skills(existing_skills, new_skill_ids, positions) - - new_skill_ids = {} - new_skills.each_with_index do |skill, index| - new_skill_ids["skill#{index + 1}_id"] = skill.id - end - - @party.attributes = new_skill_ids - end - end end render :update, status: :ok if @party.save! @@ -121,8 +105,8 @@ class Api::V1::PartiesController < Api::V1::ApiController def destroy if @party.user != current_user render_unauthorized_response - else - render :destroyed, status: :ok if @party.destroy + elsif @party.destroy + render :destroyed, status: :ok end end @@ -143,69 +127,6 @@ class Api::V1::PartiesController < Api::V1::ApiController private - def merge_skills_with_existing_skills( - existing_skills, - new_skill_ids, - positions - ) - new_skills = [] - new_skill_ids.each { |id| new_skills << JobSkill.find(id) } - - progress = existing_skills - new_skills.each do |skill, index| - progress = place_skill_in_existing_skills(progress, skill, positions[0]) - end - - progress - end - - def place_skill_in_existing_skills(existing_skills, skill, position) - old_position = existing_skills.index { |x| x.id == skill.id } - - if old_position - existing_skills = swap_skills_at_position(existing_skills, skill, position, old_position) - else - # Test if skill will exceed allowances of skill types - skill_type = skill.sub ? 'sub' : 'emp' - unless can_add_skill_of_type(existing_skills, position, skill_type) - raise Api::V1::TooManySkillsOfTypeError.new(skill_type: skill_type) - end - - existing_skills[position] = skill - end - - existing_skills - end - - def swap_skills_at_position(skills, new_skill, position1, position2) - # Check desired position for a skill - displaced_skill = skills[position1] if skills[position1].present? - - # Put skill in new position - skills[position1] = new_skill - skills[position2] = displaced_skill - - skills - end - - def can_add_skill_of_type(skills, position, type) - max_skill_of_type = 2 - - count = skills.reject - .with_index { |_el, index| index == position } - .reduce(0) do |sum, skill| - sum + 1 if type == 'emp' && skill.emp - sum + 1 if type == 'sub' && skill.sub - end - - count + 1 <= max_skill_of_type - end - - def extract_positions_from_keys(keys) - # Subtract by 1 because we won't operate on the 0th skill, so we don't pass it - keys.map { |key| key["skill".length].to_i - 1 } - end - def random_string numChars = 6 o = [("a".."z"), ("A".."Z"), (0..9)].map(&:to_a).flatten diff --git a/app/views/api/v1/jobs/update.json.rabl b/app/views/api/v1/jobs/update.json.rabl new file mode 100644 index 0000000..6cb851f --- /dev/null +++ b/app/views/api/v1/jobs/update.json.rabl @@ -0,0 +1,20 @@ +object @party + +attributes :id, :user_id, :shortcode + +node :is_extra do |p| + p.extra +end + +node :job do |p| + partial("jobs/base", object: p.job) +end + +node :job_skills do |p| + { + "0" => partial("job_skills/base", object: p.skill0), + "1" => partial("job_skills/base", object: p.skill1), + "2" => partial("job_skills/base", object: p.skill2), + "3" => partial("job_skills/base", object: p.skill3), + } +end diff --git a/config/routes.rb b/config/routes.rb index d722ad4..ac6b33a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,54 +1,57 @@ Rails.application.routes.draw do - use_doorkeeper do - controllers :tokens => 'tokens' - skip_controllers :applications, :authorized_applications - end - - namespace :api, defaults: { format: :json } do - namespace :v1 do - resources :parties, only: [:index, :create, :update, :destroy] - resources :users, only: [:create, :update, :show] - resources :grid_weapons, only: [:update] - resources :favorites, only: [:create] - - get 'users/info/:id', to: 'users#info' - - get 'parties/favorites', to: 'parties#favorites' - get 'parties/:id', to: 'parties#show' - get 'parties/:id/weapons', to: 'parties#weapons' - get 'parties/:id/summons', to: 'parties#summons' - get 'parties/:id/characters', to: 'parties#characters' - - post 'check/email', to: 'users#check_email' - post 'check/username', to: 'users#check_username' - - post 'search/characters', to: 'search#characters' - post 'search/weapons', to: 'search#weapons' - post 'search/summons', to: 'search#summons' - post 'search/job_skills', to: 'search#job_skills' - - get 'jobs', to: 'jobs#all' - - get 'jobs/skills', to: 'job_skills#all' - get 'jobs/:id/skills', to: 'job_skills#job' - - get 'raids', to: 'raids#all' - get 'weapon_keys', to: 'weapon_keys#all' - - post 'characters', to: 'grid_characters#create' - post 'characters/resolve', to: 'grid_characters#resolve' - post 'characters/update_uncap', to: 'grid_characters#update_uncap_level' - delete 'characters', to: 'grid_characters#destroy' - - post 'weapons', to: 'grid_weapons#create' - post 'weapons/update_uncap', to: 'grid_weapons#update_uncap_level' - delete 'weapons', to: 'grid_weapons#destroy' - - post 'summons', to: 'grid_summons#create' - post 'summons/update_uncap', to: 'grid_summons#update_uncap_level' - delete 'summons', to: 'grid_summons#destroy' - - delete 'favorites', to: 'favorites#destroy' - end + use_doorkeeper do + controllers :tokens => 'tokens' + skip_controllers :applications, :authorized_applications + end + + namespace :api, defaults: { format: :json } do + namespace :v1 do + resources :parties, only: [:index, :create, :update, :destroy] + resources :users, only: [:create, :update, :show] + resources :grid_weapons, only: [:update] + resources :favorites, only: [:create] + + get 'users/info/:id', to: 'users#info' + + get 'parties/favorites', to: 'parties#favorites' + get 'parties/:id', to: 'parties#show' + get 'parties/:id/weapons', to: 'parties#weapons' + get 'parties/:id/summons', to: 'parties#summons' + get 'parties/:id/characters', to: 'parties#characters' + + put 'parties/:id/job', to: 'jobs#update_job' + put 'parties/:id/job_skills', to: 'jobs#update_job_skills' + + post 'check/email', to: 'users#check_email' + post 'check/username', to: 'users#check_username' + + post 'search/characters', to: 'search#characters' + post 'search/weapons', to: 'search#weapons' + post 'search/summons', to: 'search#summons' + post 'search/job_skills', to: 'search#job_skills' + + get 'jobs', to: 'jobs#all' + + get 'jobs/skills', to: 'job_skills#all' + get 'jobs/:id/skills', to: 'job_skills#job' + + get 'raids', to: 'raids#all' + get 'weapon_keys', to: 'weapon_keys#all' + + post 'characters', to: 'grid_characters#create' + post 'characters/resolve', to: 'grid_characters#resolve' + post 'characters/update_uncap', to: 'grid_characters#update_uncap_level' + delete 'characters', to: 'grid_characters#destroy' + + post 'weapons', to: 'grid_weapons#create' + post 'weapons/update_uncap', to: 'grid_weapons#update_uncap_level' + delete 'weapons', to: 'grid_weapons#destroy' + + post 'summons', to: 'grid_summons#create' + post 'summons/update_uncap', to: 'grid_summons#update_uncap_level' + delete 'summons', to: 'grid_summons#destroy' + + delete 'favorites', to: 'favorites#destroy' end + end end