From b41a67df307ae3d15627d897c41a2f6e5f065027 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sun, 27 Nov 2022 14:12:16 -0800 Subject: [PATCH 01/29] Add job skills table --- .../20221120055510_add_job_skills_table.rb | 14 ++++++++++++++ ...331_remove_null_constraint_from_job_skills.rb | 6 ++++++ .../20221120075133_add_order_to_job_skills.rb | 5 +++++ ...120135403_add_base_and_group_to_job_skills.rb | 6 ++++++ ...0221120145204_remove_group_from_job_skills.rb | 5 +++++ db/schema.rb | 16 +++++++++++++++- 6 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20221120055510_add_job_skills_table.rb create mode 100644 db/migrate/20221120065331_remove_null_constraint_from_job_skills.rb create mode 100644 db/migrate/20221120075133_add_order_to_job_skills.rb create mode 100644 db/migrate/20221120135403_add_base_and_group_to_job_skills.rb create mode 100644 db/migrate/20221120145204_remove_group_from_job_skills.rb diff --git a/db/migrate/20221120055510_add_job_skills_table.rb b/db/migrate/20221120055510_add_job_skills_table.rb new file mode 100644 index 0000000..b21a873 --- /dev/null +++ b/db/migrate/20221120055510_add_job_skills_table.rb @@ -0,0 +1,14 @@ +class AddJobSkillsTable < ActiveRecord::Migration[6.1] + def change + create_table :job_skills, id: :uuid, default: -> { "gen_random_uuid()" } do |t| + t.references :job, type: :uuid + t.string :name_en, null: false, unique: true + t.string :name_jp, null: false, unique: true + t.string :slug, null: false, unique: true + t.integer :color, null: false + t.boolean :main, default: false + t.boolean :sub, default: false + t.boolean :emp, default: false + end + end +end diff --git a/db/migrate/20221120065331_remove_null_constraint_from_job_skills.rb b/db/migrate/20221120065331_remove_null_constraint_from_job_skills.rb new file mode 100644 index 0000000..8aa1a7f --- /dev/null +++ b/db/migrate/20221120065331_remove_null_constraint_from_job_skills.rb @@ -0,0 +1,6 @@ +class RemoveNullConstraintFromJobSkills < ActiveRecord::Migration[6.1] + def change + change_column :job_skills, :name_en, :string, unique: false + change_column :job_skills, :name_jp, :string, unique: false + end +end diff --git a/db/migrate/20221120075133_add_order_to_job_skills.rb b/db/migrate/20221120075133_add_order_to_job_skills.rb new file mode 100644 index 0000000..c244627 --- /dev/null +++ b/db/migrate/20221120075133_add_order_to_job_skills.rb @@ -0,0 +1,5 @@ +class AddOrderToJobSkills < ActiveRecord::Migration[6.1] + def change + add_column :job_skills, :order, :integer + end +end diff --git a/db/migrate/20221120135403_add_base_and_group_to_job_skills.rb b/db/migrate/20221120135403_add_base_and_group_to_job_skills.rb new file mode 100644 index 0000000..1644821 --- /dev/null +++ b/db/migrate/20221120135403_add_base_and_group_to_job_skills.rb @@ -0,0 +1,6 @@ +class AddBaseAndGroupToJobSkills < ActiveRecord::Migration[6.1] + def change + add_column :job_skills, :base, :boolean, default: false + add_column :job_skills, :group, :integer + end +end diff --git a/db/migrate/20221120145204_remove_group_from_job_skills.rb b/db/migrate/20221120145204_remove_group_from_job_skills.rb new file mode 100644 index 0000000..6f82de9 --- /dev/null +++ b/db/migrate/20221120145204_remove_group_from_job_skills.rb @@ -0,0 +1,5 @@ +class RemoveGroupFromJobSkills < ActiveRecord::Migration[6.1] + def change + remove_column :job_skills, :group, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 9d4fe9d..748bd82 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.define(version: 2022_11_17_070255) do +ActiveRecord::Schema.define(version: 2022_11_20_145204) do # These are extensions that must be enabled in order to support this database enable_extension "btree_gin" @@ -102,6 +102,20 @@ ActiveRecord::Schema.define(version: 2022_11_17_070255) do t.index ["weapon_id"], name: "index_grid_weapons_on_weapon_id" end + create_table "job_skills", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| + t.uuid "job_id" + t.string "name_en", null: false + t.string "name_jp", null: false + t.string "slug", null: false + t.integer "color", null: false + t.boolean "main", default: false + t.boolean "sub", default: false + t.boolean "emp", default: false + t.integer "order" + t.boolean "base", default: false + t.index ["job_id"], name: "index_job_skills_on_job_id" + end + create_table "jobs", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| t.string "name_en" t.string "name_jp" From b9bea3ecf43188f1a59bca1c4739bd2fff15eac5 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sun, 27 Nov 2022 14:12:33 -0800 Subject: [PATCH 02/29] Add model --- app/models/job.rb | 10 +++++----- app/models/job_skill.rb | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 app/models/job_skill.rb diff --git a/app/models/job.rb b/app/models/job.rb index 1263a02..8c1867f 100644 --- a/app/models/job.rb +++ b/app/models/job.rb @@ -1,7 +1,7 @@ class Job < ApplicationRecord - belongs_to :party - - def display_resource(job) - job.name_en - end + belongs_to :party + + def display_resource(job) + job.name_en + end end diff --git a/app/models/job_skill.rb b/app/models/job_skill.rb new file mode 100644 index 0000000..9705435 --- /dev/null +++ b/app/models/job_skill.rb @@ -0,0 +1,7 @@ +class JobSkill < ApplicationRecord + belongs_to :job + + def display_resource(skill) + skill.name_en + end +end From 5fe712c31aa5ab3e6584af7b74b6c22eec8188a3 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sun, 27 Nov 2022 14:12:52 -0800 Subject: [PATCH 03/29] Add route to get skills for given job --- app/controllers/api/v1/jobs_controller.rb | 9 ++++++++- app/views/api/v1/jobs/skills.json.rabl | 11 +++++++++++ config/routes.rb | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 app/views/api/v1/jobs/skills.json.rabl diff --git a/app/controllers/api/v1/jobs_controller.rb b/app/controllers/api/v1/jobs_controller.rb index f79a40d..7084bce 100644 --- a/app/controllers/api/v1/jobs_controller.rb +++ b/app/controllers/api/v1/jobs_controller.rb @@ -3,4 +3,11 @@ class Api::V1::JobsController < Api::V1::ApiController @jobs = Job.all() render :all, status: :ok end -end \ No newline at end of file + + def skills + job = Job.find(params[:id]) + + @skills = JobSkill.where(job: job).or(JobSkill.where(sub: true)) + render :skills, status: :ok + end +end diff --git a/app/views/api/v1/jobs/skills.json.rabl b/app/views/api/v1/jobs/skills.json.rabl new file mode 100644 index 0000000..bd7ab3d --- /dev/null +++ b/app/views/api/v1/jobs/skills.json.rabl @@ -0,0 +1,11 @@ +collection @skills + +attributes :id, :job, :slug, :color, :main, :base, :sub, :emp, :order + +node :name do |w| + { + :en => w.name_en, + :ja => w.name_jp + } +end + diff --git a/config/routes.rb b/config/routes.rb index cf1c984..526992c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,6 +27,7 @@ Rails.application.routes.draw do post 'search/summons', to: 'search#summons' get 'jobs', to: 'jobs#all' + get 'jobs/:id/skills', to: 'jobs#skills' get 'raids', to: 'raids#all' get 'weapon_keys', to: 'weapon_keys#all' From 5755aab240ad58e2e681c6be45d2ca8e9bcb67e8 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sun, 27 Nov 2022 18:54:32 -0800 Subject: [PATCH 04/29] Disable object root --- app/views/api/v1/jobs/skills.json.rabl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/api/v1/jobs/skills.json.rabl b/app/views/api/v1/jobs/skills.json.rabl index bd7ab3d..d0592e1 100644 --- a/app/views/api/v1/jobs/skills.json.rabl +++ b/app/views/api/v1/jobs/skills.json.rabl @@ -1,4 +1,4 @@ -collection @skills +collection @skills, object_root: false attributes :id, :job, :slug, :color, :main, :base, :sub, :emp, :order From 353db31ef7a49804d2e2e810e887433388bfdcf5 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sun, 27 Nov 2022 19:27:45 -0800 Subject: [PATCH 05/29] Move to JobSkillsController It makes more sense to pre-fetch all job skills and filter on the client, so we're consolidating into a JobSkillsController --- app/controllers/api/v1/job_skills_controller.rb | 13 +++++++++++++ app/controllers/api/v1/jobs_controller.rb | 7 ------- app/views/api/v1/job_skills/all.json.rabl | 3 +++ .../skills.json.rabl => job_skills/base.json.rabl} | 3 +-- config/routes.rb | 5 ++++- 5 files changed, 21 insertions(+), 10 deletions(-) create mode 100644 app/controllers/api/v1/job_skills_controller.rb create mode 100644 app/views/api/v1/job_skills/all.json.rabl rename app/views/api/v1/{jobs/skills.json.rabl => job_skills/base.json.rabl} (78%) diff --git a/app/controllers/api/v1/job_skills_controller.rb b/app/controllers/api/v1/job_skills_controller.rb new file mode 100644 index 0000000..24d519f --- /dev/null +++ b/app/controllers/api/v1/job_skills_controller.rb @@ -0,0 +1,13 @@ +class Api::V1::JobSkillsController < Api::V1::ApiController + def all + @skills = JobSkill.all() + render :all, status: :ok + end + + def job + job = Job.find(params[:id]) + + @skills = JobSkill.where(job: job).or(JobSkill.where(sub: true)) + render :all, status: :ok + end +end diff --git a/app/controllers/api/v1/jobs_controller.rb b/app/controllers/api/v1/jobs_controller.rb index 7084bce..c6fbabf 100644 --- a/app/controllers/api/v1/jobs_controller.rb +++ b/app/controllers/api/v1/jobs_controller.rb @@ -3,11 +3,4 @@ class Api::V1::JobsController < Api::V1::ApiController @jobs = Job.all() render :all, status: :ok end - - def skills - job = Job.find(params[:id]) - - @skills = JobSkill.where(job: job).or(JobSkill.where(sub: true)) - render :skills, status: :ok - end end diff --git a/app/views/api/v1/job_skills/all.json.rabl b/app/views/api/v1/job_skills/all.json.rabl new file mode 100644 index 0000000..ba30a1c --- /dev/null +++ b/app/views/api/v1/job_skills/all.json.rabl @@ -0,0 +1,3 @@ +collection @skills, object_root: false + +extends 'job_skills/base' diff --git a/app/views/api/v1/jobs/skills.json.rabl b/app/views/api/v1/job_skills/base.json.rabl similarity index 78% rename from app/views/api/v1/jobs/skills.json.rabl rename to app/views/api/v1/job_skills/base.json.rabl index d0592e1..03c6f1e 100644 --- a/app/views/api/v1/jobs/skills.json.rabl +++ b/app/views/api/v1/job_skills/base.json.rabl @@ -1,4 +1,4 @@ -collection @skills, object_root: false +object :job_skill attributes :id, :job, :slug, :color, :main, :base, :sub, :emp, :order @@ -8,4 +8,3 @@ node :name do |w| :ja => w.name_jp } end - diff --git a/config/routes.rb b/config/routes.rb index 526992c..44260c7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,7 +27,10 @@ Rails.application.routes.draw do post 'search/summons', to: 'search#summons' get 'jobs', to: 'jobs#all' - get 'jobs/:id/skills', to: 'jobs#skills' + + 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' From c0bdb47d43c57de2705d2df1647469f11c857450 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 30 Nov 2022 02:03:28 -0800 Subject: [PATCH 06/29] Add scoped search for job skills --- app/controllers/api/v1/api_controller.rb | 3 +- app/controllers/api/v1/search_controller.rb | 49 +++++++++++++++++++- app/errors/api/v1/NoJobProvidedError.rb | 22 +++++++++ app/models/job_skill.rb | 20 ++++++++ app/views/api/v1/search/job_skills.json.rabl | 11 +++++ config/routes.rb | 1 + 6 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 app/errors/api/v1/NoJobProvidedError.rb create mode 100644 app/views/api/v1/search/job_skills.json.rabl diff --git a/app/controllers/api/v1/api_controller.rb b/app/controllers/api/v1/api_controller.rb index 424b216..e9dcb95 100644 --- a/app/controllers/api/v1/api_controller.rb +++ b/app/controllers/api/v1/api_controller.rb @@ -11,6 +11,7 @@ module Api::V1 rescue_from ActiveRecord::RecordNotUnique, with: :render_unprocessable_entity_response rescue_from Api::V1::SameFavoriteUserError, with: :render_unprocessable_entity_response rescue_from Api::V1::FavoriteAlreadyExistsError, with: :render_unprocessable_entity_response + rescue_from Api::V1::NoJobProvidedError, with: :render_unprocessable_entity_response rescue_from Api::V1::UnauthorizedError, with: :render_unauthorized_response rescue_from ActionController::ParameterMissing, with: :render_unprocessable_entity_response @@ -63,4 +64,4 @@ module Api::V1 raise UnauthorizedError unless current_user end end -end \ No newline at end of file +end diff --git a/app/controllers/api/v1/search_controller.rb b/app/controllers/api/v1/search_controller.rb index f3230a7..1701b51 100644 --- a/app/controllers/api/v1/search_controller.rb +++ b/app/controllers/api/v1/search_controller.rb @@ -76,10 +76,57 @@ class Api::V1::SearchController < Api::V1::ApiController @summons = @summons.paginate(page: search_params[:page], per_page: 10) end + def job_skills + raise Api::V1::NoJobProvidedError unless search_params[:job].present? + + # Set up basic parameters we'll use + job = Job.find(search_params[:job]) + locale = search_params[:locale] || 'en' + group = search_params[:group].to_i unless !search_params[:group].present? + + # Set the conditions based on the group requested + conditions = {} + if (group) + if (group < 4) + conditions[:color] = group + conditions[:emp] = false + conditions[:base] = false + elsif (group == 4) + conditions[:emp] = true + elsif (group == 5) + conditions[:base] = true + end + end + + # Perform the query + if search_params[:query].present? && search_params[:query].length >= 2 + @skills = JobSkill.method("#{locale}_search").(search_params[:query]) + .where(conditions) + .where(job: job.id, main: false) + .or( + JobSkill.method("#{locale}_search").(search_params[:query]) + .where(conditions) + .where(sub: true) + ) + else + @skills = JobSkill.all + .where(conditions) + .where(job: job.id, main: false) + .or( + JobSkill.all + .where(conditions) + .where(sub:true) + ) + end + + @count = @skills.length + @skills = @skills.paginate(page: search_params[:page], per_page: 10) + end + private # Specify whitelisted properties that can be modified. def search_params params.require(:search).permit! end -end \ No newline at end of file +end diff --git a/app/errors/api/v1/NoJobProvidedError.rb b/app/errors/api/v1/NoJobProvidedError.rb new file mode 100644 index 0000000..6727873 --- /dev/null +++ b/app/errors/api/v1/NoJobProvidedError.rb @@ -0,0 +1,22 @@ +module Api::V1 + class NoJobProvidedError < StandardError + def http_status + 422 + end + + def code + "no_job_provided" + end + + def message + "A job ID must be provided to search for job skills" + end + + def to_hash + { + message: message, + code: code + } + end + end +end diff --git a/app/models/job_skill.rb b/app/models/job_skill.rb index 9705435..8e47fbe 100644 --- a/app/models/job_skill.rb +++ b/app/models/job_skill.rb @@ -1,6 +1,26 @@ class JobSkill < ApplicationRecord + include PgSearch::Model + belongs_to :job + pg_search_scope :en_search, + against: :name_en, + using: { + tsearch: { + prefix: true, + dictionary: "simple" + } + } + + pg_search_scope :jp_search, + against: :name_jp, + using: { + tsearch: { + prefix: true, + dictionary: "simple" + } + } + def display_resource(skill) skill.name_en end diff --git a/app/views/api/v1/search/job_skills.json.rabl b/app/views/api/v1/search/job_skills.json.rabl new file mode 100644 index 0000000..100cca8 --- /dev/null +++ b/app/views/api/v1/search/job_skills.json.rabl @@ -0,0 +1,11 @@ +node :count do + @count +end + +node :total_pages do + (@count.to_f / 10 > 1) ? (@count.to_f / 10).ceil() : 1 +end + +node(:results) { + partial('job_skills/base', object: @skills) +} unless @skills.empty? diff --git a/config/routes.rb b/config/routes.rb index 44260c7..d722ad4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -25,6 +25,7 @@ Rails.application.routes.draw do 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' From a69fda5ead6e5b07dc850d65f83706e4c07f20e0 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 30 Nov 2022 02:51:58 -0800 Subject: [PATCH 07/29] Small refactor of handling groups Using the `filters` key so we can hook into existing search infrastructure on the frontend --- app/controllers/api/v1/search_controller.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/search_controller.rb b/app/controllers/api/v1/search_controller.rb index 1701b51..dcf4013 100644 --- a/app/controllers/api/v1/search_controller.rb +++ b/app/controllers/api/v1/search_controller.rb @@ -82,11 +82,12 @@ class Api::V1::SearchController < Api::V1::ApiController # Set up basic parameters we'll use job = Job.find(search_params[:job]) locale = search_params[:locale] || 'en' - group = search_params[:group].to_i unless !search_params[:group].present? # Set the conditions based on the group requested conditions = {} - if (group) + if search_params[:filters].present? && search_params[:filters]["group"].present? + group = search_params[:filters]["group"].to_i + if (group < 4) conditions[:color] = group conditions[:emp] = false From ae36ff59b4a169bf1b992baadd5720507da33f29 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 30 Nov 2022 05:17:40 -0800 Subject: [PATCH 08/29] Properly gate group conditions The default group ("All Skills") is technically -1, not null --- app/controllers/api/v1/search_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/api/v1/search_controller.rb b/app/controllers/api/v1/search_controller.rb index dcf4013..f8b9f27 100644 --- a/app/controllers/api/v1/search_controller.rb +++ b/app/controllers/api/v1/search_controller.rb @@ -88,7 +88,7 @@ class Api::V1::SearchController < Api::V1::ApiController if search_params[:filters].present? && search_params[:filters]["group"].present? group = search_params[:filters]["group"].to_i - if (group < 4) + if (group >= 0 && group < 4) conditions[:color] = group conditions[:emp] = false conditions[:base] = false From 8e5571265ff62402a3662ac4073f5516010e8952 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 1 Dec 2022 04:46:24 -0800 Subject: [PATCH 09/29] Add job skills to database --- .../20221130155225_add_job_skills_to_party.rb | 9 +++++++++ db/migrate/20221201123645_add_skill0_to_party.rb | 7 +++++++ db/schema.rb | 14 +++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20221130155225_add_job_skills_to_party.rb create mode 100644 db/migrate/20221201123645_add_skill0_to_party.rb diff --git a/db/migrate/20221130155225_add_job_skills_to_party.rb b/db/migrate/20221130155225_add_job_skills_to_party.rb new file mode 100644 index 0000000..d993378 --- /dev/null +++ b/db/migrate/20221130155225_add_job_skills_to_party.rb @@ -0,0 +1,9 @@ +class AddJobSkillsToParty < ActiveRecord::Migration[6.1] + def change + change_table(:parties) do |t| + t.references :skill1, type: :uuid, foreign_key: { to_table: 'job_skills' } + t.references :skill2, type: :uuid, foreign_key: { to_table: 'job_skills' } + t.references :skill3, type: :uuid, foreign_key: { to_table: 'job_skills' } + end + end +end diff --git a/db/migrate/20221201123645_add_skill0_to_party.rb b/db/migrate/20221201123645_add_skill0_to_party.rb new file mode 100644 index 0000000..f5e66d4 --- /dev/null +++ b/db/migrate/20221201123645_add_skill0_to_party.rb @@ -0,0 +1,7 @@ +class AddSkill0ToParty < ActiveRecord::Migration[6.1] + def change + change_table(:parties) do |t| + t.references :skill0, type: :uuid, foreign_key: { to_table: "job_skills" } + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 748bd82..eed9131 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.define(version: 2022_11_20_145204) do +ActiveRecord::Schema.define(version: 2022_12_01_123645) do # These are extensions that must be enabled in order to support this database enable_extension "btree_gin" @@ -177,7 +177,15 @@ ActiveRecord::Schema.define(version: 2022_11_20_145204) do t.integer "weapons_count" t.uuid "job_id" t.integer "ml" + t.uuid "skill1_id" + t.uuid "skill2_id" + t.uuid "skill3_id" + t.uuid "skill0_id" t.index ["job_id"], name: "index_parties_on_job_id" + t.index ["skill0_id"], name: "index_parties_on_skill0_id" + t.index ["skill1_id"], name: "index_parties_on_skill1_id" + t.index ["skill2_id"], name: "index_parties_on_skill2_id" + t.index ["skill3_id"], name: "index_parties_on_skill3_id" t.index ["user_id"], name: "index_parties_on_user_id" end @@ -273,6 +281,10 @@ ActiveRecord::Schema.define(version: 2022_11_20_145204) do add_foreign_key "grid_weapons", "weapons" add_foreign_key "oauth_access_grants", "oauth_applications", column: "application_id" add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id" + add_foreign_key "parties", "job_skills", column: "skill0_id" + add_foreign_key "parties", "job_skills", column: "skill1_id" + add_foreign_key "parties", "job_skills", column: "skill2_id" + add_foreign_key "parties", "job_skills", column: "skill3_id" add_foreign_key "parties", "jobs" add_foreign_key "parties", "raids" add_foreign_key "parties", "users" From 1ba16a2c61d653d7fba34eb9dee887d6ae2eb5b8 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 1 Dec 2022 04:46:44 -0800 Subject: [PATCH 10/29] Export job skills in template --- app/models/party.rb | 69 ++++++++++++++++--------- app/views/api/v1/parties/base.json.rabl | 32 +++++++++--- 2 files changed, 69 insertions(+), 32 deletions(-) diff --git a/app/models/party.rb b/app/models/party.rb index 62db4e8..573470e 100644 --- a/app/models/party.rb +++ b/app/models/party.rb @@ -1,28 +1,49 @@ class Party < ApplicationRecord -##### ActiveRecord Associations - belongs_to :user, optional: true - belongs_to :raid, optional: true - belongs_to :job, optional: true - - has_many :characters, - foreign_key: "party_id", - class_name: "GridCharacter", - dependent: :destroy + ##### ActiveRecord Associations + belongs_to :user, optional: true + belongs_to :raid, optional: true + belongs_to :job, optional: true - has_many :weapons, - foreign_key: "party_id", - class_name: "GridWeapon", - dependent: :destroy + belongs_to :skill0, + foreign_key: "skill0_id", + class_name: "JobSkill", + optional: true - has_many :summons, - foreign_key: "party_id", - class_name: "GridSummon", - dependent: :destroy - has_many :favorites - - attr_accessor :favorited + belongs_to :skill1, + foreign_key: "skill1_id", + class_name: "JobSkill", + optional: true - def is_favorited(user) - user.favorite_parties.include? self - end -end \ No newline at end of file + belongs_to :skill2, + foreign_key: "skill2_id", + class_name: "JobSkill", + optional: true + + belongs_to :skill3, + foreign_key: "skill3_id", + class_name: "JobSkill", + optional: true + + has_many :characters, + foreign_key: "party_id", + class_name: "GridCharacter", + dependent: :destroy + + has_many :weapons, + foreign_key: "party_id", + class_name: "GridWeapon", + dependent: :destroy + + has_many :summons, + foreign_key: "party_id", + class_name: "GridSummon", + dependent: :destroy + + has_many :favorites + + attr_accessor :favorited + + def is_favorited(user) + user.favorite_parties.include? self + end +end diff --git a/app/views/api/v1/parties/base.json.rabl b/app/views/api/v1/parties/base.json.rabl index 42dfc00..364c711 100644 --- a/app/views/api/v1/parties/base.json.rabl +++ b/app/views/api/v1/parties/base.json.rabl @@ -1,31 +1,47 @@ object :party -attributes :id, :name, :description, :element, :favorited, :shortcode, :created_at, :updated_at +attributes :id, + :name, + :description, + :element, + :favorited, + :shortcode, + :created_at, + :updated_at node :extra do |p| - p.extra + p.extra end node :user do |p| - partial('users/base', :object => p.user) + partial("users/base", object: p.user) end node :raid do |p| - partial('raids/base', :object => p.raid) + partial("raids/base", object: p.raid) end node :job do |p| - partial('jobs/base', :object => p.job) + 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 node :characters do |p| - partial('grid_characters/base', :object => p.characters) + partial("grid_characters/base", object: p.characters) end node :weapons do |p| - partial('grid_weapons/base', :object => p.weapons) + partial("grid_weapons/base", object: p.weapons) end node :summons do |p| - partial('grid_summons/base', :object => p.summons) + partial("grid_summons/base", object: p.summons) end From 296fa0187c110fdabf2bb297deca0c83d6f8bd9c Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 1 Dec 2022 04:46:53 -0800 Subject: [PATCH 11/29] Save base job skills on job change --- app/controllers/api/v1/parties_controller.rb | 244 +++++++++++-------- 1 file changed, 141 insertions(+), 103 deletions(-) diff --git a/app/controllers/api/v1/parties_controller.rb b/app/controllers/api/v1/parties_controller.rb index c25c709..a3553c5 100644 --- a/app/controllers/api/v1/parties_controller.rb +++ b/app/controllers/api/v1/parties_controller.rb @@ -1,123 +1,161 @@ class Api::V1::PartiesController < Api::V1::ApiController - before_action :set_from_slug, except: ['create', 'destroy', 'update', 'index', 'favorites'] - before_action :set, only: ['update', 'destroy'] + before_action :set_from_slug, + except: %w[create destroy update index favorites] + before_action :set, only: %w[update destroy] - def create - @party = Party.new(shortcode: random_string) - @party.extra = party_params['extra'] - - if current_user - @party.user = current_user + def create + @party = Party.new(shortcode: random_string) + @party.extra = party_params["extra"] + + @party.user = current_user if current_user + + render :show, status: :created if @party.save! + end + + def show + render_not_found_response if @party.nil? + end + + def update + if @party.user != current_user + render_unauthorized_response + else + @party.attributes = party_params + + if party_params["job_id"].present? + job_skills = JobSkill.where(job_id: party_params["job_id"], main: true) + job_skills.each_with_index do |skill, i| + @party["skill#{i}_id"] = skill.id end + ap @party + end - render :show, status: :created if @party.save! + render :update, status: :ok if @party.save! end + end - def show - render_not_found_response if @party.nil? - end + def index + @per_page = 15 - def index - @per_page = 15 + now = DateTime.current + start_time = + ( + now - request.params["recency"].to_i.seconds + ).to_datetime.beginning_of_day unless request.params["recency"].blank? - now = DateTime.current - start_time = (now - request.params['recency'].to_i.seconds).to_datetime.beginning_of_day unless request.params['recency'].blank? + conditions = {} + conditions[:element] = request.params["element"] unless request.params[ + "element" + ].blank? + conditions[:raid] = request.params["raid"] unless request.params[ + "raid" + ].blank? + conditions[:created_at] = start_time..now unless request.params[ + "recency" + ].blank? + conditions[:weapons_count] = 5..13 - conditions = {} - conditions[:element] = request.params['element'] unless request.params['element'].blank? - conditions[:raid] = request.params['raid'] unless request.params['raid'].blank? - conditions[:created_at] = start_time..now unless request.params['recency'].blank? - conditions[:weapons_count] = 5..13 - - @parties = Party - .where(conditions) - .order(created_at: :desc) - .paginate(page: request.params[:page], per_page: @per_page) - .each { |party| - party.favorited = (current_user) ? party.is_favorited(current_user) : false - } - @count = Party.where(conditions).count - - render :all, status: :ok - end - - def favorites - raise Api::V1::UnauthorizedError unless current_user - - @per_page = 15 - - now = DateTime.current - start_time = (now - params['recency'].to_i.seconds).to_datetime.beginning_of_day unless request.params['recency'].blank? - - conditions = {} - conditions[:element] = request.params['element'] unless request.params['element'].blank? - conditions[:raid] = request.params['raid'] unless request.params['raid'].blank? - conditions[:created_at] = start_time..now unless request.params['recency'].blank? - conditions[:favorites] = { user_id: current_user.id } - - @parties = Party - .joins(:favorites) - .where(conditions) - .order('favorites.created_at DESC') - .paginate(page: request.params[:page], per_page: @per_page) - .each { |party| - party.favorited = party.is_favorited(current_user) - } - @count = Party.joins(:favorites).where(conditions).count - - render :all, status: :ok - end - - def update - if @party.user != current_user - render_unauthorized_response - else - @party.attributes = party_params - render :update, status: :ok if @party.save! + @parties = + Party + .where(conditions) + .order(created_at: :desc) + .paginate(page: request.params[:page], per_page: @per_page) + .each do |party| + party.favorited = + (current_user) ? party.is_favorited(current_user) : false end - end + @count = Party.where(conditions).count - def destroy - if @party.user != current_user - render_unauthorized_response - else - render :destroyed, status: :ok if @party.destroy - end - end + render :all, status: :ok + end - def weapons - render_not_found_response if @party.nil? - render :weapons, status: :ok - end + def favorites + raise Api::V1::UnauthorizedError unless current_user - def summons - render_not_found_response if @party.nil? - render :summons, status: :ok - end + @per_page = 15 - def characters - render_not_found_response if @party.nil? - render :characters, status: :ok - end + now = DateTime.current + start_time = + ( + now - params["recency"].to_i.seconds + ).to_datetime.beginning_of_day unless request.params["recency"].blank? - private + conditions = {} + conditions[:element] = request.params["element"] unless request.params[ + "element" + ].blank? + conditions[:raid] = request.params["raid"] unless request.params[ + "raid" + ].blank? + conditions[:created_at] = start_time..now unless request.params[ + "recency" + ].blank? + conditions[:favorites] = { user_id: current_user.id } - def random_string - numChars = 6 - o = [('a'..'z'), ('A'..'Z'), (0..9)].map(&:to_a).flatten - return (0...numChars).map { o[rand(o.length)] }.join - end + @parties = + Party + .joins(:favorites) + .where(conditions) + .order("favorites.created_at DESC") + .paginate(page: request.params[:page], per_page: @per_page) + .each { |party| party.favorited = party.is_favorited(current_user) } + @count = Party.joins(:favorites).where(conditions).count - def set_from_slug - @party = Party.where("shortcode = ?", params[:id]).first - @party.favorited = (current_user && @party) ? @party.is_favorited(current_user) : false - end + render :all, status: :ok + end - def set - @party = Party.where("id = ?", params[:id]).first + def destroy + if @party.user != current_user + render_unauthorized_response + else + render :destroyed, status: :ok if @party.destroy end + end - def party_params - params.require(:party).permit(:user_id, :extra, :name, :description, :raid_id, :job_id) - end -end \ No newline at end of file + def weapons + render_not_found_response if @party.nil? + render :weapons, status: :ok + end + + def summons + render_not_found_response if @party.nil? + render :summons, status: :ok + end + + def characters + render_not_found_response if @party.nil? + render :characters, status: :ok + end + + private + + def random_string + numChars = 6 + o = [("a".."z"), ("A".."Z"), (0..9)].map(&:to_a).flatten + return (0...numChars).map { o[rand(o.length)] }.join + end + + def set_from_slug + @party = Party.where("shortcode = ?", params[:id]).first + @party.favorited = + (current_user && @party) ? @party.is_favorited(current_user) : false + end + + def set + @party = Party.where("id = ?", params[:id]).first + end + + def party_params + params.require(:party).permit( + :user_id, + :extra, + :name, + :description, + :raid_id, + :job_id, + :skill1_id, + :skill2_id, + :skill3_id, + ) + end +end From 741d20aa70d0e1bae8c6b7363ddac6416af3aab2 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Thu, 1 Dec 2022 04:47:31 -0800 Subject: [PATCH 12/29] Send job and skills when party is updated --- app/views/api/v1/parties/update.json.rabl | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/app/views/api/v1/parties/update.json.rabl b/app/views/api/v1/parties/update.json.rabl index fafd7e0..6cb851f 100644 --- a/app/views/api/v1/parties/update.json.rabl +++ b/app/views/api/v1/parties/update.json.rabl @@ -3,5 +3,18 @@ object @party attributes :id, :user_id, :shortcode node :is_extra do |p| - p.extra -end \ No newline at end of file + 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 From aa5d2451a8bced8202036d29d1d17a62805d6a65 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Fri, 2 Dec 2022 11:20:11 -0800 Subject: [PATCH 13/29] Add skill uniqueness validation to Party --- app/models/party.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/app/models/party.rb b/app/models/party.rb index 573470e..8c6ef3d 100644 --- a/app/models/party.rb +++ b/app/models/party.rb @@ -41,9 +41,32 @@ class Party < ApplicationRecord has_many :favorites + ##### ActiveRecord Validations + validate :skills_are_unique + attr_accessor :favorited def is_favorited(user) user.favorite_parties.include? self end + + private + + def skills_are_unique + skills = [skill0, skill1, skill2, skill3].compact + + if skills.uniq.length != skills.length + errors.add(:skill1, "must be unique") if skill0 == skill1 + + if skill0 == skill2 || skill1 == skill2 + errors.add(:skill2, "must be unique") + end + + if skill0 == skill3 || skill1 == skill3 || skill2 == skill3 + errors.add(:skill3, "must be unique") + end + + errors.add(:job_skills, "must be unique") + end + end end From d1eba7aed881302e3a9b26c9a824c7e97988638a Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Fri, 2 Dec 2022 11:20:22 -0800 Subject: [PATCH 14/29] Add == and update search --- app/models/job_skill.rb | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/app/models/job_skill.rb b/app/models/job_skill.rb index 8e47fbe..e487edc 100644 --- a/app/models/job_skill.rb +++ b/app/models/job_skill.rb @@ -3,25 +3,29 @@ class JobSkill < ApplicationRecord belongs_to :job - pg_search_scope :en_search, - against: :name_en, - using: { - tsearch: { - prefix: true, - dictionary: "simple" - } - } + pg_search_scope :en_search, + against: :name_en, + using: { + tsearch: { + prefix: true, + dictionary: "simple", + }, + } - pg_search_scope :jp_search, - against: :name_jp, - using: { - tsearch: { - prefix: true, - dictionary: "simple" - } - } + pg_search_scope :jp_search, + against: :name_jp, + using: { + tsearch: { + prefix: true, + dictionary: "simple", + }, + } def display_resource(skill) skill.name_en end + + def ==(other) + self.class == other.class && @id == other.id + end end From 644ffd74e5773ddbd620b6ba269eaabfef7669eb Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Fri, 2 Dec 2022 11:20:43 -0800 Subject: [PATCH 15/29] Added logic for swapping and replacing job skills --- app/controllers/api/v1/parties_controller.rb | 70 ++++++++++++++++++-- 1 file changed, 64 insertions(+), 6 deletions(-) diff --git a/app/controllers/api/v1/parties_controller.rb b/app/controllers/api/v1/parties_controller.rb index a3553c5..53f2ab3 100644 --- a/app/controllers/api/v1/parties_controller.rb +++ b/app/controllers/api/v1/parties_controller.rb @@ -20,14 +20,32 @@ class Api::V1::PartiesController < Api::V1::ApiController if @party.user != current_user render_unauthorized_response else - @party.attributes = party_params + @party.attributes = + party_params.except(:skill1_id, :skill2_id, :skill3_id) - if party_params["job_id"].present? - job_skills = JobSkill.where(job_id: party_params["job_id"], main: true) - job_skills.each_with_index do |skill, i| - @party["skill#{i}_id"] = skill.id + # Determine which incoming keys contain new skills + new_skill_keys = + party_params.keys - %i[skill0_id skill1_id skill2_id skill3_id] + + if !new_skill_keys.empty? + # If there are new skills, merge them with the existing skills + 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 - ap @party + + @party.attributes = new_skill_ids end render :update, status: :ok if @party.save! @@ -129,6 +147,46 @@ 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 + + return 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 + # Check desired position for a skill + displaced_skill = existing_skills[position] if existing_skills[ + position + ].present? + + # Put skill in new position + existing_skills[position] = skill + existing_skills[old_position] = displaced_skill + else + existing_skills[position] = skill + end + + return existing_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 - 1 } + end + def random_string numChars = 6 o = [("a".."z"), ("A".."Z"), (0..9)].map(&:to_a).flatten From f6613d6e910b32a3efde3424965a86a5ed9c02d2 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Fri, 2 Dec 2022 20:45:46 -0800 Subject: [PATCH 16/29] Add TooManySkillsOfTypeError --- app/controllers/api/v1/api_controller.rb | 1 + app/errors/api/v1/TooManySkillsOfTypeError.rb | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 app/errors/api/v1/TooManySkillsOfTypeError.rb diff --git a/app/controllers/api/v1/api_controller.rb b/app/controllers/api/v1/api_controller.rb index e9dcb95..a019f99 100644 --- a/app/controllers/api/v1/api_controller.rb +++ b/app/controllers/api/v1/api_controller.rb @@ -12,6 +12,7 @@ module Api::V1 rescue_from Api::V1::SameFavoriteUserError, with: :render_unprocessable_entity_response rescue_from Api::V1::FavoriteAlreadyExistsError, with: :render_unprocessable_entity_response rescue_from Api::V1::NoJobProvidedError, with: :render_unprocessable_entity_response + rescue_from Api::V1::TooManySkillsOfTypeError, with: :render_unprocessable_entity_response rescue_from Api::V1::UnauthorizedError, with: :render_unauthorized_response rescue_from ActionController::ParameterMissing, with: :render_unprocessable_entity_response diff --git a/app/errors/api/v1/TooManySkillsOfTypeError.rb b/app/errors/api/v1/TooManySkillsOfTypeError.rb new file mode 100644 index 0000000..873a452 --- /dev/null +++ b/app/errors/api/v1/TooManySkillsOfTypeError.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Api + module V1 + class TooManySkillsOfTypeError < StandardError + def initialize(data) + @data = data + end + + def http_status + 422 + end + + def code + 'too_many_skills_of_type' + end + + def message + 'You can only have up to 2 skills of type' + end + + def to_hash + { + message: message, + code: code, + idea_id: @data[:skill_type] + } + end + end + end +end From 5c9a2b8d7aa7f28c47a8e118b36372864274032f Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Fri, 2 Dec 2022 20:46:51 -0800 Subject: [PATCH 17/29] Refactor job skill assignment a bit --- app/controllers/api/v1/parties_controller.rb | 94 ++++++++++++-------- 1 file changed, 57 insertions(+), 37 deletions(-) diff --git a/app/controllers/api/v1/parties_controller.rb b/app/controllers/api/v1/parties_controller.rb index 53f2ab3..cdd67c2 100644 --- a/app/controllers/api/v1/parties_controller.rb +++ b/app/controllers/api/v1/parties_controller.rb @@ -20,36 +20,32 @@ class Api::V1::PartiesController < Api::V1::ApiController if @party.user != current_user render_unauthorized_response else - @party.attributes = - party_params.except(:skill1_id, :skill2_id, :skill3_id) - + @party.attributes = party_params.except(:skill1_id, :skill2_id, :skill3_id) + ap party_params # Determine which incoming keys contain new skills - new_skill_keys = - party_params.keys - %i[skill0_id skill1_id skill2_id skill3_id] + 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 !new_skill_keys.empty? # If there are new skills, merge them with the existing skills - 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) + 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_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 + 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 - - @party.attributes = new_skill_ids end - - render :update, status: :ok if @party.save! end + + render :update, status: :ok if @party.save! end def index @@ -79,9 +75,9 @@ class Api::V1::PartiesController < Api::V1::ApiController .order(created_at: :desc) .paginate(page: request.params[:page], per_page: @per_page) .each do |party| - party.favorited = - (current_user) ? party.is_favorited(current_user) : false - end + party.favorited = + current_user ? party.is_favorited(current_user) : false + end @count = Party.where(conditions).count render :all, status: :ok @@ -160,26 +156,49 @@ class Api::V1::PartiesController < Api::V1::ApiController progress = place_skill_in_existing_skills(progress, skill, positions[0]) end - return progress + 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 - # Check desired position for a skill - displaced_skill = existing_skills[position] if existing_skills[ - position - ].present? - - # Put skill in new position - existing_skills[position] = skill - existing_skills[old_position] = displaced_skill + 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 - return existing_skills + 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) @@ -196,7 +215,7 @@ class Api::V1::PartiesController < Api::V1::ApiController def set_from_slug @party = Party.where("shortcode = ?", params[:id]).first @party.favorited = - (current_user && @party) ? @party.is_favorited(current_user) : false + current_user && @party ? @party.is_favorited(current_user) : false end def set @@ -211,9 +230,10 @@ class Api::V1::PartiesController < Api::V1::ApiController :description, :raid_id, :job_id, + :skill0_id, :skill1_id, :skill2_id, - :skill3_id, + :skill3_id ) end end From b1c69301214cabbc27ed1f76a9f1616666a1c702 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 3 Dec 2022 11:43:27 -0800 Subject: [PATCH 18/29] Add base_job so we can track base skills --- db/migrate/20221203112452_add_base_job_to_jobs.rb | 7 +++++++ db/schema.rb | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20221203112452_add_base_job_to_jobs.rb diff --git a/db/migrate/20221203112452_add_base_job_to_jobs.rb b/db/migrate/20221203112452_add_base_job_to_jobs.rb new file mode 100644 index 0000000..e689758 --- /dev/null +++ b/db/migrate/20221203112452_add_base_job_to_jobs.rb @@ -0,0 +1,7 @@ +class AddBaseJobToJobs < ActiveRecord::Migration[6.1] + def change + change_table(:jobs) do |t| + t.references :base_job, type: :uuid, foreign_key: { to_table: 'jobs' } + end + end +end diff --git a/db/schema.rb b/db/schema.rb index eed9131..52d1af4 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.define(version: 2022_12_01_123645) do +ActiveRecord::Schema.define(version: 2022_12_03_112452) do # These are extensions that must be enabled in order to support this database enable_extension "btree_gin" @@ -124,6 +124,8 @@ ActiveRecord::Schema.define(version: 2022_12_01_123645) do t.string "row" t.boolean "ml", default: false t.integer "order" + t.uuid "base_job_id" + t.index ["base_job_id"], name: "index_jobs_on_base_job_id" end create_table "oauth_access_grants", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| @@ -279,6 +281,7 @@ ActiveRecord::Schema.define(version: 2022_12_01_123645) do add_foreign_key "grid_weapons", "parties" add_foreign_key "grid_weapons", "weapon_keys", column: "weapon_key3_id" add_foreign_key "grid_weapons", "weapons" + add_foreign_key "jobs", "jobs", column: "base_job_id" add_foreign_key "oauth_access_grants", "oauth_applications", column: "application_id" add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id" add_foreign_key "parties", "job_skills", column: "skill0_id" From 4ee90f6c099738e275a8e9e8b41b63f3bac27380 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 3 Dec 2022 11:43:56 -0800 Subject: [PATCH 19/29] Add new errors and fix handling in ApiController --- app/controllers/api/v1/api_controller.rb | 118 ++++++++++-------- app/errors/api/v1/IncompatibleSkillError.rb | 29 +++++ app/errors/api/v1/NoJobProvidedError.rb | 38 +++--- app/errors/api/v1/NoJobSkillProvidedError.rb | 22 ++++ app/errors/api/v1/TooManySkillsOfTypeError.rb | 2 +- 5 files changed, 134 insertions(+), 75 deletions(-) create mode 100644 app/errors/api/v1/IncompatibleSkillError.rb create mode 100644 app/errors/api/v1/NoJobSkillProvidedError.rb diff --git a/app/controllers/api/v1/api_controller.rb b/app/controllers/api/v1/api_controller.rb index a019f99..1460919 100644 --- a/app/controllers/api/v1/api_controller.rb +++ b/app/controllers/api/v1/api_controller.rb @@ -1,68 +1,76 @@ module Api::V1 - class ApiController < ActionController::API + class ApiController < ActionController::API ##### Doorkeeper - include Doorkeeper::Rails::Helpers + include Doorkeeper::Rails::Helpers ##### Errors - rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable_entity_response - rescue_from ActiveRecord::RecordNotDestroyed, with: :render_unprocessable_entity_response - rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_response - rescue_from ActiveRecord::RecordNotSaved, with: :render_unprocessable_entity_response - rescue_from ActiveRecord::RecordNotUnique, with: :render_unprocessable_entity_response - rescue_from Api::V1::SameFavoriteUserError, with: :render_unprocessable_entity_response - rescue_from Api::V1::FavoriteAlreadyExistsError, with: :render_unprocessable_entity_response - rescue_from Api::V1::NoJobProvidedError, with: :render_unprocessable_entity_response - rescue_from Api::V1::TooManySkillsOfTypeError, with: :render_unprocessable_entity_response - rescue_from Api::V1::UnauthorizedError, with: :render_unauthorized_response - rescue_from ActionController::ParameterMissing, with: :render_unprocessable_entity_response + rescue_from ActiveRecord::RecordInvalid, with: :render_unprocessable_entity_response + rescue_from ActiveRecord::RecordNotDestroyed, with: :render_unprocessable_entity_response + rescue_from ActiveRecord::RecordNotFound, with: :render_not_found_response + rescue_from ActiveRecord::RecordNotSaved, with: :render_unprocessable_entity_response + rescue_from ActiveRecord::RecordNotUnique, with: :render_unprocessable_entity_response + rescue_from Api::V1::SameFavoriteUserError, with: :render_unprocessable_entity_response + rescue_from Api::V1::FavoriteAlreadyExistsError, with: :render_unprocessable_entity_response + rescue_from Api::V1::NoJobProvidedError, with: :render_unprocessable_entity_response + rescue_from Api::V1::TooManySkillsOfTypeError, with: :render_unprocessable_entity_response + rescue_from Api::V1::UnauthorizedError, with: :render_unauthorized_response + rescue_from ActionController::ParameterMissing, with: :render_unprocessable_entity_response + + rescue_from StandardError do |e| + render_error(e) + end ##### Hooks - before_action :current_user - before_action :set_default_content_type + before_action :current_user + before_action :set_default_content_type ##### Responders - respond_to :json + respond_to :json ##### Methods - # Assign the current user if the Doorkeeper token isn't nil, then - # update the current user's last seen datetime and last IP address - # before returning - def current_user - @current_user ||= User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token + # Assign the current user if the Doorkeeper token isn't nil, then + # update the current user's last seen datetime and last IP address + # before returning + def current_user + @current_user ||= User.find(doorkeeper_token.resource_owner_id) if doorkeeper_token - return @current_user - end - - # Set the response content-type - def set_content_type(content_type) - response.headers["Content-Type"] = content_type - end - - # Set the default response content-type to application/javascript - # with a UTF-8 charset - def set_default_content_type - set_content_type("application/javascript; charset=utf-8") - end - - ### Error response methods - def render_unprocessable_entity_response(exception) - @exception = exception - render action: 'errors', status: :unprocessable_entity - end - - def render_not_found_response - response = { errors: [{ message: "Record could not be found.", code: "not_found" }]} - render 'not_found', status: :not_found - end - - def render_unauthorized_response - render action: 'errors', status: :unauthorized - end - - private - - def restrict_access - raise UnauthorizedError unless current_user - end + return @current_user end + + # Set the response content-type + def set_content_type(content_type) + response.headers["Content-Type"] = content_type + end + + # Set the default response content-type to application/javascript + # with a UTF-8 charset + def set_default_content_type + set_content_type("application/javascript; charset=utf-8") + end + + ### Error response methods + def render_error(error) + render action: 'errors', json: error.to_hash, status: error.http_status + end + + def render_unprocessable_entity_response(exception) + @exception = exception + render action: 'errors', status: :unprocessable_entity + end + + def render_not_found_response + response = { errors: [{ message: "Record could not be found.", code: "not_found" }] } + render 'not_found', status: :not_found + end + + def render_unauthorized_response + render action: 'errors', status: :unauthorized + end + + private + + def restrict_access + raise UnauthorizedError unless current_user + end + end end diff --git a/app/errors/api/v1/IncompatibleSkillError.rb b/app/errors/api/v1/IncompatibleSkillError.rb new file mode 100644 index 0000000..bd018d2 --- /dev/null +++ b/app/errors/api/v1/IncompatibleSkillError.rb @@ -0,0 +1,29 @@ +module Api::V1 + class IncompatibleSkillError < StandardError + def initialize(data) + @data = data + end + + def http_status + 422 + end + + def code + 'incompatible_skill' + end + + def message + 'The selected skill cannot be added to the current job' + end + + def to_hash + ap @data + { + message: message, + code: code, + job: @data[:job], + skill: @data[:skill] + } + end + end +end diff --git a/app/errors/api/v1/NoJobProvidedError.rb b/app/errors/api/v1/NoJobProvidedError.rb index 6727873..1e94592 100644 --- a/app/errors/api/v1/NoJobProvidedError.rb +++ b/app/errors/api/v1/NoJobProvidedError.rb @@ -1,22 +1,22 @@ module Api::V1 - class NoJobProvidedError < StandardError - def http_status - 422 - end - - def code - "no_job_provided" - end - - def message - "A job ID must be provided to search for job skills" - end - - def to_hash - { - message: message, - code: code - } - end + class NoJobProvidedError < StandardError + def http_status + 422 end + + def code + "no_job_provided" + end + + def message + "A job ID must be provided" + end + + def to_hash + { + message: message, + code: code + } + end + end end diff --git a/app/errors/api/v1/NoJobSkillProvidedError.rb b/app/errors/api/v1/NoJobSkillProvidedError.rb new file mode 100644 index 0000000..1e66e2d --- /dev/null +++ b/app/errors/api/v1/NoJobSkillProvidedError.rb @@ -0,0 +1,22 @@ +module Api::V1 + class NoJobSkillProvidedError < StandardError + def http_status + 422 + end + + def code + "no_job_skill_provided" + end + + def message + "A job skill ID must be provided" + end + + def to_hash + { + message: message, + code: code + } + end + end +end diff --git a/app/errors/api/v1/TooManySkillsOfTypeError.rb b/app/errors/api/v1/TooManySkillsOfTypeError.rb index 873a452..2323192 100644 --- a/app/errors/api/v1/TooManySkillsOfTypeError.rb +++ b/app/errors/api/v1/TooManySkillsOfTypeError.rb @@ -23,7 +23,7 @@ module Api { message: message, code: code, - idea_id: @data[:skill_type] + skill_type: @data[:skill_type] } end end From bd33ee44fc062f0cc4753c53af510532b2d579e1 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 3 Dec 2022 11:44:09 -0800 Subject: [PATCH 20/29] Update Job model to reference base_job --- app/models/job.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/models/job.rb b/app/models/job.rb index 8c1867f..bded82e 100644 --- a/app/models/job.rb +++ b/app/models/job.rb @@ -1,7 +1,12 @@ class Job < ApplicationRecord belongs_to :party - + + belongs_to :base_job, + foreign_key: 'base_job_id', + class_name: 'Job', + optional: true + def display_resource(job) - job.name_en + job.name_en end end From ec3b42101b10e1d310e9b55fa23391c4b068055c Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 3 Dec 2022 11:45:00 -0800 Subject: [PATCH 21/29] 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 From b93d0dd366dd2fb1f10798dd3b2fe0236ac488ae Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 3 Dec 2022 13:45:34 -0800 Subject: [PATCH 22/29] Update routes.rb --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index ac6b33a..f7f94e2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -19,7 +19,7 @@ Rails.application.routes.draw do 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/jobs', to: 'jobs#update_job' put 'parties/:id/job_skills', to: 'jobs#update_job_skills' post 'check/email', to: 'users#check_email' From 27d31d61c072bf03a052a0e6a9da0724561185da Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 3 Dec 2022 13:55:41 -0800 Subject: [PATCH 23/29] Fix race condition --- app/controllers/api/v1/jobs_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/jobs_controller.rb b/app/controllers/api/v1/jobs_controller.rb index 3191380..5866d32 100644 --- a/app/controllers/api/v1/jobs_controller.rb +++ b/app/controllers/api/v1/jobs_controller.rb @@ -11,7 +11,6 @@ class Api::V1::JobsController < Api::V1::ApiController # 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 @@ -22,7 +21,8 @@ class Api::V1::JobsController < Api::V1::ApiController # 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])) + ap "In here with #{key}" + @party[key] = nil if @party[key] && mismatched_skill(@party.job, JobSkill.find(@party[key])) end render :update, status: :ok if @party.save! From 243ba2de3eefddecc88e70d20dfe196358892280 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 3 Dec 2022 14:10:18 -0800 Subject: [PATCH 24/29] Fix bug when desired position is empty --- app/controllers/api/v1/jobs_controller.rb | 32 ++++++++++++----------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/app/controllers/api/v1/jobs_controller.rb b/app/controllers/api/v1/jobs_controller.rb index 5866d32..1417763 100644 --- a/app/controllers/api/v1/jobs_controller.rb +++ b/app/controllers/api/v1/jobs_controller.rb @@ -42,12 +42,11 @@ class Api::V1::JobsController < Api::V1::ApiController 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 + raise Api::V1::IncompatibleSkillError.new(job: @party.job, skill: skill) if mismatched_skill(@party.job, skill) end positions = extract_positions_from_keys(new_skill_keys) @@ -80,19 +79,22 @@ class Api::V1::JobsController < Api::V1::ApiController 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 - + if !existing_skills[position] existing_skills[position] = skill + else + 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 end existing_skills From 813c2ba20259c6ce8e78bda9dd6c16b1a03503b3 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 3 Dec 2022 14:51:01 -0800 Subject: [PATCH 25/29] Fix bug regarding finding old_position --- app/controllers/api/v1/jobs_controller.rb | 6 ++++-- app/models/job_skill.rb | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/controllers/api/v1/jobs_controller.rb b/app/controllers/api/v1/jobs_controller.rb index 1417763..a2f8042 100644 --- a/app/controllers/api/v1/jobs_controller.rb +++ b/app/controllers/api/v1/jobs_controller.rb @@ -82,9 +82,11 @@ class Api::V1::JobsController < Api::V1::ApiController if !existing_skills[position] existing_skills[position] = skill else - old_position = existing_skills.key(existing_skills.detect { |_, value| value.id == skill.id }) + value = existing_skills.detect { |_, value| value.id == skill.id } + old_position = existing_skills.key(value[1]) if value + if old_position - existing_skills = swap_skills_at_position(existing_skills, skill, position, old_position[0]) + 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' diff --git a/app/models/job_skill.rb b/app/models/job_skill.rb index e487edc..3ab1d84 100644 --- a/app/models/job_skill.rb +++ b/app/models/job_skill.rb @@ -1,4 +1,6 @@ class JobSkill < ApplicationRecord + alias eql? == + include PgSearch::Model belongs_to :job @@ -25,7 +27,7 @@ class JobSkill < ApplicationRecord skill.name_en end - def ==(other) - self.class == other.class && @id == other.id + def ==(o) + self.class == o.class && id == o.id end end From 6ba5335668a524ae2fd1c00fabe4a9aeb452a75e Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 3 Dec 2022 16:39:38 -0800 Subject: [PATCH 26/29] Fix last bugs around adding skills with 0~3 existing --- app/controllers/api/v1/jobs_controller.rb | 38 +++++++++++++---------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/app/controllers/api/v1/jobs_controller.rb b/app/controllers/api/v1/jobs_controller.rb index a2f8042..2c0a624 100644 --- a/app/controllers/api/v1/jobs_controller.rb +++ b/app/controllers/api/v1/jobs_controller.rb @@ -21,7 +21,6 @@ class Api::V1::JobsController < Api::V1::ApiController # Check for incompatible Base and EMP skills %w[skill1_id skill2_id skill3_id].each do |key| - ap "In here with #{key}" @party[key] = nil if @party[key] && mismatched_skill(@party.job, JobSkill.find(@party[key])) end @@ -53,7 +52,7 @@ class Api::V1::JobsController < Api::V1::ApiController 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 + memo["skill#{index}_id"] = skill.id if skill end @party.attributes = new_skill_ids @@ -79,22 +78,22 @@ class Api::V1::JobsController < Api::V1::ApiController end def place_skill_in_existing_skills(existing_skills, skill, position) + # 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 + if !existing_skills[position] existing_skills[position] = skill else - value = existing_skills.detect { |_, value| value.id == skill.id } + value = existing_skills.compact.detect { |_, value| value && value.id == skill.id } old_position = existing_skills.key(value[1]) if value 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 end @@ -119,21 +118,26 @@ class Api::V1::JobsController < Api::V1::ApiController end def can_add_skill_of_type(skills, position, type) - max_skill_of_type = 2 - skills_to_check = skills.reject { |key, _| key == position } + if skills.values.compact.length.positive? + max_skill_of_type = 2 + skills_to_check = skills.compact.reject { |key, _| key == position } + + sum = skills_to_check.values.count { |value| value.send(type) } - sum = skills_to_check.values.count { |value| value.send(type) } - - sum + 1 <= max_skill_of_type + sum + 1 <= max_skill_of_type + else + true + end end def mismatched_skill(job, skill) + mismatched_main = (skill.job.id != job.id) && skill.main && !skill.sub 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 if mismatched_emp || mismatched_base || mismatched_main + elsif mismatched_emp || mismatched_main true else false From a08421722f60103805f4b81bd20e1ef3a2bc5dcd Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 3 Dec 2022 18:21:01 -0800 Subject: [PATCH 27/29] Add GranblueError and move app errors to extend it --- app/controllers/api/v1/api_controller.rb | 8 +++- .../api/v1/FavoriteAlreadyExistsError.rb | 38 +++++++++---------- app/errors/api/v1/GranblueError.rb | 31 +++++++++++++++ app/errors/api/v1/IncompatibleSkillError.rb | 4 +- app/errors/api/v1/NoJobProvidedError.rb | 2 +- app/errors/api/v1/NoJobSkillProvidedError.rb | 2 +- app/errors/api/v1/SameFavoriteUserError.rb | 34 ++++++++--------- app/errors/api/v1/TooManySkillsOfTypeError.rb | 10 +---- 8 files changed, 77 insertions(+), 52 deletions(-) create mode 100644 app/errors/api/v1/GranblueError.rb diff --git a/app/controllers/api/v1/api_controller.rb b/app/controllers/api/v1/api_controller.rb index 1460919..79e13a9 100644 --- a/app/controllers/api/v1/api_controller.rb +++ b/app/controllers/api/v1/api_controller.rb @@ -16,7 +16,7 @@ module Api::V1 rescue_from Api::V1::UnauthorizedError, with: :render_unauthorized_response rescue_from ActionController::ParameterMissing, with: :render_unprocessable_entity_response - rescue_from StandardError do |e| + rescue_from GranblueError do |e| render_error(e) end @@ -50,7 +50,11 @@ module Api::V1 ### Error response methods def render_error(error) - render action: 'errors', json: error.to_hash, status: error.http_status + if error + render action: 'errors', json: error.to_hash, status: error.http_status + else + render action: 'errors' + end end def render_unprocessable_entity_response(exception) diff --git a/app/errors/api/v1/FavoriteAlreadyExistsError.rb b/app/errors/api/v1/FavoriteAlreadyExistsError.rb index 8c7ca73..e0ac7b7 100644 --- a/app/errors/api/v1/FavoriteAlreadyExistsError.rb +++ b/app/errors/api/v1/FavoriteAlreadyExistsError.rb @@ -1,22 +1,22 @@ module Api::V1 - class FavoriteAlreadyExistsError < StandardError - def http_status - 422 - end - - def code - "favorite_already_exists" - end - - def message - "This user has favorited this party already" - end - - def to_hash - { - message: message, - code: code - } - end + class FavoriteAlreadyExistsError < GranblueError + def http_status + 422 end + + def code + "favorite_already_exists" + end + + def message + "This user has favorited this party already" + end + + def to_hash + { + message: message, + code: code + } + end + end end diff --git a/app/errors/api/v1/GranblueError.rb b/app/errors/api/v1/GranblueError.rb new file mode 100644 index 0000000..dec43a1 --- /dev/null +++ b/app/errors/api/v1/GranblueError.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Api + module V1 + # This is the base error that we inherit from for application errors + class GranblueError < StandardError + def initialize(data) + @data = data + end + + def http_status + 422 + end + + def code + 'granblue_error' + end + + def message + 'Something went wrong' + end + + def to_hash + { + message: message, + code: code + } + end + end + end +end diff --git a/app/errors/api/v1/IncompatibleSkillError.rb b/app/errors/api/v1/IncompatibleSkillError.rb index bd018d2..bd8c808 100644 --- a/app/errors/api/v1/IncompatibleSkillError.rb +++ b/app/errors/api/v1/IncompatibleSkillError.rb @@ -1,9 +1,9 @@ module Api::V1 - class IncompatibleSkillError < StandardError + class IncompatibleSkillError < GranblueError def initialize(data) @data = data end - + def http_status 422 end diff --git a/app/errors/api/v1/NoJobProvidedError.rb b/app/errors/api/v1/NoJobProvidedError.rb index 1e94592..c27f48a 100644 --- a/app/errors/api/v1/NoJobProvidedError.rb +++ b/app/errors/api/v1/NoJobProvidedError.rb @@ -1,5 +1,5 @@ module Api::V1 - class NoJobProvidedError < StandardError + class NoJobProvidedError < GranblueError def http_status 422 end diff --git a/app/errors/api/v1/NoJobSkillProvidedError.rb b/app/errors/api/v1/NoJobSkillProvidedError.rb index 1e66e2d..60ad888 100644 --- a/app/errors/api/v1/NoJobSkillProvidedError.rb +++ b/app/errors/api/v1/NoJobSkillProvidedError.rb @@ -1,5 +1,5 @@ module Api::V1 - class NoJobSkillProvidedError < StandardError + class NoJobSkillProvidedError < GranblueError def http_status 422 end diff --git a/app/errors/api/v1/SameFavoriteUserError.rb b/app/errors/api/v1/SameFavoriteUserError.rb index b948c1a..6fbcc79 100644 --- a/app/errors/api/v1/SameFavoriteUserError.rb +++ b/app/errors/api/v1/SameFavoriteUserError.rb @@ -1,22 +1,20 @@ -module Api::V1 - class SameFavoriteUserError < StandardError - def http_status - 422 - end +module Api + module V1 + class SameFavoriteUserError < GranblueError + def code + 'same_favorite_user' + end - def code - "same_favorite_user" - end + def message + 'Users cannot favorite their own parties' + end - def message - "Users cannot favorite their own parties" - end - - def to_hash - { - message: message, - code: code - } - end + def to_hash + { + message: message, + code: code + } + end end + end end diff --git a/app/errors/api/v1/TooManySkillsOfTypeError.rb b/app/errors/api/v1/TooManySkillsOfTypeError.rb index 2323192..3d0b4ab 100644 --- a/app/errors/api/v1/TooManySkillsOfTypeError.rb +++ b/app/errors/api/v1/TooManySkillsOfTypeError.rb @@ -2,15 +2,7 @@ module Api module V1 - class TooManySkillsOfTypeError < StandardError - def initialize(data) - @data = data - end - - def http_status - 422 - end - + class TooManySkillsOfTypeError < GranblueError def code 'too_many_skills_of_type' end From fc7cde4c5a6502ab711bfe3ebc7d9f54094f4753 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 3 Dec 2022 18:21:15 -0800 Subject: [PATCH 28/29] Fix race condition for base skill mismatch check --- app/controllers/api/v1/jobs_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/jobs_controller.rb b/app/controllers/api/v1/jobs_controller.rb index 2c0a624..74d5a9a 100644 --- a/app/controllers/api/v1/jobs_controller.rb +++ b/app/controllers/api/v1/jobs_controller.rb @@ -121,7 +121,7 @@ class Api::V1::JobsController < Api::V1::ApiController if skills.values.compact.length.positive? max_skill_of_type = 2 skills_to_check = skills.compact.reject { |key, _| key == position } - + sum = skills_to_check.values.count { |value| value.send(type) } sum + 1 <= max_skill_of_type @@ -133,7 +133,7 @@ class Api::V1::JobsController < Api::V1::ApiController def mismatched_skill(job, skill) mismatched_main = (skill.job.id != job.id) && skill.main && !skill.sub 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 + mismatched_base = skill.job.base_job && (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 || mismatched_main From a8301019f56e9dce77bdce0a99f27dc1c90c0335 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 3 Dec 2022 18:34:38 -0800 Subject: [PATCH 29/29] Fix search for base skills Before they didn't really show up at all, then they showed up on everything, then they showed up on EX1 and EX2, now they only show up on EX2. --- app/controllers/api/v1/search_controller.rb | 226 ++++++++++---------- 1 file changed, 116 insertions(+), 110 deletions(-) diff --git a/app/controllers/api/v1/search_controller.rb b/app/controllers/api/v1/search_controller.rb index f8b9f27..9f42fc6 100644 --- a/app/controllers/api/v1/search_controller.rb +++ b/app/controllers/api/v1/search_controller.rb @@ -1,133 +1,139 @@ class Api::V1::SearchController < Api::V1::ApiController - def characters - filters = search_params[:filters] - locale = search_params[:locale] || 'en' - conditions = {} + def characters + filters = search_params[:filters] + locale = search_params[:locale] || 'en' + conditions = {} - if filters - conditions[:rarity] = filters['rarity'] unless filters['rarity'].blank? || filters['rarity'].empty? - conditions[:element] = filters['element'] unless filters['element'].blank? || filters['element'].empty? - conditions[:proficiency1] = filters['proficiency1'] unless filters['proficiency1'].blank? || filters['proficiency1'].empty? - conditions[:proficiency2] = filters['proficiency2'] unless filters['proficiency2'].blank? || filters['proficiency2'].empty? - # conditions[:series] = filters['series'] unless filters['series'].blank? || filters['series'].empty? - end - - if search_params[:query].present? && search_params[:query].length >= 2 - if locale == 'ja' - @characters = Character.jp_search(search_params[:query]).where(conditions) - else - @characters = Character.en_search(search_params[:query]).where(conditions) - end - else - @characters = Character.where(conditions) - end - - @count = @characters.length - @characters = @characters.paginate(page: search_params[:page], per_page: 10) + if filters + conditions[:rarity] = filters['rarity'] unless filters['rarity'].blank? || filters['rarity'].empty? + conditions[:element] = filters['element'] unless filters['element'].blank? || filters['element'].empty? + conditions[:proficiency1] = filters['proficiency1'] unless filters['proficiency1'].blank? || filters['proficiency1'].empty? + conditions[:proficiency2] = filters['proficiency2'] unless filters['proficiency2'].blank? || filters['proficiency2'].empty? + # conditions[:series] = filters['series'] unless filters['series'].blank? || filters['series'].empty? end - def weapons - filters = search_params[:filters] - locale = search_params[:locale] || 'en' - conditions = {} + @characters = if search_params[:query].present? && search_params[:query].length >= 2 + if locale == 'ja' + Character.jp_search(search_params[:query]).where(conditions) + else + Character.en_search(search_params[:query]).where(conditions) + end + else + Character.where(conditions) + end - if filters - conditions[:rarity] = filters['rarity'] unless filters['rarity'].blank? || filters['rarity'].empty? - conditions[:element] = filters['element'] unless filters['element'].blank? || filters['element'].empty? - conditions[:proficiency] = filters['proficiency1'] unless filters['proficiency1'].blank? || filters['proficiency1'].empty? - conditions[:series] = filters['series'] unless filters['series'].blank? || filters['series'].empty? - end + @count = @characters.length + @characters = @characters.paginate(page: search_params[:page], per_page: 10) + end - if search_params[:query].present? && search_params[:query].length >= 2 - if locale == 'ja' - @weapons = Weapon.jp_search(search_params[:query]).where(conditions) - else - @weapons = Weapon.en_search(search_params[:query]).where(conditions) - end - else - @weapons = Weapon.where(conditions) - end + def weapons + filters = search_params[:filters] + locale = search_params[:locale] || 'en' + conditions = {} - @count = @weapons.length - @weapons = @weapons.paginate(page: search_params[:page], per_page: 10) + if filters + conditions[:rarity] = filters['rarity'] unless filters['rarity'].blank? || filters['rarity'].empty? + conditions[:element] = filters['element'] unless filters['element'].blank? || filters['element'].empty? + conditions[:proficiency] = filters['proficiency1'] unless filters['proficiency1'].blank? || filters['proficiency1'].empty? + conditions[:series] = filters['series'] unless filters['series'].blank? || filters['series'].empty? end - def summons - filters = search_params[:filters] - locale = search_params[:locale] || 'en' - conditions = {} + @weapons = if search_params[:query].present? && search_params[:query].length >= 2 + if locale == 'ja' + Weapon.jp_search(search_params[:query]).where(conditions) + else + Weapon.en_search(search_params[:query]).where(conditions) + end + else + Weapon.where(conditions) + end - if filters - conditions[:rarity] = filters['rarity'] unless filters['rarity'].blank? || filters['rarity'].empty? - conditions[:element] = filters['element'] unless filters['element'].blank? || filters['element'].empty? - end + @count = @weapons.length + @weapons = @weapons.paginate(page: search_params[:page], per_page: 10) + end - if search_params[:query].present? && search_params[:query].length >= 2 - if locale == 'ja' - @summons = Summon.jp_search(search_params[:query]).where(conditions) - else - @summons = Summon.en_search(search_params[:query]).where(conditions) - end - else - @summons = Summon.where(conditions) - end + def summons + filters = search_params[:filters] + locale = search_params[:locale] || 'en' + conditions = {} - @count = @summons.length - @summons = @summons.paginate(page: search_params[:page], per_page: 10) + if filters + conditions[:rarity] = filters['rarity'] unless filters['rarity'].blank? || filters['rarity'].empty? + conditions[:element] = filters['element'] unless filters['element'].blank? || filters['element'].empty? end - def job_skills - raise Api::V1::NoJobProvidedError unless search_params[:job].present? + @summons = if search_params[:query].present? && search_params[:query].length >= 2 + if locale == 'ja' + Summon.jp_search(search_params[:query]).where(conditions) + else + Summon.en_search(search_params[:query]).where(conditions) + end + else + Summon.where(conditions) + end - # Set up basic parameters we'll use - job = Job.find(search_params[:job]) - locale = search_params[:locale] || 'en' + @count = @summons.length + @summons = @summons.paginate(page: search_params[:page], per_page: 10) + end - # Set the conditions based on the group requested - conditions = {} - if search_params[:filters].present? && search_params[:filters]["group"].present? - group = search_params[:filters]["group"].to_i + def job_skills + raise Api::V1::NoJobProvidedError unless search_params[:job].present? - if (group >= 0 && group < 4) - conditions[:color] = group - conditions[:emp] = false - conditions[:base] = false - elsif (group == 4) - conditions[:emp] = true - elsif (group == 5) - conditions[:base] = true - end - end + # Set up basic parameters we'll use + job = Job.find(search_params[:job]) + locale = search_params[:locale] || 'en' - # Perform the query - if search_params[:query].present? && search_params[:query].length >= 2 - @skills = JobSkill.method("#{locale}_search").(search_params[:query]) - .where(conditions) - .where(job: job.id, main: false) - .or( - JobSkill.method("#{locale}_search").(search_params[:query]) - .where(conditions) - .where(sub: true) - ) - else - @skills = JobSkill.all - .where(conditions) - .where(job: job.id, main: false) - .or( - JobSkill.all - .where(conditions) - .where(sub:true) - ) - end + # Set the conditions based on the group requested + conditions = {} + if search_params[:filters].present? && search_params[:filters]['group'].present? + group = search_params[:filters]['group'].to_i - @count = @skills.length - @skills = @skills.paginate(page: search_params[:page], per_page: 10) + if group >= 0 && group < 4 + conditions[:color] = group + conditions[:emp] = false + conditions[:base] = false + elsif group == 4 + conditions[:emp] = true + elsif group == 5 + conditions[:base] = true + end end - private + # Perform the query + @skills = if search_params[:query].present? && search_params[:query].length >= 2 + JobSkill.method("#{locale}_search").call(search_params[:query]) + .where(conditions) + .where(job: job.id, main: false) + .or( + JobSkill.method("#{locale}_search").call(search_params[:query]) + .where(conditions) + .where(sub: true) + ) + else + JobSkill.all + .where(conditions) + .where(job: job.id, main: false) + .or( + JobSkill.all + .where(conditions) + .where(sub: true) + ) + .or( + JobSkill.all + .where(conditions) + .where(job: job.base_job.id, base: true) + .where.not(job: job.id) + ) + end - # Specify whitelisted properties that can be modified. - def search_params - params.require(:search).permit! - end + @count = @skills.length + @skills = @skills.paginate(page: search_params[:page], per_page: 10) + end + + private + + # Specify whitelisted properties that can be modified. + def search_params + params.require(:search).permit! + end end