Merge branch 'develop' into feat/CW-3059

This commit is contained in:
Sivin Varghese
2024-02-20 21:50:58 +05:30
committed by GitHub
26 changed files with 173 additions and 47 deletions
@@ -49,6 +49,11 @@ class Api::V1::Accounts::AgentsController < Api::V1::Accounts::BaseController
Rails.logger.info "[Agent#bulk_create] ignoring email #{email}, errors: #{e.record.errors}"
end
end
# This endpoint is used to bulk create agents during onboarding
# onboarding_step key in present in Current account custom attributes, since this is a one time operation
Current.account.custom_attributes.delete('onboarding_step')
Current.account.save!
head :ok
end
@@ -46,7 +46,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
def export
column_names = params['column_names']
Account::ContactsExportJob.perform_later(Current.account.id, column_names)
Account::ContactsExportJob.perform_later(Current.account.id, column_names, Current.user.email)
head :ok, message: I18n.t('errors.contacts.export.success')
end
@@ -54,7 +54,8 @@ class Api::V1::Accounts::NotificationsController < Api::V1::Accounts::BaseContro
end
def snooze
@notification.update(snoozed_until: parse_date_time(params[:snoozed_until].to_s)) if params[:snoozed_until]
updated_meta = (@notification.meta || {}).merge('last_snoozed_at' => nil)
@notification.update(snoozed_until: parse_date_time(params[:snoozed_until].to_s), meta: updated_meta) if params[:snoozed_until]
render json: @notification
end
@@ -46,6 +46,7 @@ class Api::V1::AccountsController < Api::BaseController
def update
@account.assign_attributes(account_params.slice(:name, :locale, :domain, :support_email, :auto_resolve_duration))
@account.custom_attributes.merge!(custom_attributes_params)
@account.custom_attributes['onboarding_step'] = 'invite_team' if @account.custom_attributes['onboarding_step'] == 'account_update'
@account.save!
end
@@ -22,6 +22,7 @@ class Api::V2::AccountsController < Api::BaseController
).perform
fetch_account_and_user_info
update_account_info if @account.present?
if @user
send_auth_headers(@user)
@@ -33,6 +34,18 @@ class Api::V2::AccountsController < Api::BaseController
private
def account_attributes
{
custom_attributes: @account.custom_attributes.merge({ 'onboarding_step' => 'profile_update' })
}
end
def update_account_info
@account.update!(
account_attributes
)
end
def fetch_account_and_user_info; end
def fetch_account
+1 -1
View File
@@ -55,7 +55,7 @@ class DashboardController < ActionController::Base
VAPID_PUBLIC_KEY: VapidService.public_key,
ENABLE_ACCOUNT_SIGNUP: GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false'),
FB_APP_ID: GlobalConfigService.load('FB_APP_ID', ''),
FACEBOOK_API_VERSION: 'v14.0',
FACEBOOK_API_VERSION: GlobalConfigService.load('FACEBOOK_API_VERSION', 'v17.0'),
IS_ENTERPRISE: ChatwootApp.enterprise?,
AZURE_APP_ID: ENV.fetch('AZURE_APP_ID', ''),
GIT_SHA: GIT_HASH
@@ -34,7 +34,7 @@ class SuperAdmin::AppConfigsController < SuperAdmin::ApplicationController
def allowed_configs
@allowed_configs = case @config
when 'facebook'
%w[FB_APP_ID FB_VERIFY_TOKEN FB_APP_SECRET IG_VERIFY_TOKEN ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT]
%w[FB_APP_ID FB_VERIFY_TOKEN FB_APP_SECRET IG_VERIFY_TOKEN FACEBOOK_API_VERSION ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT]
when 'email'
['MAILER_INBOUND_EMAIL_DOMAIN']
else
@@ -79,7 +79,13 @@
"TITLE": "Export Contacts",
"DESC": "Export contacts to a CSV file.",
"SUCCESS_MESSAGE": "Export is in progress. You will be notified on email when the export file is ready to download.",
"ERROR_MESSAGE": "There was an error, please try again"
"ERROR_MESSAGE": "There was an error, please try again",
"CONFIRM": {
"TITLE": "Export Contacts",
"MESSAGE": "Are you sure you want to export all contacts?",
"YES": "Yes, Export",
"NO": "No, Cancel"
}
},
"DELETE_NOTE": {
"CONFIRM": {
@@ -113,6 +113,13 @@
</woot-button>
</div>
</div>
<woot-confirm-modal
ref="confirmExportContactsDialog"
:title="$t('EXPORT_CONTACTS.CONFIRM.TITLE')"
:description="$t('EXPORT_CONTACTS.CONFIRM.MESSAGE')"
:confirm-label="$t('EXPORT_CONTACTS.CONFIRM.YES')"
:cancel-label="$t('EXPORT_CONTACTS.CONFIRM.NO')"
/>
</header>
</template>
@@ -175,8 +182,13 @@ export default {
toggleImport() {
this.$emit('on-toggle-import');
},
submitExport() {
this.$emit('on-export-submit');
async submitExport() {
const ok =
await this.$refs.confirmExportContactsDialog.showConfirmation();
if (ok) {
this.$emit('on-export-submit');
}
},
submitSearch() {
this.$emit('on-search-submit');
+2 -2
View File
@@ -1,13 +1,13 @@
class Account::ContactsExportJob < ApplicationJob
queue_as :low
def perform(account_id, column_names)
def perform(account_id, column_names, email_to)
account = Account.find(account_id)
headers = valid_headers(column_names)
generate_csv(account, headers)
file_url = account_contact_export_url(account)
AdministratorNotifications::ChannelNotificationsMailer.with(account: account).contact_export_complete(file_url)&.deliver_later
AdministratorNotifications::ChannelNotificationsMailer.with(account: account).contact_export_complete(file_url, email_to)&.deliver_later
end
def generate_csv(account, headers)
@@ -2,9 +2,24 @@ class Notification::ReopenSnoozedNotificationsJob < ApplicationJob
queue_as :low
def perform
# rubocop:disable Rails/SkipsModelValidations
Notification.where(snoozed_until: 3.days.ago..Time.current)
.update_all(snoozed_until: nil, updated_at: Time.current, last_activity_at: Time.current, read_at: nil)
# rubocop:enable Rails/SkipsModelValidations
Notification.where(snoozed_until: 3.days.ago..Time.current).find_in_batches(batch_size: 100) do |notifications_batch|
notifications_batch.each do |notification|
update_notification(notification)
end
end
end
private
def update_notification(notification)
updated_meta = (notification.meta || {}).merge('last_snoozed_at' => notification.snoozed_until)
notification.update!(
snoozed_until: nil,
updated_at: Time.current,
last_activity_at: Time.current,
meta: updated_meta,
read_at: nil
)
end
end
@@ -60,12 +60,13 @@ class AdministratorNotifications::ChannelNotificationsMailer < ApplicationMailer
send_mail_with_liquid(to: admin_emails, subject: subject) and return
end
def contact_export_complete(file_url)
def contact_export_complete(file_url, email_to)
return unless smtp_config_set_or_development?
@action_url = file_url
subject = "Your contact's export file is available to download."
send_mail_with_liquid(to: admin_emails, subject: subject) and return
send_mail_with_liquid(to: email_to, subject: subject) and return
end
private
+2 -9
View File
@@ -64,24 +64,17 @@ class Notification < ApplicationRecord
created_at: created_at.to_i,
last_activity_at: last_activity_at.to_i,
snoozed_until: snoozed_until,
meta: meta,
account_id: account_id
}
if primary_actor.present?
payload[:primary_actor] = primary_actor_data
payload[:primary_actor] = primary_actor&.push_event_data
payload[:push_message_title] = push_message_title
end
payload
end
def primary_actor_data
{
id: primary_actor.push_event_data[:id],
meta: primary_actor.push_event_data[:meta],
inbox_id: primary_actor.push_event_data[:inbox_id]
}
end
def fcm_push_data
{
id: id,
@@ -21,6 +21,7 @@ json.data do
json.created_at notification.created_at.to_i
json.last_activity_at notification.last_activity_at.to_i
json.snoozed_until notification.snoozed_until
json.meta notification.meta
end
end
end
@@ -6,6 +6,11 @@ if resource.custom_attributes.present?
json.subscribed_quantity resource.custom_attributes['subscribed_quantity']
json.subscription_status resource.custom_attributes['subscription_status']
json.subscription_ends_on resource.custom_attributes['subscription_ends_on']
json.industry resource.custom_attributes['industry'] if resource.custom_attributes['industry'].present?
json.company_size resource.custom_attributes['company_size'] if resource.custom_attributes['company_size'].present?
json.timezone resource.custom_attributes['timezone'] if resource.custom_attributes['timezone'].present?
json.logo resource.custom_attributes['logo'] if resource.custom_attributes['logo'].present?
json.onboarding_step resource.custom_attributes['onboarding_step'] if resource.custom_attributes['onboarding_step'].present?
end
end
json.domain @account.domain
+5
View File
@@ -110,6 +110,11 @@
display_title: 'Instagram Verify Token'
description: 'The verify token used for Instagram Webhook'
locked: false
- name: FACEBOOK_API_VERSION
display_title: 'Facebook API Version'
description: 'Configure this if you want to use a different Facebook API version. Make sure its prefixed with `v`'
value: 'v17.0'
locked: false
- name: ENABLE_MESSENGER_CHANNEL_HUMAN_AGENT
display_title: 'Enable human agent'
value: false
@@ -2,12 +2,11 @@ module Enterprise::Api::V2::AccountsController
private
def fetch_account_and_user_info
data = fetch_from_clearbit
@data = fetch_from_clearbit
return if data.blank?
return if @data.blank?
update_user_info(data)
update_account_info(data)
update_user_info
end
def fetch_from_clearbit
@@ -17,19 +16,25 @@ module Enterprise::Api::V2::AccountsController
nil
end
def update_user_info(data)
@user.update!(name: data[:name])
def update_user_info
@user.update!(name: @data[:name]) if @data[:name].present?
end
def update_account_info(data)
@account.update!(
name: data[:company_name],
custom_attributes: @account.custom_attributes.merge(
'industry' => data[:industry],
'company_size' => data[:company_size],
'timezone' => data[:timezone],
'logo' => data[:logo]
)
def data_from_clearbit
return {} if @data.blank?
{ name: @data[:company_name],
custom_attributes: {
'industry' => @data[:industry],
'company_size' => @data[:company_size],
'timezone' => @data[:timezone],
'logo' => @data[:logo]
} }
end
def account_attributes
super.deep_merge(
data_from_clearbit
)
end
end
@@ -197,7 +197,7 @@ RSpec.describe 'Contacts API', type: :request do
let(:admin) { create(:user, account: account, role: :administrator) }
it 'enqueues a contact export job' do
expect(Account::ContactsExportJob).to receive(:perform_later).with(account.id, nil).once
expect(Account::ContactsExportJob).to receive(:perform_later).with(account.id, nil, admin.email).once
get "/api/v1/accounts/#{account.id}/contacts/export",
headers: admin.create_new_auth_token,
@@ -207,7 +207,7 @@ RSpec.describe 'Contacts API', type: :request do
end
it 'enqueues a contact export job with sent_columns' do
expect(Account::ContactsExportJob).to receive(:perform_later).with(account.id, %w[phone_number email]).once
expect(Account::ContactsExportJob).to receive(:perform_later).with(account.id, %w[phone_number email], admin.email).once
get "/api/v1/accounts/#{account.id}/contacts/export",
headers: admin.create_new_auth_token,
@@ -29,6 +29,7 @@ RSpec.describe 'Notifications API', type: :request do
expect(response_json['data']['meta']['count']).to eq 2
# notification appear in descending order
expect(response_json['data']['payload'].first['id']).to eq notification2.id
expect(response_json['data']['payload'].first['primary_actor']).not_to be_nil
end
end
end
@@ -178,6 +179,7 @@ RSpec.describe 'Notifications API', type: :request do
expect(response).to have_http_status(:success)
expect(notification.reload.snoozed_until).not_to eq('')
expect(notification.reload.meta['last_snoozed_at']).to be_nil
end
end
end
@@ -213,6 +213,25 @@ RSpec.describe 'Accounts API', type: :request do
end
end
it 'updates onboarding step to invite_team if onboarding step is present in account custom attributes' do
account.update(custom_attributes: { onboarding_step: 'account_update' })
put "/api/v1/accounts/#{account.id}",
params: params,
headers: admin.create_new_auth_token,
as: :json
expect(account.reload.custom_attributes['onboarding_step']).to eq('invite_team')
end
it 'will not update onboarding step if onboarding step is not present in account custom attributes' do
put "/api/v1/accounts/#{account.id}",
params: params,
headers: admin.create_new_auth_token,
as: :json
expect(account.reload.custom_attributes['onboarding_step']).to be_nil
end
it 'Throws error 422' do
params[:name] = 'test' * 999
@@ -30,6 +30,20 @@ RSpec.describe 'Accounts API', type: :request do
end
end
it 'updates the onboarding step in custom attributes' do
with_modified_env ENABLE_ACCOUNT_SIGNUP: 'true' do
allow(account_builder).to receive(:perform).and_return([user, account])
params = { email: email, user: nil, locale: nil, password: 'Password1!' }
post api_v2_accounts_url,
params: params,
as: :json
expect(account.reload.custom_attributes['onboarding_step']).to eq('profile_update')
end
end
it 'calls ChatwootCaptcha' do
with_modified_env ENABLE_ACCOUNT_SIGNUP: 'true' do
captcha = double
@@ -41,5 +41,15 @@ RSpec.describe 'Agents API', type: :request do
expect(response.body).to include('Account limit exceeded. Please purchase more licenses')
end
end
context 'when onboarding step is present in account custom attributes' do
it 'removes onboarding step from account custom attributes' do
account.update(custom_attributes: { onboarding_step: 'completed' })
post "/api/v1/accounts/#{account.id}/agents/bulk_create", params: bulk_create_params, headers: admin.create_new_auth_token
expect(account.reload.custom_attributes).not_to include('onboarding_step')
end
end
end
end
@@ -51,6 +51,22 @@ RSpec.describe Enterprise::Api::V2::AccountsController, type: :request do
end
end
it 'updates the onboarding step in custom attributes' do
with_modified_env ENABLE_ACCOUNT_SIGNUP: 'true' do
allow(account_builder).to receive(:perform).and_return([user, account])
params = { email: email, user: nil, locale: nil, password: 'Password1!' }
post api_v2_accounts_url,
params: params,
as: :json
custom_attributes = account.custom_attributes
expect(custom_attributes['onboarding_step']).to eq('profile_update')
end
end
it 'handles errors when fetching data from clearbit' do
with_modified_env ENABLE_ACCOUNT_SIGNUP: 'true' do
allow(account_builder).to receive(:perform).and_return([user, account])
@@ -24,17 +24,17 @@ RSpec.describe Account::ContactsExportJob do
allow(AdministratorNotifications::ChannelNotificationsMailer).to receive(:with).with(account: account).and_return(mailer)
allow(mailer).to receive(:contact_export_complete)
described_class.perform_now(account.id, [])
described_class.perform_now(account.id, [], 'test@test.com')
file_url = Rails.application.routes.url_helpers.rails_blob_url(account.contacts_export)
expect(account.contacts_export).to be_present
expect(file_url).to be_present
expect(mailer).to have_received(:contact_export_complete).with(file_url)
expect(mailer).to have_received(:contact_export_complete).with(file_url, 'test@test.com')
end
it 'generates valid data export file' do
described_class.perform_now(account.id, [])
described_class.perform_now(account.id, [], 'test@test.com')
csv_data = CSV.parse(account.contacts_export.download, headers: true)
emails = csv_data.pluck('email')
@@ -14,10 +14,13 @@ RSpec.describe Notification::ReopenSnoozedNotificationsJob do
it 'reopens snoozed notifications whose snooze until has passed' do
described_class.perform_now
snoozed_until = snoozed_till_5_minutes_ago.reload.snoozed_until
expect(snoozed_till_5_minutes_ago.reload.snoozed_until).to be_nil
expect(snoozed_till_tomorrow.reload.snoozed_until.to_date).to eq 1.day.from_now.to_date
expect(snoozed_indefinitely.reload.snoozed_until).to be_nil
expect(snoozed_indefinitely.reload.read_at).to be_nil
expect(snoozed_until).to eq(snoozed_till_5_minutes_ago.reload.meta['snoozed_until'])
end
end
end
@@ -10,7 +10,6 @@ RSpec.describe AdministratorNotifications::ChannelNotificationsMailer do
before do
allow(described_class).to receive(:new).and_return(class_instance)
allow(class_instance).to receive(:smtp_config_set_or_development?).and_return(true)
Account::ContactsExportJob.perform_now(account.id, [])
end
describe 'slack_disconnect' do
@@ -92,8 +91,8 @@ RSpec.describe AdministratorNotifications::ChannelNotificationsMailer do
end
describe 'contact_export_complete' do
let!(:file_url) { Rails.application.routes.url_helpers.rails_blob_url(account.contacts_export) }
let(:mail) { described_class.with(account: account).contact_export_complete(file_url).deliver_now }
let!(:file_url) { 'http://test.com/test' }
let(:mail) { described_class.with(account: account).contact_export_complete(file_url, administrator.email).deliver_now }
it 'renders the subject' do
expect(mail.subject).to eq("Your contact's export file is available to download.")