diff --git a/app/controllers/api/v1/post_interactions_controller.rb b/app/controllers/api/v1/post_interactions_controller.rb index f52418a8c..c5d6e1697 100644 --- a/app/controllers/api/v1/post_interactions_controller.rb +++ b/app/controllers/api/v1/post_interactions_controller.rb @@ -51,11 +51,32 @@ module Api render json: I18n.t("api.endpoint_errors.posts.cant_report"), status: :unprocessable_entity end + def vote + begin + post = post_service.find!(params[:post_id]) + rescue ActiveRecord::RecordNotFound + render json: I18n.t("api.endpoint_errors.posts.post_not_found"), status: :not_found + return + end + poll_vote = poll_service.vote(post.id, params[:poll_answer_id]) + if poll_vote + head :no_content + else + render json: I18n.t("api.endpoint_errors.interactions.cant_vote"), status: :unprocessable_entity + end + rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotFound + render json: I18n.t("api.endpoint_errors.interactions.cant_vote"), status: :unprocessable_entity + end + private def post_service @post_service ||= PostService.new(current_user) end + + def poll_service + @poll_service ||= PollParticipationService.new(current_user) + end end end end diff --git a/app/controllers/poll_participations_controller.rb b/app/controllers/poll_participations_controller.rb index 555d53168..20aa38b0c 100644 --- a/app/controllers/poll_participations_controller.rb +++ b/app/controllers/poll_participations_controller.rb @@ -4,8 +4,7 @@ class PollParticipationsController < ApplicationController before_action :authenticate_user! def create - answer = PollAnswer.find(params[:poll_answer_id]) - poll_participation = current_user.participate_in_poll!(target, answer) if target + poll_participation = poll_service.vote(params[:post_id], params[:poll_answer_id]) respond_to do |format| format.mobile { redirect_to stream_path } format.json { render json: poll_participation, :status => 201 } @@ -19,9 +18,7 @@ class PollParticipationsController < ApplicationController private - def target - @target ||= if params[:post_id] - current_user.find_visible_shareable_by_id(Post, params[:post_id]) || raise(ActiveRecord::RecordNotFound.new) - end + def poll_service + @poll_service ||= PollParticipationService.new(current_user) end end diff --git a/app/services/poll_participation_service.rb b/app/services/poll_participation_service.rb new file mode 100644 index 000000000..3cd772689 --- /dev/null +++ b/app/services/poll_participation_service.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class PollParticipationService + def initialize(user) + @user = user + end + + def vote(post_id, answer_id) + answer = PollAnswer.find(answer_id) + @user.participate_in_poll!(target(post_id), answer) if target(post_id) + end + + private + + def target(post_id) + @target ||= @user.find_visible_shareable_by_id(Post, post_id) || raise(ActiveRecord::RecordNotFound.new) + end +end diff --git a/config/locales/diaspora/en.yml b/config/locales/diaspora/en.yml index 26b0a3fd3..1ba39a247 100644 --- a/config/locales/diaspora/en.yml +++ b/config/locales/diaspora/en.yml @@ -974,6 +974,7 @@ en: no_like: "Like doesn’t exist" interactions: cant_subscribe: "Can't subscribe to this post" + cant_vote: "Can't vote on this post" notifications: not_found: "Notification with provided guid could not be found" cant_process: "Couldn't process the notifications request" diff --git a/config/routes.rb b/config/routes.rb index 15ddfc366..7311f2f20 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -236,6 +236,7 @@ Rails.application.routes.draw do post "mute" => "post_interactions#mute" post "hide" => "post_interactions#hide" post "report" => "post_interactions#report" + post "vote" => "post_interactions#vote" end resources :conversations, only: %i[show index create destroy] do resources :messages, only: %i[index create] diff --git a/spec/controllers/poll_participations_controller_spec.rb b/spec/controllers/poll_participations_controller_spec.rb new file mode 100644 index 000000000..45e6fdd33 --- /dev/null +++ b/spec/controllers/poll_participations_controller_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +describe PollParticipationsController, type: :controller do + let(:poll_post) { FactoryGirl.create(:status_message_with_poll, public: true) } + let(:poll_answer) { poll_post.poll.poll_answers.first } + + before do + sign_in alice, scope: :user + request.env["HTTP_ACCEPT"] = "application/json" + end + + describe "voting on poll" do + it "succeeds" do + post :create, params: {post_id: poll_post.id, poll_answer_id: poll_answer.id} + expect(response.status).to eq(201) + poll_participation = JSON.parse(response.body)["poll_participation"] + expect(poll_participation["poll_answer_id"]).to eq(poll_answer.id) + end + + it "fails to vote twice" do + post :create, params: {post_id: poll_post.id, poll_answer_id: poll_answer.id} + expect(response.status).to eq(201) + post :create, params: {post_id: poll_post.id, poll_answer_id: poll_answer.id} + expect(response.status).to eq(403) + end + + it "fails with bad answer id" do + expect { + post :create, params: {post_id: poll_post.id, poll_answer_id: -1} + }.to raise_error(ActiveRecord::RecordNotFound) + end + + it "fails with bad post id" do + expect { post :create, params: {post_id: -1, poll_answer_id: poll_answer.id} } + .to raise_error(ActiveRecord::RecordNotFound) + end + end +end diff --git a/spec/integration/api/post_interactions_controller_spec.rb b/spec/integration/api/post_interactions_controller_spec.rb index cb9f0af0b..7c45f3734 100644 --- a/spec/integration/api/post_interactions_controller_spec.rb +++ b/spec/integration/api/post_interactions_controller_spec.rb @@ -291,4 +291,91 @@ describe Api::V1::PostInteractionsController do end end end + + describe "#vote" do + before do + base_params = {status_message: {text: "myText"}, public: true} + poll_params = {poll_question: "something?", poll_answers: %w[yes no maybe]} + merged_params = base_params.merge(poll_params) + @poll_post = StatusMessageCreationService.new(alice).create(merged_params) + @poll_answer = @poll_post.poll.poll_answers.first + end + + it "succeeds" do + post( + api_v1_post_vote_path(@poll_post.guid), + params: { + poll_answer_id: @poll_answer.id, + access_token: access_token + } + ) + expect(response.status).to eq(204) + expect(@poll_answer.reload.vote_count).to eq(1) + end + + it "fails to vote twice" do + post( + api_v1_post_vote_path(@poll_post.guid), + params: { + poll_answer_id: @poll_answer.id, + access_token: access_token + } + ) + expect(response.status).to eq(204) + post( + api_v1_post_vote_path(@poll_post.guid), + params: { + poll_answer_id: @poll_answer.id, + access_token: access_token + } + ) + expect(response.status).to eq(422) + expect(response.body).to eq(I18n.t("api.endpoint_errors.interactions.cant_vote")) + end + + it "fails with bad answer id" do + post( + api_v1_post_vote_path(@poll_post.guid), + params: { + poll_answer_id: -1, + access_token: access_token + } + ) + expect(response.status).to eq(422) + expect(response.body).to eq(I18n.t("api.endpoint_errors.interactions.cant_vote")) + end + + it "fails with bad post id" do + post( + api_v1_post_vote_path("999_999_999"), + params: { + poll_answer_id: @poll_answer.id, + access_token: access_token + } + ) + expect(response.status).to eq(404) + expect(response.body).to eq(I18n.t("api.endpoint_errors.posts.post_not_found")) + end + it "with read only token" do + post( + api_v1_post_vote_path(@poll_post.guid), + params: { + poll_answer_id: @poll_answer.id, + access_token: access_token_read_only + } + ) + expect(response.status).to eq(403) + end + + it "with invalid token" do + post( + api_v1_post_vote_path(@poll_post.guid), + params: { + poll_answer_id: @poll_answer.id, + access_token: "999_999_999" + } + ) + expect(response.status).to eq(401) + end + end end diff --git a/spec/services/poll_participation_service_spec.rb b/spec/services/poll_participation_service_spec.rb new file mode 100644 index 000000000..d9b0e7b6c --- /dev/null +++ b/spec/services/poll_participation_service_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +describe PollParticipationService do + let(:poll_post) { FactoryGirl.create(:status_message_with_poll, public: true) } + let(:poll_answer) { poll_post.poll.poll_answers.first } + + describe "voting on poll" do + it "succeeds" do + expect(poll_service.vote(poll_post.id, poll_answer.id)).not_to be_nil + end + + it "fails to vote twice" do + expect(poll_service.vote(poll_post.id, poll_answer.id)).not_to be_nil + expect { poll_service.vote(poll_post.id, poll_answer.id) }.to raise_error(ActiveRecord::RecordInvalid) + end + + it "fails with bad answer id" do + expect { poll_service.vote(poll_post.id, -2) }.to raise_error(ActiveRecord::RecordNotFound) + end + + it "fails with bad post id" do + expect { poll_service.vote(-1, poll_answer.id) }.to raise_error(ActiveRecord::RecordNotFound) + end + end + + def poll_service(user=alice) + PollParticipationService.new(user) + end +end