| @@ -6,7 +6,7 @@ class DeerjikistsController < ApplicationController | |||||
| deerjikist = Deerjikist | deerjikist = Deerjikist | ||||
| .joins(:tag) | .joins(:tag) | ||||
| .includes(:tag, tag: :tag_name) | |||||
| .includes(tag: :tag_name) | |||||
| .find_by(platform:, code:) | .find_by(platform:, code:) | ||||
| if deerjikist | if deerjikist | ||||
| render json: DeerjikistRepr.base(deerjikist) | render json: DeerjikistRepr.base(deerjikist) | ||||
| @@ -14,4 +14,34 @@ class DeerjikistsController < ApplicationController | |||||
| head :not_found | head :not_found | ||||
| end | end | ||||
| end | end | ||||
| def update | |||||
| return head :unauthorized unless current_user | |||||
| return head :forbidden unless current_user.member? | |||||
| platform = params[:platform].to_s.strip | |||||
| code = params[:code].to_s.strip | |||||
| tag_id = params[:tag_id].to_i | |||||
| return head :bad_request if platform.blank? || code.blank? || tag_id <= 0 | |||||
| deerjikist = Deerjikist.find_or_initialize_by(platform:, code:).tap do |d| | |||||
| d.tag_id = tag_id | |||||
| d.save! | |||||
| end | |||||
| render json: DeerjikistRepr.base(deerjikist) | |||||
| end | |||||
| def destroy | |||||
| return head :unauthorized unless current_user | |||||
| return head :forbidden unless current_user.member? | |||||
| platform = params[:platform].to_s.strip | |||||
| code = params[:code].to_s.strip | |||||
| return head :bad_request if platform.blank? || code.blank? | |||||
| Deerjikist.find([platform, code]).destroy! | |||||
| head :no_content | |||||
| end | |||||
| end | end | ||||
| @@ -90,7 +90,7 @@ class TagsController < ApplicationController | |||||
| tag = Tag.joins(:tag_name) | tag = Tag.joins(:tag_name) | ||||
| .includes(:tag_name, tag_name: :wiki_page) | .includes(:tag_name, tag_name: :wiki_page) | ||||
| .find_by(id: params[:id]) | .find_by(id: params[:id]) | ||||
| return head :bad_request unless tag | |||||
| return head :not_found unless tag | |||||
| render json: DeerjikistRepr.many(tag.deerjikists) | render json: DeerjikistRepr.many(tag.deerjikists) | ||||
| end | end | ||||
| @@ -65,9 +65,11 @@ Rails.application.routes.draw do | |||||
| resources :deerjikists, only: [] do | resources :deerjikists, only: [] do | ||||
| collection do | collection do | ||||
| get ':platform/:code', action: :show | |||||
| put ':platfrom/:code', action: :update | |||||
| delete ':platform/:code', action: :destroy | |||||
| scope ':platform/:code' do | |||||
| get '', action: :show | |||||
| put '', action: :update | |||||
| delete '', action: :destroy | |||||
| end | |||||
| end | end | ||||
| end | end | ||||
| end | end | ||||
| @@ -1,11 +1,11 @@ | |||||
| FactoryBot.define do | FactoryBot.define do | ||||
| factory :tag do | factory :tag do | ||||
| category { 'general' } | |||||
| category { :general } | |||||
| post_count { 0 } | post_count { 0 } | ||||
| association :tag_name | association :tag_name | ||||
| trait :nico do | trait :nico do | ||||
| category { 'nico' } | |||||
| category { :nico } | |||||
| tag_name { association(:tag_name, name: "nico:#{ SecureRandom.hex(4) }") } | tag_name { association(:tag_name, name: "nico:#{ SecureRandom.hex(4) }") } | ||||
| end | end | ||||
| end | end | ||||
| @@ -0,0 +1,181 @@ | |||||
| require 'rails_helper' | |||||
| RSpec.describe 'Deerjikists API', type: :request do | |||||
| let(:platform) { 'nico' } | |||||
| let(:code) { 'deerjika-bot' } | |||||
| let!(:tag1) { create(:tag, category: :deerjikist) } | |||||
| let!(:tag2) { create(:tag, category: :deerjikist) } | |||||
| let(:member) { create(:user, :member) } | |||||
| let(:guest) { create(:user, role: :guest) } | |||||
| describe 'GET /deerjikists/:platform/:code' do | |||||
| subject(:do_request) do | |||||
| get "/deerjikists/#{ platform }/#{ code }" | |||||
| end | |||||
| context 'when deerjikist exists' do | |||||
| before do | |||||
| Deerjikist.create!(platform:, code:, tag: tag1) | |||||
| end | |||||
| it 'returns 200 and deerjikist json' do | |||||
| do_request | |||||
| expect(response).to have_http_status(:ok) | |||||
| expect(json).to be_a(Hash) | |||||
| expect(json['platform']).to eq(platform) | |||||
| expect(json['code']).to eq(code) | |||||
| expect(json['tag']).to be_a(Hash) | |||||
| expect(json['tag']['id']).to eq(tag1.id) | |||||
| expect(json['tag']['name']).to eq(tag1.name) | |||||
| end | |||||
| end | |||||
| context 'when deerjikist does not exist' do | |||||
| it 'returns 404' do | |||||
| do_request | |||||
| expect(response).to have_http_status(:not_found) | |||||
| end | |||||
| end | |||||
| context 'when platform or code become blank after strip' do | |||||
| it 'returns 400' do | |||||
| get '/deerjikists/%20/%20' | |||||
| expect(response).to have_http_status(:bad_request) | |||||
| end | |||||
| end | |||||
| end | |||||
| describe 'PUT /deerjikists/:platform/:code' do | |||||
| subject(:do_request) do | |||||
| put "/deerjikists/#{ platform }/#{ code }", params: payload | |||||
| end | |||||
| let(:payload) { { tag_id: tag1.id } } | |||||
| context 'when not legged in' do | |||||
| it 'returns 401' do | |||||
| sign_out | |||||
| do_request | |||||
| expect(response).to have_http_status(:unauthorized) | |||||
| end | |||||
| end | |||||
| context 'when logged in but not member' do | |||||
| it 'returns 403' do | |||||
| sign_in_as guest | |||||
| do_request | |||||
| expect(response).to have_http_status(:forbidden) | |||||
| end | |||||
| end | |||||
| context 'when member' do | |||||
| before do | |||||
| sign_in_as member | |||||
| end | |||||
| context 'when params invalid' do | |||||
| it 'returns 400 when tag_id is missing or invalid' do | |||||
| put "/deerjikists/#{ platform }/#{ code }", params: { tag_id: 0 } | |||||
| expect(response).to have_http_status(:bad_request) | |||||
| end | |||||
| it 'returns 400 when platform or code blank after strip' do | |||||
| put '/deerjikists/%20/%20', params: { tag_id: tag1.id } | |||||
| expect(response).to have_http_status(:bad_request) | |||||
| end | |||||
| end | |||||
| context 'when creating new deerjikist' do | |||||
| it 'creates and returns 200 with json' do | |||||
| expect { do_request }.to change { Deerjikist.count }.by(1) | |||||
| expect(response).to have_http_status(:ok) | |||||
| d = Deerjikist.find_by(platform:, code:) | |||||
| expect(d).to be_present | |||||
| expect(d.tag_id).to eq(tag1.id) | |||||
| expect(json['platform']).to eq(platform) | |||||
| expect(json['code']).to eq(code) | |||||
| end | |||||
| end | |||||
| context 'when updating existing deerjikist' do | |||||
| before do | |||||
| Deerjikist.create!(platform:, code:, tag: tag1) | |||||
| end | |||||
| let(:payload) { { tag_id: tag2.id } } | |||||
| it 'updates tag_id and returns 200' do | |||||
| expect { do_request }.not_to change { Deerjikist.count } | |||||
| expect(response).to have_http_status(:ok) | |||||
| d = Deerjikist.find_by(platform:, code:) | |||||
| expect(d.tag_id).to eq(tag2.id) | |||||
| expect(json['platform']).to eq(platform) | |||||
| expect(json['code']).to eq(code) | |||||
| end | |||||
| end | |||||
| end | |||||
| end | |||||
| describe 'DELETE /deerjikists/:platform/:code' do | |||||
| subject(:do_request) do | |||||
| delete "/deerjikists/#{ platform }/#{ code }" | |||||
| end | |||||
| context 'when not logged in' do | |||||
| it 'returns 401' do | |||||
| sign_out | |||||
| do_request | |||||
| expect(response).to have_http_status(:unauthorized) | |||||
| end | |||||
| end | |||||
| context 'when logged in but not member' do | |||||
| it 'returns 403' do | |||||
| sign_in_as guest | |||||
| do_request | |||||
| expect(response).to have_http_status(:forbidden) | |||||
| end | |||||
| end | |||||
| context 'when member' do | |||||
| before do | |||||
| sign_in_as member | |||||
| end | |||||
| it 'returns 400 when platform/code blank after strip' do | |||||
| delete '/deerjikists/%20/%20' | |||||
| expect(response).to have_http_status(:bad_request) | |||||
| end | |||||
| context 'when deerjikist exists' do | |||||
| before do | |||||
| Deerjikist.create!(platform: platform, code: code, tag: tag1) | |||||
| end | |||||
| it 'destroys and returns 204' do | |||||
| expect { | |||||
| do_request | |||||
| }.to change { Deerjikist.exists?(platform: platform, code: code) } | |||||
| .from(true).to(false) | |||||
| expect(response).to have_http_status(:no_content) | |||||
| end | |||||
| end | |||||
| context 'when deerjikist does not exist' do | |||||
| it 'returns 404' do | |||||
| do_request | |||||
| expect(response).to have_http_status(:not_found) | |||||
| end | |||||
| end | |||||
| end | |||||
| end | |||||
| end | |||||
| @@ -0,0 +1,102 @@ | |||||
| require 'rails_helper' | |||||
| RSpec.describe 'Tags deerjikists API', type: :request do | |||||
| let(:platform1) { 'nico' } | |||||
| let(:code1) { 'deerjika-bot' } | |||||
| let(:platform2) { 'youtube' } | |||||
| let(:code2) { 'deerjika-bot.bsky.social' } | |||||
| let!(:tag) { create(:tag, category: :deerjikist) } | |||||
| before do | |||||
| # show_by_name / deerjikists_by_name 用に名前を固定 | |||||
| tag.tag_name.update!(name: 'deerjika') | |||||
| end | |||||
| describe 'GET /tags/:id/deerjikists' do | |||||
| subject(:do_request) do | |||||
| get "/tags/#{ tag_id }/deerjikists" | |||||
| end | |||||
| let(:tag_id) { tag.id } | |||||
| context 'when tag exists and has no deerjikists' do | |||||
| it 'returns 200 and empty array' do | |||||
| do_request | |||||
| expect(response).to have_http_status(:ok) | |||||
| expect(json).to eq([]) | |||||
| end | |||||
| end | |||||
| context 'when tag exists and has deerjikists' do | |||||
| before do | |||||
| Deerjikist.create!(platform: platform1, code: code1, tag: tag) | |||||
| Deerjikist.create!(platform: platform2, code: code2, tag: tag) | |||||
| end | |||||
| it 'returns 200 and deerjikists array' do | |||||
| do_request | |||||
| expect(response).to have_http_status(:ok) | |||||
| expect(json).to be_a(Array) | |||||
| expect(json.size).to eq(2) | |||||
| expect(json.map { |h| [h['platform'], h['code']] }).to contain_exactly( | |||||
| [platform1, code1], | |||||
| [platform2, code2], | |||||
| ) | |||||
| end | |||||
| end | |||||
| context 'when tag does not exist' do | |||||
| let(:tag_id) { 9_999_999 } | |||||
| it 'returns 404' do | |||||
| do_request | |||||
| expect(response).to have_http_status(:not_found) | |||||
| end | |||||
| end | |||||
| end | |||||
| describe 'GET /tags/name/:name/deerjikists' do | |||||
| subject(:do_request) do | |||||
| get "/tags/name/#{ name }/deerjikists" | |||||
| end | |||||
| let(:name) { 'deerjika' } | |||||
| context 'when name is blank after strip' do | |||||
| let(:name) { '%20' } | |||||
| it 'returns 400' do | |||||
| do_request | |||||
| expect(response).to have_http_status(:bad_request) | |||||
| end | |||||
| end | |||||
| context 'when tag does not exist for name' do | |||||
| let(:name) { 'no-such-tag' } | |||||
| it 'returns 404' do | |||||
| do_request | |||||
| expect(response).to have_http_status(:not_found) | |||||
| end | |||||
| end | |||||
| context 'when tag exists and has deerjikists' do | |||||
| before do | |||||
| Deerjikist.create!(platform: platform1, code: code1, tag: tag) | |||||
| end | |||||
| it 'returns 200 and deerjikists array' do | |||||
| do_request | |||||
| expect(response).to have_http_status(:ok) | |||||
| expect(json).to be_a(Array) | |||||
| expect(json.size).to eq(1) | |||||
| expect(json[0]['platform']).to eq(platform1) | |||||
| expect(json[0]['code']).to eq(code1) | |||||
| end | |||||
| end | |||||
| end | |||||
| end | |||||