From 27419eef66de5dd0aba860783cab02412d14a24a Mon Sep 17 00:00:00 2001 From: Pranav Raj S Date: Thu, 20 Jul 2023 12:01:22 -0700 Subject: [PATCH 1/7] feat: Add report on customer waiting time (#7545) --- app/builders/v2/report_builder.rb | 5 +- app/helpers/report_helper.rb | 17 +++ .../dashboard/i18n/locale/en/report.json | 4 + .../dashboard/mixins/reportMixin.js | 22 ++-- .../dashboard/settings/reports/Index.vue | 2 + .../settings/reports/ReportContainer.vue | 2 + .../reports/components/WootReports.vue | 2 + .../dashboard/settings/reports/constants.js | 102 +++++++----------- .../dashboard/store/modules/reports.js | 3 + 9 files changed, 79 insertions(+), 80 deletions(-) diff --git a/app/builders/v2/report_builder.rb b/app/builders/v2/report_builder.rb index b0f6ba25edddc..c96950ee91e3d 100644 --- a/app/builders/v2/report_builder.rb +++ b/app/builders/v2/report_builder.rb @@ -20,7 +20,7 @@ def timeseries # For backward compatible with old report def build - if %w[avg_first_response_time avg_resolution_time].include?(params[:metric]) + if %w[avg_first_response_time avg_resolution_time reply_time].include?(params[:metric]) timeseries.each_with_object([]) do |p, arr| arr << { value: p[1], timestamp: p[0].in_time_zone(@timezone).to_i, count: @grouped_values.count[p[0]] } end @@ -38,7 +38,8 @@ def summary outgoing_messages_count: outgoing_messages.count, avg_first_response_time: avg_first_response_time_summary, avg_resolution_time: avg_resolution_time_summary, - resolutions_count: resolutions.count + resolutions_count: resolutions.count, + reply_time: reply_time_summary } end diff --git a/app/helpers/report_helper.rb b/app/helpers/report_helper.rb index 796986e5bce1f..2b0f43b3fc0ec 100644 --- a/app/helpers/report_helper.rb +++ b/app/helpers/report_helper.rb @@ -56,6 +56,13 @@ def avg_first_response_time grouped_reporting_events.average(:value) end + def reply_time + grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'reply_time', account_id: account.id)) + return grouped_reporting_events.average(:value_in_business_hours) if params[:business_hours] + + grouped_reporting_events.average(:value) + end + def avg_resolution_time grouped_reporting_events = (get_grouped_values scope.reporting_events.where(name: 'conversation_resolved', account_id: account.id)) return grouped_reporting_events.average(:value_in_business_hours) if params[:business_hours] @@ -77,6 +84,16 @@ def avg_resolution_time_summary avg_rt end + def reply_time_summary + reporting_events = scope.reporting_events + .where(name: 'reply_time', account_id: account.id, created_at: range) + reply_time = params[:business_hours] ? reporting_events.average(:value_in_business_hours) : reporting_events.average(:value) + + return 0 if reply_time.blank? + + reply_time + end + def avg_first_response_time_summary reporting_events = scope.reporting_events .where(name: 'first_response', account_id: account.id, created_at: range) diff --git a/app/javascript/dashboard/i18n/locale/en/report.json b/app/javascript/dashboard/i18n/locale/en/report.json index f384adad79bb1..0b234ec165a9b 100644 --- a/app/javascript/dashboard/i18n/locale/en/report.json +++ b/app/javascript/dashboard/i18n/locale/en/report.json @@ -34,6 +34,10 @@ "RESOLUTION_COUNT": { "NAME": "Resolution Count", "DESC": "( Total )" + }, + "REPLY_TIME": { + "NAME": "Customer waiting time", + "TOOLTIP_TEXT": "Waiting time is %{metricValue} (based on %{conversationCount} conversations)" } }, "DATE_RANGE_OPTIONS": { diff --git a/app/javascript/dashboard/mixins/reportMixin.js b/app/javascript/dashboard/mixins/reportMixin.js index 3a53f1196ed09..2b8a5f87d68fb 100644 --- a/app/javascript/dashboard/mixins/reportMixin.js +++ b/app/javascript/dashboard/mixins/reportMixin.js @@ -7,19 +7,13 @@ export default { accountSummary: 'getAccountSummary', accountReport: 'getAccountReports', }), - calculateTrend() { - return metric_key => { - if (!this.accountSummary.previous[metric_key]) return 0; - const diff = - this.accountSummary[metric_key] - - this.accountSummary.previous[metric_key]; - return Math.round( - (diff / this.accountSummary.previous[metric_key]) * 100 - ); - }; - }, }, methods: { + calculateTrend(key) { + if (!this.accountSummary.previous[key]) return 0; + const diff = this.accountSummary[key] - this.accountSummary.previous[key]; + return Math.round((diff / this.accountSummary.previous[key]) * 100); + }, displayMetric(key) { if (this.isAverageMetricType(key)) { return formatTime(this.accountSummary[key]); @@ -39,7 +33,11 @@ export default { return ''; }, isAverageMetricType(key) { - return ['avg_first_response_time', 'avg_resolution_time'].includes(key); + return [ + 'avg_first_response_time', + 'avg_resolution_time', + 'reply_time', + ].includes(key); }, }, }; diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/Index.vue b/app/javascript/dashboard/routes/dashboard/settings/reports/Index.vue index b04216bff0cb1..4ab4a82169788 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/reports/Index.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/reports/Index.vue @@ -35,6 +35,7 @@ const REPORTS_KEYS = { FIRST_RESPONSE_TIME: 'avg_first_response_time', RESOLUTION_TIME: 'avg_resolution_time', RESOLUTION_COUNT: 'resolutions_count', + REPLY_TIME: 'reply_time', }; export default { @@ -78,6 +79,7 @@ export default { 'FIRST_RESPONSE_TIME', 'RESOLUTION_TIME', 'RESOLUTION_COUNT', + 'REPLY_TIME', ].forEach(async key => { try { await this.$store.dispatch('fetchAccountReport', { diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/ReportContainer.vue b/app/javascript/dashboard/routes/dashboard/settings/reports/ReportContainer.vue index 0fa504a36757b..3dbd4eb57d945 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/reports/ReportContainer.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/reports/ReportContainer.vue @@ -44,6 +44,7 @@ const REPORTS_KEYS = { FIRST_RESPONSE_TIME: 'avg_first_response_time', RESOLUTION_TIME: 'avg_resolution_time', RESOLUTION_COUNT: 'resolutions_count', + REPLY_TIME: 'reply_time', }; export default { @@ -60,6 +61,7 @@ export default { const reportKeys = [ 'CONVERSATIONS', 'FIRST_RESPONSE_TIME', + 'REPLY_TIME', 'RESOLUTION_TIME', 'RESOLUTION_COUNT', 'INCOMING_MESSAGES', diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/components/WootReports.vue b/app/javascript/dashboard/routes/dashboard/settings/reports/components/WootReports.vue index fdac063bad990..6ed2545b6dfaf 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/reports/components/WootReports.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/reports/components/WootReports.vue @@ -38,6 +38,7 @@ const REPORTS_KEYS = { FIRST_RESPONSE_TIME: 'avg_first_response_time', RESOLUTION_TIME: 'avg_resolution_time', RESOLUTION_COUNT: 'resolutions_count', + REPLY_TIME: 'reply_time', }; export default { @@ -106,6 +107,7 @@ export default { 'FIRST_RESPONSE_TIME', 'RESOLUTION_TIME', 'RESOLUTION_COUNT', + 'REPLY_TIME', ].forEach(async key => { try { const { from, to, groupBy, businessHours } = this; diff --git a/app/javascript/dashboard/routes/dashboard/settings/reports/constants.js b/app/javascript/dashboard/routes/dashboard/settings/reports/constants.js index cc23529b88b8d..f4eb5a6dc76d3 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/reports/constants.js +++ b/app/javascript/dashboard/routes/dashboard/settings/reports/constants.js @@ -151,78 +151,48 @@ export const DEFAULT_CHART = { }, }; -export const METRIC_CHART = { - conversations_count: DEFAULT_CHART, - incoming_messages_count: DEFAULT_CHART, - outgoing_messages_count: DEFAULT_CHART, - avg_first_response_time: { - datasets: [DEFAULT_BAR_CHART], - scales: { - xAxes: [ - { - ticks: { - fontFamily: CHART_FONT_FAMILY, - }, - gridLines: { - drawOnChartArea: false, - }, +const TIME_CHART_CONFIG = { + datasets: [DEFAULT_BAR_CHART], + scales: { + xAxes: [ + { + ticks: { + fontFamily: CHART_FONT_FAMILY, }, - ], - yAxes: [ - { - id: 'y-left', - type: 'linear', - position: 'left', - ticks: { - fontFamily: CHART_FONT_FAMILY, - callback: (value, index, values) => { - if (!index || index === values.length - 1) { - return formatTime(value); - } - return ''; - }, - }, - gridLines: { - drawOnChartArea: false, - }, + gridLines: { + drawOnChartArea: false, }, - ], - }, - }, - avg_resolution_time: { - datasets: [DEFAULT_BAR_CHART], - scales: { - xAxes: [ - { - ticks: { - fontFamily: CHART_FONT_FAMILY, - }, - gridLines: { - drawOnChartArea: false, + }, + ], + yAxes: [ + { + id: 'y-left', + type: 'linear', + position: 'left', + ticks: { + fontFamily: CHART_FONT_FAMILY, + callback: (value, index, values) => { + if (!index || index === values.length - 1) { + return formatTime(value); + } + return ''; }, }, - ], - yAxes: [ - { - id: 'y-left', - type: 'linear', - position: 'left', - ticks: { - fontFamily: CHART_FONT_FAMILY, - callback: (value, index, values) => { - if (!index || index === values.length - 1) { - return formatTime(value); - } - return ''; - }, - }, - gridLines: { - drawOnChartArea: false, - }, + gridLines: { + drawOnChartArea: false, }, - ], - }, + }, + ], }, +}; + +export const METRIC_CHART = { + conversations_count: DEFAULT_CHART, + incoming_messages_count: DEFAULT_CHART, + outgoing_messages_count: DEFAULT_CHART, + avg_first_response_time: TIME_CHART_CONFIG, + reply_time: TIME_CHART_CONFIG, + avg_resolution_time: TIME_CHART_CONFIG, resolutions_count: DEFAULT_CHART, }; diff --git a/app/javascript/dashboard/store/modules/reports.js b/app/javascript/dashboard/store/modules/reports.js index 7f51eaf42c07f..b529730febf8e 100644 --- a/app/javascript/dashboard/store/modules/reports.js +++ b/app/javascript/dashboard/store/modules/reports.js @@ -19,6 +19,7 @@ const state = { avg_first_response_time: false, avg_resolution_time: false, resolutions_count: false, + reply_time: false, }, data: { conversations_count: [], @@ -27,6 +28,7 @@ const state = { avg_first_response_time: [], avg_resolution_time: [], resolutions_count: [], + reply_time: [], }, }, accountSummary: { @@ -35,6 +37,7 @@ const state = { conversations_count: 0, incoming_messages_count: 0, outgoing_messages_count: 0, + reply_time: 0, resolutions_count: 0, previous: {}, }, From d66eb8b21f71368ec73056a2afb5a02ce9743239 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jul 2023 12:25:00 -0700 Subject: [PATCH 2/7] chore(deps): bump word-wrap from 1.2.3 to 1.2.4 (#7543) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index c1dff2b955d9f..cb98cc388a14f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20806,9 +20806,9 @@ with@^7.0.0: babel-walk "3.0.0-canary-5" word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + version "1.2.4" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" + integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== wordwrap@^1.0.0: version "1.0.0" From 2b4f4f0b5ce9b6829829bd63d4deeb63d1a67245 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Thu, 20 Jul 2023 22:47:54 +0300 Subject: [PATCH 3/7] [Snyk] Security upgrade administrate from 0.18.0 to 0.19.0 (#7547) Co-authored-by: snyk-bot --- Gemfile | 2 +- Gemfile.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index bbc7a6f742118..743818b98fe9e 100644 --- a/Gemfile +++ b/Gemfile @@ -74,7 +74,7 @@ gem 'devise_token_auth' gem 'jwt' gem 'pundit' # super admin -gem 'administrate' +gem 'administrate', '>= 0.19.0' gem 'administrate-field-active_storage' ##--- gems for pubsub service ---## diff --git a/Gemfile.lock b/Gemfile.lock index 0b250bd7785f9..1051e724e0a7d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -105,7 +105,7 @@ GEM activerecord (>= 6.0, < 7.1) addressable (2.8.4) public_suffix (>= 2.0.2, < 6.0) - administrate (0.18.0) + administrate (0.19.0) actionpack (>= 5.0) actionview (>= 5.0) activerecord (>= 5.0) @@ -376,7 +376,7 @@ GEM actionview (>= 5.0.0) activesupport (>= 5.0.0) jmespath (1.6.2) - jquery-rails (4.5.1) + jquery-rails (4.6.0) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) @@ -451,7 +451,7 @@ GEM mime-types-data (3.2023.0218.1) mini_magick (4.12.0) mini_mime (1.1.2) - mini_portile2 (2.8.2) + mini_portile2 (2.8.4) minitest (5.18.1) mock_redis (0.36.0) ruby2_keywords @@ -824,7 +824,7 @@ DEPENDENCIES active_record_query_trace activerecord-import acts-as-taggable-on - administrate + administrate (>= 0.19.0) administrate-field-active_storage annotate attr_extras From a3d21024a60398c59ce16922d5c21fb7d1b6921f Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Fri, 21 Jul 2023 10:20:53 +0530 Subject: [PATCH 4/7] feat: add message condition (#7495) --- .../integrations/openai_processor_service.rb | 4 ++ .../openai/processor_service_spec.rb | 49 ++++++++----------- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/enterprise/lib/enterprise/integrations/openai_processor_service.rb b/enterprise/lib/enterprise/integrations/openai_processor_service.rb index 85cc3ad7522e6..4ef6bec6d2b68 100644 --- a/enterprise/lib/enterprise/integrations/openai_processor_service.rb +++ b/enterprise/lib/enterprise/integrations/openai_processor_service.rb @@ -27,6 +27,10 @@ def labels_with_messages character_count = labels.length conversation = find_conversation + + # return nil if conversation has less than 3 incoming messages + return nil if conversation.messages.incoming.count < 3 + messages = init_messages_body(false) add_messages_until_token_limit(conversation, messages, false, character_count) diff --git a/enterprise/spec/integrations/openai/processor_service_spec.rb b/enterprise/spec/integrations/openai/processor_service_spec.rb index 6fa41f4bc644e..2f4033ef73e3e 100644 --- a/enterprise/spec/integrations/openai/processor_service_spec.rb +++ b/enterprise/spec/integrations/openai/processor_service_spec.rb @@ -17,43 +17,36 @@ ] }.to_json end - let!(:conversation) { create(:conversation, account: account) } - let!(:customer_message) { create(:message, account: account, conversation: conversation, message_type: :incoming, content: 'hello agent') } - let!(:agent_message) { create(:message, account: account, conversation: conversation, message_type: :outgoing, content: 'hello customer') } + + let(:conversation) { create(:conversation, account: account) } describe '#perform' do - context 'when event name is label_suggestion with labels' do + context 'when event name is label_suggestion with labels with < 3 messages' do let(:event) { { 'name' => 'label_suggestion', 'data' => { 'conversation_display_id' => conversation.display_id } } } - let(:label1) { create(:label, account: account) } - let(:label2) { create(:label, account: account) } - let(:label_suggestion_payload) do - labels = "#{label1.title}, #{label2.title}" - messages = - "Customer #{customer_message.sender.name} : #{customer_message.content}\nAgent #{agent_message.sender.name} : #{agent_message.content}" - "Messages:\n#{messages}\n\nLabels:\n#{labels}" + it 'returns nil' do + create(:label, account: account) + create(:label, account: account) + + expect(subject.perform).to be_nil end + end + + context 'when event name is label_suggestion with labels with >3 messages' do + let(:event) { { 'name' => 'label_suggestion', 'data' => { 'conversation_display_id' => conversation.display_id } } } it 'returns the label suggestions' do - request_body = { - 'model' => 'gpt-3.5-turbo', - 'messages' => [ - { - role: 'system', - content: 'Your role is as an assistant to a customer support agent. You will be provided with ' \ - 'a transcript of a conversation between a customer and the support agent, along with a list of potential labels. ' \ - 'Your task is to analyze the conversation and select the two labels from the given list that most accurately ' \ - 'represent the themes or issues discussed. Ensure you preserve the exact casing of the labels as they are provided ' \ - 'in the list. Do not create new labels; only choose from those provided. Once you have made your selections, ' \ - 'please provide your response as a comma-separated list of the provided labels. Remember, your response should only contain ' \ - 'the labels you\'ve selected, in their original casing, and nothing else. ' - }, - { role: 'user', content: label_suggestion_payload } - ] - }.to_json + create(:message, account: account, conversation: conversation, message_type: :incoming, content: 'hello agent') + create(:message, account: account, conversation: conversation, message_type: :outgoing, content: 'hello customer') + create(:message, account: account, conversation: conversation, message_type: :incoming, content: 'hello agent 2') + create(:message, account: account, conversation: conversation, message_type: :incoming, content: 'hello agent 3') + create(:message, account: account, conversation: conversation, message_type: :incoming, content: 'hello agent 4') + + create(:label, account: account) + create(:label, account: account) stub_request(:post, 'https://api.openai.com/v1/chat/completions') - .with(body: request_body, headers: expected_headers) + .with(body: anything, headers: expected_headers) .to_return(status: 200, body: openai_response, headers: {}) result = subject.perform From 4828071fc36d8fc69d2ac129cc774d747b5964e7 Mon Sep 17 00:00:00 2001 From: Vishnu Narayanan Date: Fri, 21 Jul 2023 12:08:19 +0530 Subject: [PATCH 5/7] feat: add audit trail for channel updates (#7396) --- app/models/concerns/channelable.rb | 5 ++ .../app/models/enterprise/channelable.rb | 34 ++++++++++++ spec/enterprise/models/inbox_spec.rb | 52 +++++++++++++++++-- 3 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 enterprise/app/models/enterprise/channelable.rb diff --git a/app/models/concerns/channelable.rb b/app/models/concerns/channelable.rb index d88aae90a3cf8..0b23283e1ef31 100644 --- a/app/models/concerns/channelable.rb +++ b/app/models/concerns/channelable.rb @@ -4,9 +4,14 @@ module Channelable validates :account_id, presence: true belongs_to :account has_one :inbox, as: :channel, dependent: :destroy_async, touch: true + after_update :create_audit_log_entry end def messaging_window_enabled? false end + + def create_audit_log_entry; end end + +Channelable.prepend_mod_with('Channelable') diff --git a/enterprise/app/models/enterprise/channelable.rb b/enterprise/app/models/enterprise/channelable.rb new file mode 100644 index 0000000000000..e46fdb2beb70c --- /dev/null +++ b/enterprise/app/models/enterprise/channelable.rb @@ -0,0 +1,34 @@ +module Enterprise::Channelable + extend ActiveSupport::Concern + + # Active support concern has `included` which changes the order of the method lookup chain + # https://stackoverflow.com/q/40061982/3824876 + # manually prepend the instance methods to combat this + included do + prepend InstanceMethods + end + + module InstanceMethods + def create_audit_log_entry + account = self.account + associated_type = 'Account' + + return if inbox.nil? + + auditable_id = inbox.id + auditable_type = 'Inbox' + audited_changes = saved_changes.except('updated_at') + + return if audited_changes.blank? + + Enterprise::AuditLog.create( + auditable_id: auditable_id, + auditable_type: auditable_type, + action: 'update', + associated_id: account.id, + associated_type: associated_type, + audited_changes: audited_changes + ) + end + end +end diff --git a/spec/enterprise/models/inbox_spec.rb b/spec/enterprise/models/inbox_spec.rb index 978a7ba5d79e0..2cc7eeea9927e 100644 --- a/spec/enterprise/models/inbox_spec.rb +++ b/spec/enterprise/models/inbox_spec.rb @@ -40,14 +40,60 @@ describe 'audit log' do context 'when inbox is created' do it 'has associated audit log created' do - expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'create').count).to eq 1 + expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'create').count).to eq(1) end end context 'when inbox is updated' do it 'has associated audit log created' do - inbox.update(auto_assignment_config: { max_assignment_limit: 2 }) - expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update').count).to eq 1 + inbox.update(name: 'Updated Inbox') + expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update').count).to eq(1) + end + end + + context 'when channel is updated' do + it 'has associated audit log created' do + previous_color = inbox.channel.widget_color + new_color = '#ff0000' + inbox.channel.update(widget_color: new_color) + + # check if channel update creates an audit log against inbox + expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update').count).to eq(1) + # Check for the specific widget_color update in the audit log + expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update', + audited_changes: { 'widget_color' => [previous_color, new_color] }).count).to eq(1) + end + end + end + + describe 'audit log with api channel' do + let!(:channel) { create(:channel_api) } + let!(:inbox) { channel.inbox } + + context 'when inbox is created' do + it 'has associated audit log created' do + expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'create').count).to eq(1) + end + end + + context 'when inbox is updated' do + it 'has associated audit log created' do + inbox.update(name: 'Updated Inbox') + expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update').count).to eq(1) + end + end + + context 'when channel is updated' do + it 'has associated audit log created' do + previous_webhook = inbox.channel.webhook_url + new_webhook = 'https://example2.com' + inbox.channel.update(webhook_url: new_webhook) + + # check if channel update creates an audit log against inbox + expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update').count).to eq(1) + # Check for the specific webhook_update update in the audit log + expect(Audited::Audit.where(auditable_type: 'Inbox', action: 'update', + audited_changes: { 'webhook_url' => [previous_webhook, new_webhook] }).count).to eq(1) end end end From d188600559b8678496932b575607c95fda06d654 Mon Sep 17 00:00:00 2001 From: Vishnu Narayanan Date: Fri, 21 Jul 2023 14:28:49 +0530 Subject: [PATCH 6/7] fix: modify exception tracker to log even if sentry configured (#7563) Right now, if sentry is configured exception won't be logged. This results in the log management tool missing every error captured with ChatwootExceptionTracker. This change logs the exception, even if Sentry is configured or not. Fixes https://linear.app/chatwoot/issue/CW-2145/improve-logging-info-debug-trace --- lib/chatwoot_exception_tracker.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/chatwoot_exception_tracker.rb b/lib/chatwoot_exception_tracker.rb index e525f3e8980c8..d2879a08c8c7b 100644 --- a/lib/chatwoot_exception_tracker.rb +++ b/lib/chatwoot_exception_tracker.rb @@ -12,11 +12,8 @@ def initialize(exception, user: nil, account: nil) end def capture_exception - if ENV['SENTRY_DSN'].present? - capture_exception_with_sentry - else - Rails.logger.error @exception - end + capture_exception_with_sentry if ENV['SENTRY_DSN'].present? + Rails.logger.error @exception end private From 30f39289042c40a4dfeab5ed50f6e53b5cab17d4 Mon Sep 17 00:00:00 2001 From: Sojan Jose Date: Fri, 21 Jul 2023 15:02:05 +0300 Subject: [PATCH 7/7] chore: bump up version to 3.0.0 (#7574) Bump the Chatwoot version to 3.0.0 ref: https://github.com/orgs/chatwoot/discussions/7570 --- config/app.yml | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/app.yml b/config/app.yml index f72285e70af8e..c8d719230ed4b 100644 --- a/config/app.yml +++ b/config/app.yml @@ -1,5 +1,5 @@ shared: &shared - version: '2.18.0' + version: '3.0.0' development: <<: *shared diff --git a/package.json b/package.json index 9b96b9018476f..76c9a36bfc1dc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chatwoot/chatwoot", - "version": "2.18.0", + "version": "3.0.0", "license": "MIT", "scripts": { "eslint": "eslint app/**/*.{js,vue}",