diff --git a/app/listeners/notification_listener.rb b/app/listeners/notification_listener.rb index 7f712e896..4192ed250 100644 --- a/app/listeners/notification_listener.rb +++ b/app/listeners/notification_listener.rb @@ -31,7 +31,8 @@ class NotificationListener < BaseListener message, account = extract_message_and_account(event) conversation = message.conversation - generate_notifications_for_mentions(message, account) + # Check if the message contains any mentions + Messages::MentionService.new(message: message).perform # only want to notify agents about customer messages return unless message.incoming? @@ -44,34 +45,4 @@ class NotificationListener < BaseListener primary_actor: message ).perform end - - private - - def get_valid_mentioned_ids(mentioned_ids, inbox) - valid_mentionable_ids = inbox.account.administrators.map(&:id) + inbox.members.map(&:id) - # Intersection of ids - mentioned_ids & valid_mentionable_ids.uniq.map(&:to_s) - end - - def generate_notifications_for_mentions(message, account) - return unless message.private? - - return if message.content.blank? - - mentioned_ids = message.content.scan(%r{\(mention://(user|team)/(\d+)/(.+?)\)}).map(&:second).uniq - - return if mentioned_ids.blank? - - valid_mentioned_ids = get_valid_mentioned_ids(mentioned_ids, message.inbox) - Conversations::UserMentionJob.perform_later(valid_mentioned_ids, message.conversation.id, account.id) - - valid_mentioned_ids.each do |user_id| - NotificationBuilder.new( - notification_type: 'conversation_mention', - user: User.find(user_id), - account: account, - primary_actor: message - ).perform - end - end end diff --git a/app/services/messages/mention_service.rb b/app/services/messages/mention_service.rb new file mode 100644 index 000000000..aa8cbfce2 --- /dev/null +++ b/app/services/messages/mention_service.rb @@ -0,0 +1,48 @@ +class Messages::MentionService + pattr_initialize [:message!] + + def perform + return unless valid_mention_message?(message) + + validated_mentioned_ids = filter_mentioned_ids_by_inbox + return if validated_mentioned_ids.blank? + + Conversations::UserMentionJob.perform_later(validated_mentioned_ids, message.conversation.id, message.account.id) + generate_notifications_for_mentions(validated_mentioned_ids) + add_mentioned_users_as_participants(validated_mentioned_ids) + end + + private + + def valid_mention_message?(message) + message.private? && message.content.present? && mentioned_ids.present? + end + + def mentioned_ids + @mentioned_ids ||= message.content.scan(%r{\(mention://(user|team)/(\d+)/(.+?)\)}).map(&:second).uniq + end + + def filter_mentioned_ids_by_inbox + inbox = message.inbox + valid_mentionable_ids = inbox.account.administrators.map(&:id) + inbox.members.map(&:id) + # Intersection of ids + mentioned_ids & valid_mentionable_ids.uniq.map(&:to_s) + end + + def generate_notifications_for_mentions(validated_mentioned_ids) + validated_mentioned_ids.each do |user_id| + NotificationBuilder.new( + notification_type: 'conversation_mention', + user: User.find(user_id), + account: message.account, + primary_actor: message + ).perform + end + end + + def add_mentioned_users_as_participants(validated_mentioned_ids) + validated_mentioned_ids.each do |user_id| + message.conversation.conversation_participants.find_or_create_by!(user_id: user_id) + end + end +end diff --git a/spec/listeners/notification_listener_spec.rb b/spec/listeners/notification_listener_spec.rb index e581e9ac6..f30ec6391 100644 --- a/spec/listeners/notification_listener_spec.rb +++ b/spec/listeners/notification_listener_spec.rb @@ -4,7 +4,6 @@ describe NotificationListener do let!(:account) { create(:account) } let!(:user) { create(:user, account: account) } let!(:first_agent) { create(:user, account: account) } - let!(:second_agent) { create(:user, account: account) } let!(:agent_with_out_notification) { create(:user, account: account) } let!(:inbox) { create(:inbox, account: account) } let!(:conversation) { create(:conversation, account: account, inbox: inbox, assignee: user) } @@ -55,68 +54,29 @@ describe NotificationListener do notification_setting.save! end - context 'when message contains mention' do - it 'creates notifications for inbox member who was mentioned' do - builder = double - allow(NotificationBuilder).to receive(:new).and_return(builder) - allow(builder).to receive(:perform) + it 'will call mention service' do + mention_service = instance_double(Messages::MentionService) + allow(Messages::MentionService).to receive(:new).and_return(mention_service) + allow(mention_service).to receive(:perform) - create(:inbox_member, user: first_agent, inbox: inbox) - conversation.reload + create(:inbox_member, user: first_agent, inbox: inbox) + conversation.reload - message = build( - :message, - conversation: conversation, - account: account, - content: "hi [#{first_agent.name}](mention://user/#{first_agent.id}/#{first_agent.name})", - private: true - ) + message = build( + :message, + conversation: conversation, + account: account, + content: "hi [#{first_agent.name}](mention://user/#{first_agent.id}/#{first_agent.name})", + private: true + ) - event = Events::Base.new(event_name, Time.zone.now, message: message) - listener.message_created(event) - - expect(NotificationBuilder).to have_received(:new).with(notification_type: 'conversation_mention', - user: first_agent, - account: account, - primary_actor: message) - end - end - - context 'when message contains multiple mentions' do - it 'creates notifications for inbox member who was mentioned' do - builder = double - allow(NotificationBuilder).to receive(:new).and_return(builder) - allow(builder).to receive(:perform) - create(:inbox_member, user: first_agent, inbox: inbox) - create(:inbox_member, user: second_agent, inbox: inbox) - conversation.reload - - message = build( - :message, - conversation: conversation, - account: account, - content: "hey [#{second_agent.name}](mention://user/#{second_agent.id}/#{second_agent.name})/ - [#{first_agent.name}](mention://user/#{first_agent.id}/#{first_agent.name}), - please look in to this?", - private: true - ) - - event = Events::Base.new(event_name, Time.zone.now, message: message) - listener.message_created(event) - - expect(NotificationBuilder).to have_received(:new).with(notification_type: 'conversation_mention', - user: second_agent, - account: account, - primary_actor: message) - expect(NotificationBuilder).to have_received(:new).with(notification_type: 'conversation_mention', - user: first_agent, - account: account, - primary_actor: message) - end + expect(mention_service).to receive(:perform) + event = Events::Base.new(event_name, Time.zone.now, message: message) + listener.message_created(event) end context 'when message content is empty' do - it 'creates notifications' do + it 'will be processed correctly' do builder = double allow(NotificationBuilder).to receive(:new).and_return(builder) allow(builder).to receive(:perform) diff --git a/spec/services/messages/mention_service_spec.rb b/spec/services/messages/mention_service_spec.rb new file mode 100644 index 000000000..21484ab13 --- /dev/null +++ b/spec/services/messages/mention_service_spec.rb @@ -0,0 +1,70 @@ +require 'rails_helper' + +describe Messages::MentionService do + let!(:account) { create(:account) } + let!(:user) { create(:user, account: account) } + let!(:first_agent) { create(:user, account: account) } + let!(:second_agent) { create(:user, account: account) } + let!(:inbox) { create(:inbox, account: account) } + let!(:conversation) { create(:conversation, account: account, inbox: inbox, assignee: user) } + let(:builder) { double } + + before do + create(:inbox_member, user: first_agent, inbox: inbox) + create(:inbox_member, user: second_agent, inbox: inbox) + conversation.reload + allow(NotificationBuilder).to receive(:new).and_return(builder) + allow(builder).to receive(:perform) + end + + context 'when message contains mention' do + it 'creates notifications for inbox member who was mentioned' do + message = build( + :message, + conversation: conversation, + account: account, + content: "hi [#{first_agent.name}](mention://user/#{first_agent.id}/#{first_agent.name})", + private: true + ) + + described_class.new(message: message).perform + + expect(NotificationBuilder).to have_received(:new).with(notification_type: 'conversation_mention', + user: first_agent, + account: account, + primary_actor: message) + end + end + + context 'when message contains multiple mentions' do + let(:message) do + build( + :message, + conversation: conversation, + account: account, + content: "hey [#{second_agent.name}](mention://user/#{second_agent.id}/#{second_agent.name})/ + [#{first_agent.name}](mention://user/#{first_agent.id}/#{first_agent.name}), + please look in to this?", + private: true + ) + end + + it 'creates notifications for inbox member who was mentioned' do + described_class.new(message: message).perform + + expect(NotificationBuilder).to have_received(:new).with(notification_type: 'conversation_mention', + user: second_agent, + account: account, + primary_actor: message) + expect(NotificationBuilder).to have_received(:new).with(notification_type: 'conversation_mention', + user: first_agent, + account: account, + primary_actor: message) + end + + it 'add the users to the participants list' do + described_class.new(message: message).perform + expect(conversation.conversation_participants.map(&:user_id)).to match_array([first_agent.id, second_agent.id]) + end + end +end