+
@@ -80,16 +82,6 @@ export default {
max-width: 85%;
text-align: right;
- & + .user-message {
- margin-bottom: $space-micro;
- .chat-bubble {
- border-top-right-radius: $space-smaller;
- }
- }
- & + .agent-message {
- margin-top: $space-normal;
- margin-bottom: $space-micro;
- }
.message-wrap {
margin-right: $space-small;
}
@@ -103,13 +95,28 @@ export default {
padding: 0;
overflow: hidden;
}
+
.user.has-attachment {
.icon-wrap {
color: $color-white;
}
.download {
- opacity: 0.8;
+ color: $color-white;
+ }
+ }
+
+ .user-message-wrap {
+ + .user-message-wrap {
+ margin-top: $space-micro;
+
+ .user-message .chat-bubble {
+ border-top-right-radius: $space-smaller;
+ }
+ }
+
+ + .agent-message-wrap {
+ margin-top: $space-normal;
}
}
}
diff --git a/app/javascript/widget/components/UserMessageBubble.vue b/app/javascript/widget/components/UserMessageBubble.vue
index 5415aa26b..03fac6601 100755
--- a/app/javascript/widget/components/UserMessageBubble.vue
+++ b/app/javascript/widget/components/UserMessageBubble.vue
@@ -45,7 +45,7 @@ export default {
display: inline-block;
font-size: $font-size-default;
line-height: 1.5;
- padding: $space-small $space-normal;
+ padding: $space-slab $space-normal $space-slab $space-normal;
text-align: left;
> a {
diff --git a/app/javascript/widget/views/Home.vue b/app/javascript/widget/views/Home.vue
index d567d9a32..b4a0626f5 100755
--- a/app/javascript/widget/views/Home.vue
+++ b/app/javascript/widget/views/Home.vue
@@ -86,7 +86,7 @@ export default {
.header-wrap {
flex-shrink: 0;
- border-radius: $space-normal;
+ border-radius: $space-normal $space-normal $space-small $space-small;
background: white;
z-index: 99;
@include shadow-large;
diff --git a/app/listeners/action_cable_listener.rb b/app/listeners/action_cable_listener.rb
index 99c3bbce2..dddbb91a4 100644
--- a/app/listeners/action_cable_listener.rb
+++ b/app/listeners/action_cable_listener.rb
@@ -4,86 +4,88 @@ class ActionCableListener < BaseListener
def conversation_created(event)
conversation, account, timestamp = extract_conversation_and_account(event)
- send_to_agents(account, conversation.inbox.members, CONVERSATION_CREATED, conversation.push_event_data)
+ broadcast(user_tokens(account, conversation.inbox.members), CONVERSATION_CREATED, conversation.push_event_data)
end
def conversation_read(event)
conversation, account, timestamp = extract_conversation_and_account(event)
- send_to_agents(account, conversation.inbox.members, CONVERSATION_READ, conversation.push_event_data)
+ broadcast(user_tokens(account, conversation.inbox.members), CONVERSATION_READ, conversation.push_event_data)
end
def message_created(event)
message, account, timestamp = extract_message_and_account(event)
conversation = message.conversation
+ tokens = user_tokens(account, conversation.inbox.members) + contact_token(conversation.contact, message)
- send_to_agents(account, conversation.inbox.members, MESSAGE_CREATED, message.push_event_data)
- send_to_contact(conversation.contact, MESSAGE_CREATED, message)
+ broadcast(tokens, MESSAGE_CREATED, message.push_event_data)
end
def message_updated(event)
message, account, timestamp = extract_message_and_account(event)
conversation = message.conversation
contact = conversation.contact
+ tokens = user_tokens(account, conversation.inbox.members) + contact_token(conversation.contact, message)
- send_to_agents(account, conversation.inbox.members, MESSAGE_UPDATED, message.push_event_data)
- send_to_contact(contact, MESSAGE_UPDATED, message)
+ broadcast(tokens, MESSAGE_UPDATED, message.push_event_data)
end
def conversation_resolved(event)
conversation, account, timestamp = extract_conversation_and_account(event)
- send_to_agents(account, conversation.inbox.members, CONVERSATION_RESOLVED, conversation.push_event_data)
+ broadcast(user_tokens(account, conversation.inbox.members), CONVERSATION_RESOLVED, conversation.push_event_data)
end
def conversation_opened(event)
conversation, account, timestamp = extract_conversation_and_account(event)
- send_to_agents(account, conversation.inbox.members, CONVERSATION_OPENED, conversation.push_event_data)
+ broadcast(user_tokens(account, conversation.inbox.members), CONVERSATION_OPENED, conversation.push_event_data)
end
def conversation_lock_toggle(event)
conversation, account, timestamp = extract_conversation_and_account(event)
- send_to_agents(account, conversation.inbox.members, CONVERSATION_LOCK_TOGGLE, conversation.lock_event_data)
+ broadcast(user_tokens(account, conversation.inbox.members), CONVERSATION_LOCK_TOGGLE, conversation.lock_event_data)
end
def assignee_changed(event)
conversation, account, timestamp = extract_conversation_and_account(event)
- send_to_agents(account, conversation.inbox.members, ASSIGNEE_CHANGED, conversation.push_event_data)
+ broadcast(user_tokens(account, conversation.inbox.members), ASSIGNEE_CHANGED, conversation.push_event_data)
end
def contact_created(event)
contact, account, timestamp = extract_contact_and_account(event)
- send_to_agents(account, account.agents, CONTACT_CREATED, contact.push_event_data)
+ broadcast(user_tokens(account, account.agents), CONTACT_CREATED, contact.push_event_data)
end
def contact_updated(event)
contact, account, timestamp = extract_contact_and_account(event)
- send_to_agents(account, account.agents, CONTACT_UPDATED, contact.push_event_data)
+ broadcast(user_tokens(account, account.agents), CONTACT_UPDATED, contact.push_event_data)
end
private
- def send_to_agents(account, agents, event_name, data)
+ def user_tokens(account, agents)
agent_tokens = agents.pluck(:pubsub_token)
admin_tokens = account.administrators.pluck(:pubsub_token)
-
pubsub_tokens = (agent_tokens + admin_tokens).uniq
-
- return if pubsub_tokens.blank?
-
- ::ActionCableBroadcastJob.perform_later(pubsub_tokens, event_name, data)
+ pubsub_tokens
end
- def send_to_contact(contact, event_name, message)
- return if message.private?
- return if message.activity?
- return if contact.nil?
+ def contact_token(contact, message)
+ return [] if message.private?
+ return [] if message.activity?
+ return [] if contact.nil?
- ::ActionCableBroadcastJob.perform_later([contact.pubsub_token], event_name, message.push_event_data)
+ [contact.pubsub_token]
+ end
+
+ def broadcast(tokens, event_name, data)
+ return if tokens.blank?
+
+ ::ActionCableBroadcastJob.perform_later(tokens.uniq, event_name, data)
end
end
diff --git a/app/listeners/email_notification_listener.rb b/app/listeners/email_notification_listener.rb
deleted file mode 100644
index a928960b2..000000000
--- a/app/listeners/email_notification_listener.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class EmailNotificationListener < BaseListener
- def conversation_created(event)
- conversation, _account, _timestamp = extract_conversation_and_account(event)
- return if conversation.bot?
-
- conversation.inbox.members.each do |agent|
- next unless agent.notification_settings.find_by(account_id: conversation.account_id).conversation_creation?
-
- AgentNotifications::ConversationNotificationsMailer.conversation_created(conversation, agent).deliver_later
- end
- end
-end
diff --git a/app/listeners/notification_listener.rb b/app/listeners/notification_listener.rb
new file mode 100644
index 000000000..a8349993f
--- /dev/null
+++ b/app/listeners/notification_listener.rb
@@ -0,0 +1,29 @@
+class NotificationListener < BaseListener
+ def conversation_created(event)
+ conversation, account, _timestamp = extract_conversation_and_account(event)
+ return if conversation.bot?
+
+ conversation.inbox.members.each do |agent|
+ NotificationBuilder.new(
+ notification_type: 'conversation_creation',
+ user: agent,
+ account: account,
+ primary_actor: conversation
+ ).perform
+ end
+ end
+
+ def assignee_changed(event)
+ conversation, account, _timestamp = extract_conversation_and_account(event)
+ assignee = conversation.assignee
+ return unless conversation.notifiable_assignee_change?
+ return if conversation.bot?
+
+ NotificationBuilder.new(
+ notification_type: 'conversation_assignment',
+ user: assignee,
+ account: account,
+ primary_actor: conversation
+ ).perform
+ end
+end
diff --git a/app/mailers/agent_notifications/conversation_notifications_mailer.rb b/app/mailers/agent_notifications/conversation_notifications_mailer.rb
index 98650013c..c1d3b7c55 100644
--- a/app/mailers/agent_notifications/conversation_notifications_mailer.rb
+++ b/app/mailers/agent_notifications/conversation_notifications_mailer.rb
@@ -2,7 +2,7 @@ class AgentNotifications::ConversationNotificationsMailer < ApplicationMailer
default from: ENV.fetch('MAILER_SENDER_EMAIL', 'accounts@chatwoot.com')
layout 'mailer'
- def conversation_created(conversation, agent)
+ def conversation_creation(conversation, agent)
return unless smtp_config_set_or_development?
@agent = agent
@@ -11,7 +11,7 @@ class AgentNotifications::ConversationNotificationsMailer < ApplicationMailer
mail(to: @agent.email, subject: subject)
end
- def conversation_assigned(conversation, agent)
+ def conversation_assignment(conversation, agent)
return unless smtp_config_set_or_development?
@agent = agent
diff --git a/app/models/account_user.rb b/app/models/account_user.rb
index e86f1d3e2..6fe575f35 100644
--- a/app/models/account_user.rb
+++ b/app/models/account_user.rb
@@ -37,7 +37,8 @@ class AccountUser < ApplicationRecord
def create_notification_setting
setting = user.notification_settings.new(account_id: account.id)
- setting.selected_email_flags = [:conversation_assignment]
+ setting.selected_email_flags = [:email_conversation_assignment]
+ setting.selected_push_flags = [:push_conversation_assignment]
setting.save!
end
diff --git a/app/models/conversation.rb b/app/models/conversation.rb
index 4d2a2f446..a6cc6b68e 100644
--- a/app/models/conversation.rb
+++ b/app/models/conversation.rb
@@ -53,7 +53,7 @@ class Conversation < ApplicationRecord
before_create :set_bot_conversation
- after_update :notify_status_change, :create_activity, :send_email_notification_to_assignee
+ after_update :notify_status_change, :create_activity
after_create :notify_conversation_creation, :run_round_robin
@@ -105,16 +105,6 @@ class Conversation < ApplicationRecord
}
end
- private
-
- def set_bot_conversation
- self.status = :bot if inbox.agent_bot_inbox&.active?
- end
-
- def notify_conversation_creation
- dispatcher_dispatch(CONVERSATION_CREATED)
- end
-
def notifiable_assignee_change?
return false if self_assign?(assignee_id)
return false unless saved_change_to_assignee_id?
@@ -123,12 +113,14 @@ class Conversation < ApplicationRecord
true
end
- def send_email_notification_to_assignee
- return unless notifiable_assignee_change?
- return if assignee.notification_settings.find_by(account_id: account_id).not_conversation_assignment?
- return if bot?
+ private
- AgentNotifications::ConversationNotificationsMailer.conversation_assigned(self, assignee).deliver_later
+ def set_bot_conversation
+ self.status = :bot if inbox.agent_bot_inbox&.active?
+ end
+
+ def notify_conversation_creation
+ dispatcher_dispatch(CONVERSATION_CREATED)
end
def self_assign?(assignee_id)
diff --git a/app/models/message.rb b/app/models/message.rb
index 907f55eb9..99572a751 100644
--- a/app/models/message.rb
+++ b/app/models/message.rb
@@ -75,7 +75,7 @@ class Message < ApplicationRecord
:notify_via_mail
# we need to wait for the active storage attachments to be available
- after_create_commit :dispatch_event, :send_reply
+ after_create_commit :dispatch_create_events, :send_reply
after_update :dispatch_update_event
@@ -117,7 +117,7 @@ class Message < ApplicationRecord
private
- def dispatch_event
+ def dispatch_create_events
Rails.configuration.dispatcher.dispatch(MESSAGE_CREATED, Time.zone.now, message: self)
if outgoing? && conversation.messages.outgoing.count == 1
diff --git a/app/models/notification.rb b/app/models/notification.rb
new file mode 100644
index 000000000..2c0679d57
--- /dev/null
+++ b/app/models/notification.rb
@@ -0,0 +1,47 @@
+# == Schema Information
+#
+# Table name: notifications
+#
+# id :bigint not null, primary key
+# notification_type :integer not null
+# primary_actor_type :string not null
+# read_at :datetime
+# secondary_actor_type :string
+# created_at :datetime not null
+# updated_at :datetime not null
+# account_id :bigint not null
+# primary_actor_id :bigint not null
+# secondary_actor_id :bigint
+# user_id :bigint not null
+#
+# Indexes
+#
+# index_notifications_on_account_id (account_id)
+# index_notifications_on_user_id (user_id)
+# uniq_primary_actor_per_account_notifications (primary_actor_type,primary_actor_id)
+# uniq_secondary_actor_per_account_notifications (secondary_actor_type,secondary_actor_id)
+#
+
+class Notification < ApplicationRecord
+ belongs_to :account
+ belongs_to :user
+
+ belongs_to :primary_actor, polymorphic: true
+ belongs_to :secondary_actor, polymorphic: true, optional: true
+
+ NOTIFICATION_TYPES = {
+ conversation_creation: 1,
+ conversation_assignment: 2
+ }.freeze
+
+ enum notification_type: NOTIFICATION_TYPES
+
+ after_create_commit :process_notification_delivery
+
+ private
+
+ def process_notification_delivery
+ Notification::EmailNotificationService.new(notification: self).perform
+ # Notification::PushNotificationService.new(notification: self).perform
+ end
+end
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index 8a2cf8246..d31d003a5 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -4,6 +4,7 @@
#
# id :bigint not null, primary key
# email_flags :integer default(0), not null
+# push_flags :integer default(0), not null
# created_at :datetime not null
# updated_at :datetime not null
# account_id :integer
@@ -25,10 +26,9 @@ class NotificationSetting < ApplicationRecord
flag_query_mode: :bit_operator
}.freeze
- EMAIL_NOTIFICATION_FLAGS = {
- 1 => :conversation_creation,
- 2 => :conversation_assignment
- }.freeze
+ EMAIL_NOTIFICATION_FLAGS = ::Notification::NOTIFICATION_TYPES.transform_keys { |key| "email_#{key}".to_sym }.invert.freeze
+ PUSH_NOTIFICATION_FLAGS = ::Notification::NOTIFICATION_TYPES.transform_keys { |key| "push_#{key}".to_sym }.invert.freeze
has_flags EMAIL_NOTIFICATION_FLAGS.merge(column: 'email_flags').merge(DEFAULT_QUERY_SETTING)
+ has_flags PUSH_NOTIFICATION_FLAGS.merge(column: 'push_flags').merge(DEFAULT_QUERY_SETTING)
end
diff --git a/app/models/notification_subscription.rb b/app/models/notification_subscription.rb
new file mode 100644
index 000000000..b03ad7a17
--- /dev/null
+++ b/app/models/notification_subscription.rb
@@ -0,0 +1,26 @@
+# == Schema Information
+#
+# Table name: notification_subscriptions
+#
+# id :bigint not null, primary key
+# subscription_attributes :jsonb not null
+# subscription_type :integer not null
+# created_at :datetime not null
+# updated_at :datetime not null
+# user_id :bigint not null
+#
+# Indexes
+#
+# index_notification_subscriptions_on_user_id (user_id)
+#
+
+class NotificationSubscription < ApplicationRecord
+ belongs_to :user
+
+ SUBSCRIPTION_TYPES = {
+ browser_push: 1,
+ gcm: 2
+ }.freeze
+
+ enum subscription_type: SUBSCRIPTION_TYPES
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index 7779c9ba6..06c339587 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -67,7 +67,10 @@ class User < ApplicationRecord
has_many :assigned_inboxes, through: :inbox_members, source: :inbox
has_many :messages
has_many :invitees, through: :account_users, class_name: 'User', foreign_key: 'inviter_id', dependent: :nullify
+
+ has_many :notifications, dependent: :destroy
has_many :notification_settings, dependent: :destroy
+ has_many :notification_subscriptions, dependent: :destroy
before_validation :set_password_and_uid, on: :create
@@ -119,12 +122,6 @@ class User < ApplicationRecord
Rails.configuration.dispatcher.dispatch(AGENT_ADDED, Time.zone.now, account: account)
end
- def create_notification_setting
- setting = notification_settings.new(account_id: account.id)
- setting.selected_email_flags = [:conversation_assignment]
- setting.save!
- end
-
def notify_deletion
Rails.configuration.dispatcher.dispatch(AGENT_REMOVED, Time.zone.now, account: account)
end
diff --git a/app/services/notification/email_notification_service.rb b/app/services/notification/email_notification_service.rb
new file mode 100644
index 000000000..0fb8d420f
--- /dev/null
+++ b/app/services/notification/email_notification_service.rb
@@ -0,0 +1,20 @@
+class Notification::EmailNotificationService
+ pattr_initialize [:notification!]
+
+ def perform
+ return unless user_subscribed_to_notification?
+
+ # TODO : Clean up whatever happening over here
+ AgentNotifications::ConversationNotificationsMailer.public_send(notification
+ .notification_type.to_s, notification.primary_actor, notification.user).deliver_later
+ end
+
+ private
+
+ def user_subscribed_to_notification?
+ notification_setting = notification.user.notification_settings.find_by(account_id: notification.account.id)
+ return true if notification_setting.public_send("email_#{notification.notification_type}?")
+
+ false
+ end
+end
diff --git a/app/services/notification/push_notification_service.rb b/app/services/notification/push_notification_service.rb
new file mode 100644
index 000000000..3d8f3d1ce
--- /dev/null
+++ b/app/services/notification/push_notification_service.rb
@@ -0,0 +1,17 @@
+class Notification::PushNotificationService
+ pattr_initialize [:notification!]
+
+ def perform
+ return unless user_subscribed_to_notification?
+ # TODO: implement the push delivery logic here
+ end
+
+ private
+
+ def user_subscribed_to_notification?
+ notification_setting = notification.user.notification_settings.find_by(account_id: notification.account.id)
+ return true if notification_setting.public_send("push_#{notification.notification_type}?")
+
+ false
+ end
+end
diff --git a/app/views/api/v1/accounts/conversations/messages/index.json.jbuilder b/app/views/api/v1/accounts/conversations/messages/index.json.jbuilder
index 64333d3ec..e04a7dd9c 100644
--- a/app/views/api/v1/accounts/conversations/messages/index.json.jbuilder
+++ b/app/views/api/v1/accounts/conversations/messages/index.json.jbuilder
@@ -1,7 +1,9 @@
json.meta do
json.labels @conversation.label_list
json.additional_attributes @conversation.additional_attributes
- json.contact_id @conversation.contact_id
+ json.contact @conversation.contact.push_event_data
+ json.assignee @conversation.assignee.push_event_data if @conversation.assignee.present?
+ json.agent_last_seen_at @conversation.agent_last_seen_at
end
json.payload do
diff --git a/app/views/mailers/agent_notifications/conversation_notifications_mailer/conversation_assigned.html.erb b/app/views/mailers/agent_notifications/conversation_notifications_mailer/conversation_assignment.html.erb
similarity index 100%
rename from app/views/mailers/agent_notifications/conversation_notifications_mailer/conversation_assigned.html.erb
rename to app/views/mailers/agent_notifications/conversation_notifications_mailer/conversation_assignment.html.erb
diff --git a/app/views/mailers/agent_notifications/conversation_notifications_mailer/conversation_created.html.erb b/app/views/mailers/agent_notifications/conversation_notifications_mailer/conversation_creation.html.erb
similarity index 100%
rename from app/views/mailers/agent_notifications/conversation_notifications_mailer/conversation_created.html.erb
rename to app/views/mailers/agent_notifications/conversation_notifications_mailer/conversation_creation.html.erb
diff --git a/config/routes.rb b/config/routes.rb
index 1ec832e96..3ecb582d4 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -76,6 +76,7 @@ Rails.application.routes.draw do
end
end
+ resources :notifications, only: [:index, :update]
resource :notification_settings, only: [:show, :update]
resources :reports, only: [] do
@@ -103,6 +104,7 @@ Rails.application.routes.draw do
# ----------------------------------
resource :profile, only: [:show, :update]
+ resource :notification_subscriptions, only: [:create]
resources :agent_bots, only: [:index]
diff --git a/db/migrate/20200422130153_create_notifications.rb b/db/migrate/20200422130153_create_notifications.rb
new file mode 100644
index 000000000..cace1f981
--- /dev/null
+++ b/db/migrate/20200422130153_create_notifications.rb
@@ -0,0 +1,34 @@
+class CreateNotifications < ActiveRecord::Migration[6.0]
+ def change
+ create_table :notifications do |t|
+ t.references :account, index: true, null: false
+ t.references :user, index: true, null: false
+ t.integer :notification_type, null: false
+ t.references :primary_actor, polymorphic: true, null: false, index: { name: 'uniq_primary_actor_per_account_notifications' }
+ t.references :secondary_actor, polymorphic: true, index: { name: 'uniq_secondary_actor_per_account_notifications' }
+ t.timestamp :read_at, default: nil
+
+ t.timestamps
+ end
+
+ create_table :notification_subscriptions do |t|
+ t.references :user, index: true, null: false
+ t.integer :subscription_type, null: false
+ t.jsonb :subscription_attributes, null: false, default: '{}'
+ t.timestamps
+ end
+
+ add_column :notification_settings, :push_flags, :integer, default: 0, null: false
+ add_push_settings_to_users
+ end
+
+ def add_push_settings_to_users
+ ::User.find_in_batches do |users_batch|
+ users_batch.each do |user|
+ user_notification_setting = user.notification_settings.first
+ user_notification_setting.push_conversation_assignment = true
+ user_notification_setting.save!
+ end
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f975edd61..56ea99f24 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -273,9 +273,36 @@ ActiveRecord::Schema.define(version: 2020_04_29_082655) do
t.integer "email_flags", default: 0, null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
+ t.integer "push_flags", default: 0, null: false
t.index ["account_id", "user_id"], name: "by_account_user", unique: true
end
+ create_table "notification_subscriptions", force: :cascade do |t|
+ t.bigint "user_id", null: false
+ t.integer "subscription_type", null: false
+ t.jsonb "subscription_attributes", default: "{}", null: false
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["user_id"], name: "index_notification_subscriptions_on_user_id"
+ end
+
+ create_table "notifications", force: :cascade do |t|
+ t.bigint "account_id", null: false
+ t.bigint "user_id", null: false
+ t.integer "notification_type", null: false
+ t.string "primary_actor_type", null: false
+ t.bigint "primary_actor_id", null: false
+ t.string "secondary_actor_type"
+ t.bigint "secondary_actor_id"
+ t.datetime "read_at"
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["account_id"], name: "index_notifications_on_account_id"
+ t.index ["primary_actor_type", "primary_actor_id"], name: "uniq_primary_actor_per_account_notifications"
+ t.index ["secondary_actor_type", "secondary_actor_id"], name: "uniq_secondary_actor_per_account_notifications"
+ t.index ["user_id"], name: "index_notifications_on_user_id"
+ end
+
create_table "subscriptions", id: :serial, force: :cascade do |t|
t.string "pricing_version"
t.integer "account_id"
diff --git a/spec/controllers/api/v1/accounts/contacts_controller_spec.rb b/spec/controllers/api/v1/accounts/contacts_controller_spec.rb
index d4050078a..c3daf99ed 100644
--- a/spec/controllers/api/v1/accounts/contacts_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/contacts_controller_spec.rb
@@ -56,10 +56,10 @@ RSpec.describe 'Contacts API', type: :request do
let(:valid_params) { { contact: { account_id: account.id } } }
context 'when it is an unauthenticated user' do
- it 'creates the contact' do
- expect { post "/api/v1/accounts/#{account.id}/contacts", params: valid_params }.to change(Contact, :count).by(1)
+ it 'returns unauthorized' do
+ expect { post "/api/v1/accounts/#{account.id}/contacts", params: valid_params }.to change(Contact, :count).by(0)
- expect(response).to have_http_status(:success)
+ expect(response).to have_http_status(:unauthorized)
end
end
diff --git a/spec/controllers/api/v1/accounts/conversations/messages_controller_spec.rb b/spec/controllers/api/v1/accounts/conversations/messages_controller_spec.rb
index 6137a7298..7154ef47a 100644
--- a/spec/controllers/api/v1/accounts/conversations/messages_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/conversations/messages_controller_spec.rb
@@ -116,7 +116,7 @@ RSpec.describe 'Conversation Messages API', type: :request do
as: :json
expect(response).to have_http_status(:success)
- expect(JSON.parse(response.body, symbolize_names: true)[:meta][:contact_id]).to eq(conversation.contact_id)
+ expect(JSON.parse(response.body, symbolize_names: true)[:meta][:contact][:id]).to eq(conversation.contact_id)
end
end
end
diff --git a/spec/controllers/api/v1/accounts/notification_settings_controller_spec.rb b/spec/controllers/api/v1/accounts/notification_settings_controller_spec.rb
index d2f93c4a6..26ea66972 100644
--- a/spec/controllers/api/v1/accounts/notification_settings_controller_spec.rb
+++ b/spec/controllers/api/v1/accounts/notification_settings_controller_spec.rb
@@ -42,7 +42,7 @@ RSpec.describe 'Notification Settings API', type: :request do
it 'updates the email related notification flags' do
put "/api/v1/accounts/#{account.id}/notification_settings",
- params: { notification_settings: { selected_email_flags: ['conversation_assignment'] } },
+ params: { notification_settings: { selected_email_flags: ['email_conversation_assignment'] } },
headers: agent.create_new_auth_token,
as: :json
@@ -51,7 +51,7 @@ RSpec.describe 'Notification Settings API', type: :request do
agent.reload
expect(json_response['user_id']).to eq(agent.id)
expect(json_response['account_id']).to eq(account.id)
- expect(json_response['selected_email_flags']).to eq(['conversation_assignment'])
+ expect(json_response['selected_email_flags']).to eq(['email_conversation_assignment'])
end
end
end
diff --git a/spec/controllers/api/v1/accounts/notifications_controller_spec.rb b/spec/controllers/api/v1/accounts/notifications_controller_spec.rb
new file mode 100644
index 000000000..fac2df97b
--- /dev/null
+++ b/spec/controllers/api/v1/accounts/notifications_controller_spec.rb
@@ -0,0 +1,57 @@
+require 'rails_helper'
+
+RSpec.describe 'Notifications API', type: :request do
+ let(:account) { create(:account) }
+
+ describe 'GET /api/v1/accounts/{account.id}/notifications' do
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ get "/api/v1/accounts/#{account.id}/notifications"
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated user' do
+ let(:admin) { create(:user, account: account, role: :administrator) }
+ let!(:notification) { create(:notification, account: account, user: admin) }
+
+ it 'returns all notifications' do
+ get "/api/v1/accounts/#{account.id}/notifications",
+ headers: admin.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ expect(response.body).to include(notification.notification_type)
+ end
+ end
+ end
+
+ describe 'PATCH /api/v1/accounts/{account.id}/notifications/:id' do
+ let(:admin) { create(:user, account: account, role: :administrator) }
+ let!(:notification) { create(:notification, account: account, user: admin) }
+
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ put "/api/v1/accounts/#{account.id}/notifications/#{notification.id}",
+ params: { read_at: true }
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated user' do
+ let(:admin) { create(:user, account: account, role: :administrator) }
+
+ it 'updates the notification read at' do
+ patch "/api/v1/accounts/#{account.id}/notifications/#{notification.id}",
+ headers: admin.create_new_auth_token,
+ params: { read_at: true },
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ expect(notification.reload.read_at).not_to eq('')
+ end
+ end
+ end
+end
diff --git a/spec/controllers/api/v1/notification_subscriptions_controller_spec.rb b/spec/controllers/api/v1/notification_subscriptions_controller_spec.rb
new file mode 100644
index 000000000..00fa614d3
--- /dev/null
+++ b/spec/controllers/api/v1/notification_subscriptions_controller_spec.rb
@@ -0,0 +1,31 @@
+require 'rails_helper'
+
+RSpec.describe 'Notifications Subscriptions API', type: :request do
+ let(:account) { create(:account) }
+
+ describe 'POST /api/v1/notification_subscriptions' do
+ context 'when it is an unauthenticated user' do
+ it 'returns unauthorized' do
+ post '/api/v1/notification_subscriptions'
+
+ expect(response).to have_http_status(:unauthorized)
+ end
+ end
+
+ context 'when it is an authenticated user' do
+ let(:agent) { create(:user, account: account, role: :agent) }
+
+ it 'creates a notification subscriptions' do
+ post '/api/v1/notification_subscriptions',
+ params: { notification_subscription: { subscription_type: 'browser_push', 'subscription_attributes': { test: 'test' } } },
+ headers: agent.create_new_auth_token,
+ as: :json
+
+ expect(response).to have_http_status(:success)
+ json_response = JSON.parse(response.body)
+ expect(json_response['subscription_type']).to eq('browser_push')
+ expect(json_response['subscription_attributes']).to eq({ 'test' => 'test' })
+ end
+ end
+ end
+end
diff --git a/spec/factories/notifications.rb b/spec/factories/notifications.rb
new file mode 100644
index 000000000..63bae1119
--- /dev/null
+++ b/spec/factories/notifications.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :notification do
+ primary_actor { create(:conversation, account: account) }
+ notification_type { 'conversation_assignment' }
+ user
+ account
+ end
+end
diff --git a/spec/listeners/action_cable_listener_spec.rb b/spec/listeners/action_cable_listener_spec.rb
index 9dffe94a1..daad3cc6f 100644
--- a/spec/listeners/action_cable_listener_spec.rb
+++ b/spec/listeners/action_cable_listener_spec.rb
@@ -6,11 +6,6 @@ describe ActionCableListener do
let!(:inbox) { create(:inbox, account: account) }
let!(:agent) { create(:user, account: account, role: :agent) }
let!(:conversation) { create(:conversation, account: account, inbox: inbox, assignee: agent) }
- let!(:message) do
- create(:message, message_type: 'outgoing',
- account: account, inbox: inbox, conversation: conversation)
- end
- let!(:event) { Events::Base.new(event_name, Time.zone.now, message: message) }
before do
create(:inbox_member, inbox: inbox, user: agent)
@@ -18,13 +13,18 @@ describe ActionCableListener do
describe '#message_created' do
let(:event_name) { :'message.created' }
+ let!(:message) do
+ create(:message, message_type: 'outgoing',
+ account: account, inbox: inbox, conversation: conversation)
+ end
+ let!(:event) { Events::Base.new(event_name, Time.zone.now, message: message) }
it 'sends message to account admins, inbox agents and the contact' do
+ # HACK: to reload conversation inbox members
+ expect(conversation.inbox.reload.inbox_members.count).to eq(1)
+
expect(ActionCableBroadcastJob).to receive(:perform_later).with(
- [agent.pubsub_token, admin.pubsub_token], 'message.created', message.push_event_data
- )
- expect(ActionCableBroadcastJob).to receive(:perform_later).with(
- [conversation.contact.pubsub_token], 'message.created', message.push_event_data
+ [agent.pubsub_token, admin.pubsub_token, conversation.contact.pubsub_token], 'message.created', message.push_event_data
)
listener.message_created(event)
end
diff --git a/spec/listeners/email_notification_listener_spec.rb b/spec/listeners/notification_listener_spec.rb
similarity index 87%
rename from spec/listeners/email_notification_listener_spec.rb
rename to spec/listeners/notification_listener_spec.rb
index 22b5be07c..25048570b 100644
--- a/spec/listeners/email_notification_listener_spec.rb
+++ b/spec/listeners/notification_listener_spec.rb
@@ -1,5 +1,5 @@
require 'rails_helper'
-describe EmailNotificationListener do
+describe NotificationListener do
let(:listener) { described_class.instance }
let!(:account) { create(:account) }
let!(:user) { create(:user, account: account) }
@@ -13,14 +13,14 @@ describe EmailNotificationListener do
before do
creation_mailer = double
- allow(AgentNotifications::ConversationNotificationsMailer).to receive(:conversation_created).and_return(creation_mailer)
+ allow(AgentNotifications::ConversationNotificationsMailer).to receive(:conversation_creation).and_return(creation_mailer)
allow(creation_mailer).to receive(:deliver_later).and_return(true)
end
context 'when conversation is created' do
it 'sends email to inbox members who have notifications turned on' do
notification_setting = agent_with_notification.notification_settings.first
- notification_setting.selected_email_flags = [:conversation_creation]
+ notification_setting.selected_email_flags = [:email_conversation_creation]
notification_setting.save!
create(:inbox_member, user: agent_with_notification, inbox: inbox)
@@ -29,7 +29,7 @@ describe EmailNotificationListener do
event = Events::Base.new(event_name, Time.zone.now, conversation: conversation)
listener.conversation_created(event)
- expect(AgentNotifications::ConversationNotificationsMailer).to have_received(:conversation_created)
+ expect(AgentNotifications::ConversationNotificationsMailer).to have_received(:conversation_creation)
.with(conversation, agent_with_notification)
end
@@ -44,7 +44,7 @@ describe EmailNotificationListener do
event = Events::Base.new(event_name, Time.zone.now, conversation: conversation)
listener.conversation_created(event)
- expect(AgentNotifications::ConversationNotificationsMailer).not_to have_received(:conversation_created)
+ expect(AgentNotifications::ConversationNotificationsMailer).not_to have_received(:conversation_creation)
.with(conversation, agent_with_out_notification)
end
end
diff --git a/spec/mailers/agent_notifications/conversation_notifications_mailer_spec.rb b/spec/mailers/agent_notifications/conversation_notifications_mailer_spec.rb
index 40883b1d6..7f7227879 100644
--- a/spec/mailers/agent_notifications/conversation_notifications_mailer_spec.rb
+++ b/spec/mailers/agent_notifications/conversation_notifications_mailer_spec.rb
@@ -12,8 +12,8 @@ RSpec.describe AgentNotifications::ConversationNotificationsMailer, type: :maile
allow(class_instance).to receive(:smtp_config_set_or_development?).and_return(true)
end
- describe 'conversation_created' do
- let(:mail) { described_class.conversation_created(conversation, agent).deliver_now }
+ describe 'conversation_creation' do
+ let(:mail) { described_class.conversation_creation(conversation, agent).deliver_now }
it 'renders the subject' do
expect(mail.subject).to eq("#{agent.name}, A new conversation [ID - #{conversation
@@ -25,8 +25,8 @@ RSpec.describe AgentNotifications::ConversationNotificationsMailer, type: :maile
end
end
- describe 'conversation_assigned' do
- let(:mail) { described_class.conversation_assigned(conversation, agent).deliver_now }
+ describe 'conversation_assignment' do
+ let(:mail) { described_class.conversation_assignment(conversation, agent).deliver_now }
it 'renders the subject' do
expect(mail.subject).to eq("#{agent.name}, A new conversation [ID - #{conversation.display_id}] has been assigned to you.")
diff --git a/spec/models/account_user_spec.rb b/spec/models/account_user_spec.rb
index 54d16672b..364d61da1 100644
--- a/spec/models/account_user_spec.rb
+++ b/spec/models/account_user_spec.rb
@@ -9,8 +9,8 @@ RSpec.describe User do
it 'gets created with the right default settings' do
expect(account_user.user.notification_settings).not_to eq(nil)
- expect(account_user.user.notification_settings.first.conversation_creation?).to eq(false)
- expect(account_user.user.notification_settings.first.conversation_assignment?).to eq(true)
+ expect(account_user.user.notification_settings.first.email_conversation_creation?).to eq(false)
+ expect(account_user.user.notification_settings.first.email_conversation_assignment?).to eq(true)
end
end
end
diff --git a/spec/models/conversation_spec.rb b/spec/models/conversation_spec.rb
index 95c0852ba..4e4080e42 100644
--- a/spec/models/conversation_spec.rb
+++ b/spec/models/conversation_spec.rb
@@ -39,8 +39,6 @@ RSpec.describe Conversation, type: :model do
new_assignee
allow(Rails.configuration.dispatcher).to receive(:dispatch)
- allow(AgentNotifications::ConversationNotificationsMailer).to receive(:conversation_assigned).and_return(assignment_mailer)
- allow(assignment_mailer).to receive(:deliver_later)
Current.user = old_assignee
conversation.update(
@@ -61,11 +59,6 @@ RSpec.describe Conversation, type: :model do
.with(described_class::CONVERSATION_LOCK_TOGGLE, kind_of(Time), conversation: conversation)
expect(Rails.configuration.dispatcher).to have_received(:dispatch)
.with(described_class::ASSIGNEE_CHANGED, kind_of(Time), conversation: conversation)
-
- # send_email_notification_to_assignee
- expect(AgentNotifications::ConversationNotificationsMailer).to have_received(:conversation_assigned).with(conversation, new_assignee)
-
- expect(assignment_mailer).to have_received(:deliver_later) if ENV.fetch('SMTP_ADDRESS', nil).present?
end
it 'creates conversation activities' do
@@ -129,15 +122,28 @@ RSpec.describe Conversation, type: :model do
expect(conversation.reload.assignee).to eq(agent)
end
+ it 'send assignment mailer' do
+ allow(AgentNotifications::ConversationNotificationsMailer).to receive(:conversation_assignment).and_return(assignment_mailer)
+ allow(assignment_mailer).to receive(:deliver_later)
+
+ Current.user = conversation.assignee
+
+ expect(update_assignee).to eq(true)
+ # send_email_notification_to_assignee
+ expect(AgentNotifications::ConversationNotificationsMailer).to have_received(:conversation_assignment).with(conversation, agent)
+
+ expect(assignment_mailer).to have_received(:deliver_later) if ENV.fetch('SMTP_ADDRESS', nil).present?
+ end
+
it 'does not send assignment mailer if notification setting is turned off' do
- allow(AgentNotifications::ConversationNotificationsMailer).to receive(:conversation_assigned).and_return(assignment_mailer)
+ allow(AgentNotifications::ConversationNotificationsMailer).to receive(:conversation_assignment).and_return(assignment_mailer)
notification_setting = agent.notification_settings.first
notification_setting.unselect_all_email_flags
notification_setting.save!
expect(update_assignee).to eq(true)
- expect(AgentNotifications::ConversationNotificationsMailer).not_to have_received(:conversation_assigned).with(conversation, agent)
+ expect(AgentNotifications::ConversationNotificationsMailer).not_to have_received(:conversation_assignment).with(conversation, agent)
end
end