diff --git a/app/controllers/api/v1/conversations_controller.rb b/app/controllers/api/v1/conversations_controller.rb index ddda48405..bbf579dc4 100644 --- a/app/controllers/api/v1/conversations_controller.rb +++ b/app/controllers/api/v1/conversations_controller.rb @@ -42,7 +42,17 @@ module Api Diaspora::Federation::Dispatcher.defer_dispatch(current_user, conversation) render json: conversation_as_json(conversation), status: :created rescue ActiveRecord::RecordInvalid, ActionController::ParameterMissing, ActiveRecord::RecordNotFound - render_error 422, "Couldn’t accept or process the conversation" + render_error 422, "Couldn't accept or process the conversation" + end + + def update + read = BOOLEAN_TYPE.cast(params.require(:read)) + conversation = conversation_service.find!(params[:id]) + conversation.update_read_for(current_user, read: read) + + render json: conversation_as_json(conversation) + rescue ActiveRecord::RecordInvalid, ActionController::ParameterMissing, ActiveRecord::RecordNotFound + render_error 422, "Couldn't update the conversation" end def destroy diff --git a/app/controllers/api/v1/messages_controller.rb b/app/controllers/api/v1/messages_controller.rb index 6b12d2082..7dfe60f52 100644 --- a/app/controllers/api/v1/messages_controller.rb +++ b/app/controllers/api/v1/messages_controller.rb @@ -24,7 +24,6 @@ module Api def index conversation = conversation_service.find!(params.require(:conversation_id)) - conversation.set_read(current_user) messages_page = index_pager(conversation.messages).response messages_page[:data] = messages_page[:data].map {|x| message_json(x) } render_paged_api_response messages_page diff --git a/app/models/conversation.rb b/app/models/conversation.rb index bb7cc1f3b..1e64a3872 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -35,9 +35,13 @@ class Conversation < ApplicationRecord end def set_read(user) + update_read_for(user, read: true) + end + + def update_read_for(user, read:) visibility = conversation_visibilities.find_by(person_id: user.person.id) return unless visibility - visibility.unread = 0 + visibility.unread = read ? 0 : 1 visibility.save end diff --git a/config/routes.rb b/config/routes.rb index 9e6cad3f1..2fbbcf479 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -244,7 +244,7 @@ Rails.application.routes.draw do post "report" => "post_interactions#report" post "vote" => "post_interactions#vote" end - resources :conversations, only: %i[show index create destroy] do + resources :conversations do resources :messages, only: %i[index create] end resources :notifications, only: %i[index show update] diff --git a/spec/integration/api/conversations_controller_spec.rb b/spec/integration/api/conversations_controller_spec.rb index 414ea8b70..2e4818b5f 100644 --- a/spec/integration/api/conversations_controller_spec.rb +++ b/spec/integration/api/conversations_controller_spec.rb @@ -57,7 +57,7 @@ describe Api::V1::ConversationsController do context "without valid data" do it "fails with empty body" do post api_v1_conversations_path, params: {access_token: access_token} - confirm_api_error(response, 422, "Couldn’t accept or process the conversation") + confirm_api_error(response, 422, "Couldn't accept or process the conversation") end it "fails with missing subject " do @@ -67,7 +67,7 @@ describe Api::V1::ConversationsController do access_token: access_token } post api_v1_conversations_path, params: incomplete_conversation - confirm_api_error(response, 422, "Couldn’t accept or process the conversation") + confirm_api_error(response, 422, "Couldn't accept or process the conversation") end it "fails with missing body " do @@ -77,7 +77,7 @@ describe Api::V1::ConversationsController do access_token: access_token } post api_v1_conversations_path, params: incomplete_conversation - confirm_api_error(response, 422, "Couldn’t accept or process the conversation") + confirm_api_error(response, 422, "Couldn't accept or process the conversation") end it "fails with missing recipients " do @@ -87,7 +87,7 @@ describe Api::V1::ConversationsController do access_token: access_token } post api_v1_conversations_path, params: incomplete_conversation - confirm_api_error(response, 422, "Couldn’t accept or process the conversation") + confirm_api_error(response, 422, "Couldn't accept or process the conversation") end it "fails with bad recipient ID " do @@ -98,7 +98,7 @@ describe Api::V1::ConversationsController do access_token: access_token } post api_v1_conversations_path, params: incomplete_conversation - confirm_api_error(response, 422, "Couldn’t accept or process the conversation") + confirm_api_error(response, 422, "Couldn't accept or process the conversation") end it "fails with invalid recipient (not allowed to message) " do @@ -109,7 +109,7 @@ describe Api::V1::ConversationsController do access_token: access_token } post api_v1_conversations_path, params: incomplete_conversation - confirm_api_error(response, 422, "Couldn’t accept or process the conversation") + confirm_api_error(response, 422, "Couldn't accept or process the conversation") end end @@ -264,6 +264,83 @@ describe Api::V1::ConversationsController do end end + describe "#update" do + before do + post api_v1_conversations_path, params: @conversation_request + @conversation_guid = response_body(response)["guid"] + @conversation = conversation_service.find!(@conversation_guid) + end + + context "valid conversation ID" do + it "marks as read and returns the corresponding conversation" do + @conversation.conversation_visibilities.where(person: auth.user.person).update(unread: true) + + patch( + api_v1_conversation_path(@conversation_guid), + params: {read: true, access_token: access_token} + ) + expect(response.status).to eq(200) + conversation = response_body(response) + confirm_conversation_format(conversation, @conversation, [auth.user, alice]) + + expect(conversation.to_json).to match_json_schema(:api_v1_schema, fragment: "#/definitions/conversation") + end + + it "marks as unread and returns the corresponding conversation" do + @conversation.conversation_visibilities.where(person: auth.user.person).update(unread: false) + + patch( + api_v1_conversation_path(@conversation_guid), + params: {read: false, access_token: access_token} + ) + expect(response.status).to eq(200) + conversation = response_body(response) + confirm_conversation_format(conversation, @conversation, [auth.user, alice], read: false) + + expect(conversation.to_json).to match_json_schema(:api_v1_schema, fragment: "#/definitions/conversation") + end + end + + context "with missing parameters" do + it "for read" do + patch( + api_v1_conversation_path(@conversation_guid), + params: {access_token: access_token} + ) + confirm_api_error(response, 422, "Couldn't update the conversation") + end + end + + context "non existing conversation ID" do + it "returns a not found error (404)" do + get( + api_v1_conversation_path(-1), + params: {access_token: access_token} + ) + confirm_api_error(response, 404, "Conversation with provided guid could not be found") + end + end + + context "with improper credentials" do + it "fails without conversation scope" do + get( + api_v1_conversation_path(@conversation_guid), + params: {access_token: access_token_minimum_scopes} + ) + expect(response.status).to eq(403) + end + + it "fails without valid token" do + conversation_guid = response_body(response)["guid"] + get( + api_v1_conversation_path(conversation_guid), + params: {access_token: invalid_token} + ) + expect(response.status).to eq(401) + end + end + end + describe "#destroy " do before do auth.user.seed_aspects @@ -366,12 +443,12 @@ describe Api::V1::ConversationsController do end # rubocop:disable Metrics/AbcSize - def confirm_conversation_format(conversation, ref_conversation, ref_participants) + def confirm_conversation_format(conversation, ref_conversation, ref_participants, read: true) expect(conversation["guid"]).to_not be_nil conversation_service.find!(conversation["guid"]) expect(conversation["subject"]).to eq ref_conversation[:subject] expect(conversation["created_at"]).to_not be_nil - expect(conversation["read"]).to be_truthy + expect(conversation["read"]).to read ? be_truthy : be_falsy expect(conversation["participants"].length).to eq(ref_participants.length) participants = conversation["participants"] diff --git a/spec/integration/api/messages_controller_spec.rb b/spec/integration/api/messages_controller_spec.rb index bc7528a07..2cbcf38b8 100644 --- a/spec/integration/api/messages_controller_spec.rb +++ b/spec/integration/api/messages_controller_spec.rb @@ -116,6 +116,10 @@ describe Api::V1::MessagesController do before do post api_v1_conversations_path, params: @conversation @conversation_guid = JSON.parse(response.body)["guid"] + Conversation.find_by(guid: @conversation_guid) + .conversation_visibilities + .where(person: auth.user.person) + .update(unread: true) end context "retrieving messages" do @@ -132,7 +136,7 @@ describe Api::V1::MessagesController do confirm_message_format(messages[0], "first message", auth.user) conversation = get_conversation(@conversation_guid) - expect(conversation[:read]).to be_truthy + expect(conversation[:read]).to be_falsy end context "improper credentials" do