From d133a9d96e5062f10da678278ef3a687194f718c Mon Sep 17 00:00:00 2001 From: Davis Hyer Date: Wed, 9 Dec 2020 12:49:51 -0700 Subject: [PATCH] add mutation for deleting Conversations fixes VICE-871 flag=react_inbox test plan: - create a conversation with messages between two users - as a user not in the conversation - navigate to /graphiql - add a new mutation - use the deleteConversation to attempt to delete all messages from the conversation - this should fail due to insufficient permissions - as a user in the conversation - navigate to /graphiql - attempt to delete a conversation that doesn't exist - this should fail to unfound Conversation - attempt to delete a conversation that does exist - this should succeed qa risk: low Change-Id: Iea7a02fa9e22e0ebd122dd4bab6e52f91bc3004d Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/254756 Tested-by: Service Cloud Jenkins Reviewed-by: Caleb Guanzon QA-Review: Caleb Guanzon Product-Review: Caleb Guanzon --- app/graphql/audit_log_field_extension.rb | 1 + app/graphql/mutations/delete_conversation.rb | 42 ++++++++++ app/graphql/types/mutation_type.rb | 1 + .../mutations/delete_conversation_spec.rb | 83 +++++++++++++++++++ 4 files changed, 127 insertions(+) create mode 100644 app/graphql/mutations/delete_conversation.rb create mode 100644 spec/graphql/mutations/delete_conversation_spec.rb diff --git a/app/graphql/audit_log_field_extension.rb b/app/graphql/audit_log_field_extension.rb index 72422c0f3dc..17123817120 100644 --- a/app/graphql/audit_log_field_extension.rb +++ b/app/graphql/audit_log_field_extension.rb @@ -140,6 +140,7 @@ class AuditLogFieldExtension < GraphQL::Schema::FieldExtension next if mutation == Mutations::UpdateNotificationPreferences next if mutation == Mutations::CreateConversation next if mutation == Mutations::DeleteConversationMessage + next if mutation == Mutations::DeleteConversation logger = Logger.new(mutation, context, arguments) diff --git a/app/graphql/mutations/delete_conversation.rb b/app/graphql/mutations/delete_conversation.rb new file mode 100644 index 00000000000..5dd48a36835 --- /dev/null +++ b/app/graphql/mutations/delete_conversation.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +# +# Copyright (C) 2020 - present Instructure, Inc. +# +# This file is part of Canvas. +# +# Canvas is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the Free +# Software Foundation, version 3 of the License. +# +# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. +# +# You should have received a copy of the GNU Affero General Public License along +# with this program. If not, see . +# + +class Mutations::DeleteConversation < Mutations::BaseMutation + graphql_name "DeleteConversation" + + # input arguments + argument :id, ID, required: true, prepare: GraphQLHelpers.relay_or_legacy_id_prepare_func('Conversation') + + field :conversation_id, ID, null: false + + def resolve(input:) + conversation = Conversation.find_by(id: input[:id]) + raise GraphQL::ExecutionError, "Unable to find Conversation" if conversation.nil? + + participant_record = current_user.all_conversations.find_by(conversation_id: conversation.id) + raise GraphQL::ExecutionError, "insufficient permissions" if participant_record.nil? + + participant_record.remove_messages(:all) + {conversation_id: conversation.id} + + rescue ActiveRecord::RecordInvalid => invalid + errors_for(invalid.record) + end +end diff --git a/app/graphql/types/mutation_type.rb b/app/graphql/types/mutation_type.rb index 9263b38fa76..234e915849f 100644 --- a/app/graphql/types/mutation_type.rb +++ b/app/graphql/types/mutation_type.rb @@ -69,6 +69,7 @@ class Types::MutationType < Types::ApplicationObjectType field :create_module, mutation: Mutations::CreateModule field :update_notification_preferences, mutation: Mutations::UpdateNotificationPreferences field :delete_conversation_message, mutation: Mutations::DeleteConversationMessage + field :delete_conversation, mutation: Mutations::DeleteConversation # TODO: Remove the in active development string from here once this is more # finalized. diff --git a/spec/graphql/mutations/delete_conversation_spec.rb b/spec/graphql/mutations/delete_conversation_spec.rb new file mode 100644 index 00000000000..b3e791f32b6 --- /dev/null +++ b/spec/graphql/mutations/delete_conversation_spec.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +# +# Copyright (C) 2020 - present Instructure, Inc. +# +# This file is part of Canvas. +# +# Canvas is free software: you can redistribute it and/or modify it under +# the terms of the GNU Affero General Public License as published by the Free +# Software Foundation, version 3 of the License. +# +# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +# details. +# +# You should have received a copy of the GNU Affero General Public License along +# with this program. If not, see . +# + +require "spec_helper" +require_relative "../graphql_spec_helper" + +describe Mutations::DeleteConversation do + before :once do + @sender = user_model + @conversation = conversation(@sender, user_model).conversation + end + + def execute_with_input(delete_input, user_executing: @sender) + mutation_command = <<~GQL + mutation { + deleteConversation(input: { + #{delete_input} + }) { + conversationId + errors { + attribute + message + } + } + } + GQL + context = {current_user: user_executing, request: ActionDispatch::TestRequest.create} + CanvasSchema.execute(mutation_command, context: context) + end + + it "removes all messages from the participant's view" do + query = <<~QUERY + id: #{@conversation.id} + QUERY + expect(@sender.all_conversations.find_by(conversation: @conversation).messages.length).to eq 1 + result = execute_with_input(query) + expect(result.dig('errors')).to be_nil + expect(result.dig('data', 'deleteConversation', 'errors')).to be_nil + expect(result.dig('data', 'deleteConversation', 'conversationId')).to eq @conversation.id.to_s + expect(@sender.all_conversations.find_by(conversation: @conversation).messages.length).to eq 0 + end + + context "errors" do + def expect_error(result, message) + errors = result.dig('errors') || result.dig('data', 'deleteConversation', 'errors') + expect(errors).not_to be_nil + expect(errors[0]['message']).to match(/#{message}/) + end + + it "fails if the conversation doesn't exist" do + query = <<~QUERY + id: #{Conversation.maximum(:id).next} + QUERY + result = execute_with_input(query) + expect_error(result, 'Unable to find Conversation') + end + + it "fails if the requesting user is not a participant" do + query = <<~QUERY + id: #{@conversation.id} + QUERY + result = execute_with_input(query, user_executing: user_model) + expect_error(result, 'insufficient permissions') + end + end +end \ No newline at end of file