Merge branch 'develop' into CW-3013
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
## All javascript files should be reviewed by pranav before merging
|
||||
*.js @pranavrajs
|
||||
*.vue @pranavrajs
|
||||
|
||||
|
||||
## All enterprise related files should be reviewed by sojan before merging
|
||||
/enterprise/* @sojan-official
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check for log lines and calculate percentage
|
||||
run: |
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# # This action will try to install and setup
|
||||
# # chatwoot on an Ubuntu 20.04 machine using
|
||||
# # the linux installer script.
|
||||
# #
|
||||
# #
|
||||
# # This is set to run daily at midnight.
|
||||
# #
|
||||
|
||||
@@ -35,7 +35,7 @@ jobs:
|
||||
run: |
|
||||
sudo ./install.sh --install < input
|
||||
|
||||
# disabling http verify for now as http
|
||||
# disabling http verify for now as http
|
||||
# access to port 3000 fails in gh action env
|
||||
# - name: Verify
|
||||
# if: always()
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
# curl http://localhost:3000/api
|
||||
|
||||
- name: Upload chatwoot setup log file as an artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: chatwoot-setup-log-file
|
||||
|
||||
@@ -8,7 +8,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v1
|
||||
|
||||
@@ -21,7 +21,7 @@ jobs:
|
||||
GIT_REF: ${{ github.head_ref || github.ref_name }} # ref_name to get tags/branches
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
options: --entrypoint redis-server
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
with:
|
||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: yarn
|
||||
@@ -76,11 +76,11 @@ jobs:
|
||||
- name: Run backend tests
|
||||
run: |
|
||||
bundle exec rspec --profile=10 --format documentation
|
||||
env:
|
||||
env:
|
||||
NODE_OPTIONS: --openssl-legacy-provider
|
||||
|
||||
- name: Upload rails log folder
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: rails-log-folder
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
options: --entrypoint redis-server
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
with:
|
||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: yarn
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
--format documentation
|
||||
|
||||
- name: Upload rails log folder
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: rails-log-folder
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
with:
|
||||
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'yarn'
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
run: |
|
||||
rm -rf enterprise
|
||||
rm -rf spec/enterprise
|
||||
|
||||
|
||||
- name: Run asset compile
|
||||
run: bundle exec rake assets:precompile
|
||||
env:
|
||||
|
||||
+9
-9
@@ -316,16 +316,16 @@ GEM
|
||||
google-cloud-translate-v3 (0.6.0)
|
||||
gapic-common (>= 0.17.1, < 2.a)
|
||||
google-cloud-errors (~> 1.0)
|
||||
google-protobuf (3.22.3)
|
||||
google-protobuf (3.22.3-arm64-darwin)
|
||||
google-protobuf (3.22.3-x86_64-darwin)
|
||||
google-protobuf (3.22.3-x86_64-linux)
|
||||
google-protobuf (3.25.2)
|
||||
google-protobuf (3.25.2-arm64-darwin)
|
||||
google-protobuf (3.25.2-x86_64-darwin)
|
||||
google-protobuf (3.25.2-x86_64-linux)
|
||||
googleapis-common-protos (1.4.0)
|
||||
google-protobuf (~> 3.14)
|
||||
googleapis-common-protos-types (~> 1.2)
|
||||
grpc (~> 1.27)
|
||||
googleapis-common-protos-types (1.6.0)
|
||||
google-protobuf (~> 3.14)
|
||||
googleapis-common-protos-types (1.11.0)
|
||||
google-protobuf (~> 3.18)
|
||||
googleauth (1.5.2)
|
||||
faraday (>= 0.17.3, < 3.a)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
@@ -335,13 +335,13 @@ GEM
|
||||
signet (>= 0.16, < 2.a)
|
||||
groupdate (6.2.1)
|
||||
activesupport (>= 5.2)
|
||||
grpc (1.54.0)
|
||||
grpc (1.54.3)
|
||||
google-protobuf (~> 3.21)
|
||||
googleapis-common-protos-types (~> 1.0)
|
||||
grpc (1.54.0-x86_64-darwin)
|
||||
grpc (1.54.3-x86_64-darwin)
|
||||
google-protobuf (~> 3.21)
|
||||
googleapis-common-protos-types (~> 1.0)
|
||||
grpc (1.54.0-x86_64-linux)
|
||||
grpc (1.54.3-x86_64-linux)
|
||||
google-protobuf (~> 3.21)
|
||||
googleapis-common-protos-types (~> 1.0)
|
||||
haikunator (1.1.1)
|
||||
|
||||
@@ -15,8 +15,8 @@ module Api::V2::Accounts::HeatmapHelper
|
||||
dates = data.pluck(:date).uniq.sort
|
||||
|
||||
# add the dates as the first row, leave an empty cell for the hour column
|
||||
# e.g. [nil, '2023-01-01', '2023-1-02', '2023-01-03']
|
||||
result_arr << ([nil] + dates)
|
||||
# e.g. ['Start of the hour', '2023-01-01', '2023-1-02', '2023-01-03']
|
||||
result_arr << (['Start of the hour'] + dates)
|
||||
|
||||
# group the data by hour, we do not need to sort it, because the data is already sorted
|
||||
# given it starts from the beginning of the day
|
||||
@@ -25,7 +25,7 @@ module Api::V2::Accounts::HeatmapHelper
|
||||
# value = [{date: 2023-01-01, value: 1}, {date: 2023-01-02, value: 1}, {date: 2023-01-03, value: 1}, ...]
|
||||
data.group_by { |d| d[:hour] }.each do |hour, items|
|
||||
# create a new row for each hour
|
||||
row = [hour]
|
||||
row = [format('%02d:00', hour)]
|
||||
|
||||
# group the items by date, so we can easily access the value for each date
|
||||
# grouped values will be a hasg with the date as the key, and the value as the value
|
||||
@@ -37,7 +37,7 @@ module Api::V2::Accounts::HeatmapHelper
|
||||
row << (grouped_values[date][0][:value] if grouped_values[date].is_a?(Array))
|
||||
end
|
||||
|
||||
# row will look like [22, 0, 0, 1, 4, 6, 7, 4]
|
||||
# row will look like ['22:00', 0, 0, 1, 4, 6, 7, 4]
|
||||
# add the row to the result array
|
||||
|
||||
result_arr << row
|
||||
@@ -46,12 +46,12 @@ module Api::V2::Accounts::HeatmapHelper
|
||||
# return the resultant array
|
||||
# the result looks like this
|
||||
# [
|
||||
# [nil, '2023-01-01', '2023-1-02', '2023-01-03'],
|
||||
# [0, 0, 0, 0],
|
||||
# [1, 0, 0, 0],
|
||||
# [2, 0, 0, 0],
|
||||
# [3, 0, 0, 0],
|
||||
# [4, 0, 0, 0],
|
||||
# ['Start of the hour', '2023-01-01', '2023-1-02', '2023-01-03'],
|
||||
# ['00:00', 0, 0, 0],
|
||||
# ['01:00', 0, 0, 0],
|
||||
# ['02:00', 0, 0, 0],
|
||||
# ['03:00', 0, 0, 0],
|
||||
# ['04:00', 0, 0, 0],
|
||||
# ]
|
||||
result_arr
|
||||
end
|
||||
|
||||
@@ -7,6 +7,7 @@ class Inboxes::FetchImapEmailsJob < MutexApplicationJob
|
||||
return unless should_fetch_email?(channel)
|
||||
|
||||
key = format(::Redis::Alfred::EMAIL_MESSAGE_MUTEX, inbox_id: channel.inbox.id)
|
||||
|
||||
with_lock(key, 5.minutes) do
|
||||
process_email_for_channel(channel)
|
||||
end
|
||||
@@ -28,128 +29,16 @@ class Inboxes::FetchImapEmailsJob < MutexApplicationJob
|
||||
end
|
||||
|
||||
def process_email_for_channel(channel)
|
||||
if channel.microsoft?
|
||||
fetch_mail_for_ms_provider(channel)
|
||||
else
|
||||
fetch_mail_for_channel(channel)
|
||||
end
|
||||
# clearing old failures like timeouts since the mail is now successfully processed
|
||||
channel.reauthorized!
|
||||
end
|
||||
|
||||
def fetch_mail_for_channel(channel)
|
||||
imap_client = build_imap_client(channel, channel.imap_password, 'PLAIN')
|
||||
|
||||
message_ids_with_seq = fetch_message_ids_with_sequence(imap_client, channel)
|
||||
message_ids_with_seq.each do |message_id_with_seq|
|
||||
process_message_id(channel, imap_client, message_id_with_seq)
|
||||
end
|
||||
end
|
||||
|
||||
def process_message_id(channel, imap_client, message_id_with_seq)
|
||||
seq_no, message_id = message_id_with_seq
|
||||
|
||||
return if email_already_present?(channel, message_id)
|
||||
|
||||
if message_id.blank?
|
||||
Rails.logger.info "[IMAP::FETCH_EMAIL_SERVICE] Empty message id for #{channel.email} with seq no. <#{seq_no}>."
|
||||
return
|
||||
end
|
||||
|
||||
# Fetch the original mail content using the sequence no
|
||||
mail_str = imap_client.fetch(seq_no, 'RFC822')[0].attr['RFC822']
|
||||
|
||||
if mail_str.blank?
|
||||
Rails.logger.info "[IMAP::FETCH_EMAIL_SERVICE] Fetch failed for #{channel.email} with message-id <#{message_id}>."
|
||||
return
|
||||
end
|
||||
|
||||
inbound_mail = build_mail_from_string(mail_str)
|
||||
mail_info_logger(channel, inbound_mail, seq_no)
|
||||
process_mail(inbound_mail, channel)
|
||||
end
|
||||
|
||||
# Sends a FETCH command to retrieve data associated with a message in the mailbox.
|
||||
# You can send batches of message sequence number in `.fetch` method.
|
||||
def fetch_message_ids_with_sequence(imap_client, channel)
|
||||
seq_nums = fetch_available_mail_sequence_numbers(imap_client)
|
||||
|
||||
Rails.logger.info "[IMAP::FETCH_EMAIL_SERVICE] Fetching mails from #{channel.email}, found #{seq_nums.length}."
|
||||
|
||||
message_ids_with_seq = []
|
||||
seq_nums.each_slice(10).each do |batch|
|
||||
# Fetch only message-id only without mail body or contents.
|
||||
batch_message_ids = imap_client.fetch(batch, 'BODY.PEEK[HEADER]')
|
||||
|
||||
# .fetch returns an array of Net::IMAP::FetchData or nil
|
||||
# (instead of an empty array) if there is no matching message.
|
||||
# Check
|
||||
if batch_message_ids.blank?
|
||||
Rails.logger.info "[IMAP::FETCH_EMAIL_SERVICE] Fetching the batch failed for #{channel.email}."
|
||||
next
|
||||
end
|
||||
|
||||
batch_message_ids.each do |data|
|
||||
message_id = build_mail_from_string(data.attr['BODY[HEADER]']).message_id
|
||||
message_ids_with_seq.push([data.seqno, message_id])
|
||||
end
|
||||
end
|
||||
|
||||
message_ids_with_seq
|
||||
end
|
||||
|
||||
# Sends a SEARCH command to search the mailbox for messages that were
|
||||
# created between yesterday and today and returns message sequence numbers.
|
||||
# Return <message set>
|
||||
def fetch_available_mail_sequence_numbers(imap_client)
|
||||
imap_client.search(['SINCE', yesterday])
|
||||
end
|
||||
|
||||
def fetch_mail_for_ms_provider(channel)
|
||||
return if channel.provider_config['access_token'].blank?
|
||||
|
||||
access_token = valid_access_token channel
|
||||
|
||||
return unless access_token
|
||||
|
||||
imap_client = build_imap_client(channel, access_token, 'XOAUTH2')
|
||||
process_mails(imap_client, channel)
|
||||
end
|
||||
|
||||
def process_mails(imap_client, channel)
|
||||
fetch_available_mail_sequence_numbers(imap_client).each do |seq_no|
|
||||
inbound_mail = Mail.read_from_string imap_client.fetch(seq_no, 'RFC822')[0].attr['RFC822']
|
||||
|
||||
mail_info_logger(channel, inbound_mail, seq_no)
|
||||
|
||||
next if channel.inbox.messages.find_by(source_id: inbound_mail.message_id).present?
|
||||
|
||||
inbound_emails = if channel.microsoft?
|
||||
Imap::MicrosoftFetchEmailService.new(channel: channel).perform
|
||||
else
|
||||
Imap::FetchEmailService.new(channel: channel).perform
|
||||
end
|
||||
inbound_emails.map do |inbound_mail|
|
||||
process_mail(inbound_mail, channel)
|
||||
end
|
||||
end
|
||||
|
||||
def mail_info_logger(channel, inbound_mail, uid)
|
||||
return if Rails.env.test?
|
||||
|
||||
Rails.logger.info("
|
||||
#{channel.provider} Email id: #{inbound_mail.from} - message_source_id: #{inbound_mail.message_id} - sequence id: #{uid}")
|
||||
end
|
||||
|
||||
def build_imap_client(channel, access_token, auth_method)
|
||||
imap = Net::IMAP.new(channel.imap_address, channel.imap_port, true)
|
||||
imap.authenticate(auth_method, channel.imap_login, access_token)
|
||||
imap.select('INBOX')
|
||||
imap
|
||||
end
|
||||
|
||||
def email_already_present?(channel, message_id)
|
||||
channel.inbox.messages.find_by(source_id: message_id).present?
|
||||
end
|
||||
|
||||
def build_mail_from_string(raw_email_content)
|
||||
Mail.read_from_string(raw_email_content)
|
||||
end
|
||||
|
||||
def process_mail(inbound_mail, channel)
|
||||
Imap::ImapMailbox.new.process(inbound_mail, channel)
|
||||
rescue StandardError => e
|
||||
@@ -157,13 +46,4 @@ class Inboxes::FetchImapEmailsJob < MutexApplicationJob
|
||||
Rails.logger.error("
|
||||
#{channel.provider} Email dropped: #{inbound_mail.from} and message_source_id: #{inbound_mail.message_id}")
|
||||
end
|
||||
|
||||
# Making sure the access token is valid for microsoft provider
|
||||
def valid_access_token(channel)
|
||||
Microsoft::RefreshOauthTokenService.new(channel: channel).access_token
|
||||
end
|
||||
|
||||
def yesterday
|
||||
(Time.zone.today - 1).strftime('%d-%b-%Y')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
require 'net/imap'
|
||||
|
||||
class Imap::BaseFetchEmailService
|
||||
pattr_initialize [:channel!]
|
||||
|
||||
def perform
|
||||
# Override this method
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authentication_type
|
||||
# Override this method
|
||||
end
|
||||
|
||||
def imap_password
|
||||
# Override this method
|
||||
end
|
||||
|
||||
def imap_client
|
||||
@imap_client ||= build_imap_client
|
||||
end
|
||||
|
||||
def mail_info_logger(inbound_mail, seq_no)
|
||||
return if Rails.env.test?
|
||||
|
||||
Rails.logger.info("
|
||||
#{channel.provider} Email id: #{inbound_mail.from} - message_source_id: #{inbound_mail.message_id} - sequence id: #{seq_no}")
|
||||
end
|
||||
|
||||
def email_already_present?(channel, message_id)
|
||||
channel.inbox.messages.find_by(source_id: message_id).present?
|
||||
end
|
||||
|
||||
def fetch_mail_for_channel
|
||||
message_ids_with_seq = fetch_message_ids_with_sequence
|
||||
message_ids_with_seq.filter_map do |message_id_with_seq|
|
||||
process_message_id(message_id_with_seq)
|
||||
end
|
||||
end
|
||||
|
||||
def process_message_id(message_id_with_seq)
|
||||
seq_no, message_id = message_id_with_seq
|
||||
|
||||
if message_id.blank?
|
||||
Rails.logger.info "[IMAP::FETCH_EMAIL_SERVICE] Empty message id for #{channel.email} with seq no. <#{seq_no}>."
|
||||
return
|
||||
end
|
||||
|
||||
return if email_already_present?(channel, message_id)
|
||||
|
||||
# Fetch the original mail content using the sequence no
|
||||
mail_str = imap_client.fetch(seq_no, 'RFC822')[0].attr['RFC822']
|
||||
|
||||
if mail_str.blank?
|
||||
Rails.logger.info "[IMAP::FETCH_EMAIL_SERVICE] Fetch failed for #{channel.email} with message-id <#{message_id}>."
|
||||
return
|
||||
end
|
||||
|
||||
inbound_mail = build_mail_from_string(mail_str)
|
||||
mail_info_logger(inbound_mail, seq_no)
|
||||
inbound_mail
|
||||
end
|
||||
|
||||
# Sends a FETCH command to retrieve data associated with a message in the mailbox.
|
||||
# You can send batches of message sequence number in `.fetch` method.
|
||||
def fetch_message_ids_with_sequence
|
||||
seq_nums = fetch_available_mail_sequence_numbers
|
||||
|
||||
Rails.logger.info "[IMAP::FETCH_EMAIL_SERVICE] Fetching mails from #{channel.email}, found #{seq_nums.length}."
|
||||
|
||||
message_ids_with_seq = []
|
||||
seq_nums.each_slice(10).each do |batch|
|
||||
# Fetch only message-id only without mail body or contents.
|
||||
batch_message_ids = imap_client.fetch(batch, 'BODY.PEEK[HEADER]')
|
||||
|
||||
# .fetch returns an array of Net::IMAP::FetchData or nil
|
||||
# (instead of an empty array) if there is no matching message.
|
||||
# Check
|
||||
if batch_message_ids.blank?
|
||||
Rails.logger.info "[IMAP::FETCH_EMAIL_SERVICE] Fetching the batch failed for #{channel.email}."
|
||||
next
|
||||
end
|
||||
|
||||
batch_message_ids.each do |data|
|
||||
message_id = build_mail_from_string(data.attr['BODY[HEADER]']).message_id
|
||||
message_ids_with_seq.push([data.seqno, message_id])
|
||||
end
|
||||
end
|
||||
|
||||
message_ids_with_seq
|
||||
end
|
||||
|
||||
# Sends a SEARCH command to search the mailbox for messages that were
|
||||
# created between yesterday and today and returns message sequence numbers.
|
||||
# Return <message set>
|
||||
def fetch_available_mail_sequence_numbers
|
||||
imap_client.search(['SINCE', yesterday])
|
||||
end
|
||||
|
||||
def build_imap_client
|
||||
imap = Net::IMAP.new(channel.imap_address, port: channel.imap_port, ssl: true)
|
||||
imap.authenticate(authentication_type, channel.imap_login, imap_password)
|
||||
imap.select('INBOX')
|
||||
imap
|
||||
end
|
||||
|
||||
def build_mail_from_string(raw_email_content)
|
||||
Mail.read_from_string(raw_email_content)
|
||||
end
|
||||
|
||||
def yesterday
|
||||
(Time.zone.today - 1).strftime('%d-%b-%Y')
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,15 @@
|
||||
class Imap::FetchEmailService < Imap::BaseFetchEmailService
|
||||
def perform
|
||||
fetch_mail_for_channel
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authentication_type
|
||||
'PLAIN'
|
||||
end
|
||||
|
||||
def imap_password
|
||||
channel.imap_password
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
class Imap::MicrosoftFetchEmailService < Imap::BaseFetchEmailService
|
||||
def perform
|
||||
return if channel.provider_config['access_token'].blank?
|
||||
|
||||
fetch_mail_for_channel
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def authentication_type
|
||||
'XOAUTH2'
|
||||
end
|
||||
|
||||
def imap_password
|
||||
Microsoft::RefreshOauthTokenService.new(channel: channel).access_token
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,4 @@
|
||||
<%= CSVSafe.generate_line [I18n.t('reports.conversation_traffic_csv.timezone'), @timezone] %>
|
||||
<%= CSV.generate_line [I18n.t('reports.conversation_traffic_csv.timezone'), @timezone] %>
|
||||
<% @report_data.each do |row| %>
|
||||
<%= CSVSafe.generate_line row -%>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Integrations::Widget::IncomingMessageBuilder
|
||||
# params = {
|
||||
# contact_id: 1,
|
||||
# inbox_id: 1,
|
||||
# content: "Hello world"
|
||||
# }
|
||||
|
||||
attr_accessor :options, :message
|
||||
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def perform
|
||||
ActiveRecord::Base.transaction do
|
||||
build_message
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def inbox
|
||||
@inbox ||= Inbox.find(options[:inbox_id])
|
||||
end
|
||||
|
||||
def contact
|
||||
@contact ||= Contact.find(options[:contact_id])
|
||||
end
|
||||
|
||||
def conversation
|
||||
@conversation ||= Conversation.find_by(conversation_params) || Conversation.create!(conversation_params)
|
||||
end
|
||||
|
||||
def build_message
|
||||
@message = conversation.messages.new(message_params)
|
||||
@message.save!
|
||||
end
|
||||
|
||||
def conversation_params
|
||||
{
|
||||
account_id: inbox.account_id,
|
||||
inbox_id: inbox.id,
|
||||
contact_id: options[:contact_id]
|
||||
}
|
||||
end
|
||||
|
||||
def message_params
|
||||
{
|
||||
account_id: conversation.account_id,
|
||||
inbox_id: conversation.inbox_id,
|
||||
message_type: 0,
|
||||
content: options[:content],
|
||||
sender: contact
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -1,52 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Integrations::Widget::OutgoingMessageBuilder
|
||||
# params = {
|
||||
# user_id: 1,
|
||||
# inbox_id: 1,
|
||||
# content: "Hello world",
|
||||
# conversation_id: 2
|
||||
# }
|
||||
|
||||
attr_accessor :options, :message
|
||||
|
||||
def initialize(options)
|
||||
@options = options
|
||||
end
|
||||
|
||||
def perform
|
||||
ActiveRecord::Base.transaction do
|
||||
conversation
|
||||
build_message
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def inbox
|
||||
@inbox ||= Inbox.find(options[:inbox_id])
|
||||
end
|
||||
|
||||
def user
|
||||
@user ||= Contact.find(options[:user_id])
|
||||
end
|
||||
|
||||
def conversation
|
||||
@conversation ||= Conversation.find(options[:conversation_id])
|
||||
end
|
||||
|
||||
def build_message
|
||||
@message = conversation.messages.new(message_params)
|
||||
@message.save!
|
||||
end
|
||||
|
||||
def message_params
|
||||
{
|
||||
account_id: conversation.account_id,
|
||||
inbox_id: conversation.inbox_id,
|
||||
message_type: 1,
|
||||
content: options[:content],
|
||||
sender: user
|
||||
}
|
||||
end
|
||||
end
|
||||
@@ -25,5 +25,14 @@ FactoryBot.define do
|
||||
end
|
||||
provider { 'microsoft' }
|
||||
end
|
||||
|
||||
trait :imap_email do
|
||||
imap_enabled { true }
|
||||
imap_address { 'imap.gmail.com' }
|
||||
imap_port { 993 }
|
||||
imap_login { 'email@example.com' }
|
||||
imap_password { 'random-password' }
|
||||
imap_enable_ssl { true }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,161 +1,106 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Inboxes::FetchImapEmailsJob do
|
||||
include ActiveJob::TestHelper
|
||||
include ActionMailbox::TestHelper
|
||||
|
||||
let(:account) { create(:account) }
|
||||
let(:imap_email_channel) do
|
||||
create(:channel_email, imap_enabled: true, imap_address: 'imap.gmail.com', imap_port: 993, imap_login: 'imap@gmail.com',
|
||||
imap_password: 'password', account: account)
|
||||
end
|
||||
|
||||
let(:imap_email_channel) { create(:channel_email, :imap_email, account: account) }
|
||||
let(:channel_with_imap_disabled) { create(:channel_email, :imap_email, imap_enabled: false, account: account) }
|
||||
let(:microsoft_imap_email_channel) { create(:channel_email, :microsoft_email) }
|
||||
let(:ms_email_inbox) { create(:inbox, channel: microsoft_imap_email_channel, account: account) }
|
||||
let!(:conversation) { create(:conversation, inbox: imap_email_channel.inbox, account: account) }
|
||||
let(:inbound_mail) { create_inbound_email_from_mail(from: 'testemail@gmail.com', to: 'imap@outlook.com', subject: 'Hello!') }
|
||||
let(:inbound_mail_with_attachments) { create_inbound_email_from_fixture('multiple_attachments.eml') }
|
||||
|
||||
it 'enqueues the job' do
|
||||
expect { described_class.perform_later }.to have_enqueued_job(described_class)
|
||||
.on_queue('scheduled_jobs')
|
||||
end
|
||||
|
||||
context 'when imap fetch new emails' do
|
||||
it 'process the email' do
|
||||
email = Mail.new do
|
||||
to 'test@outlook.com'
|
||||
from 'test@gmail.com'
|
||||
subject :test.to_s
|
||||
body 'hello'
|
||||
end
|
||||
|
||||
imap_fetch_mail = Net::IMAP::FetchData.new
|
||||
imap_fetch_mail.attr = { seqno: 1, RFC822: email }.with_indifferent_access
|
||||
|
||||
imap = double
|
||||
|
||||
allow(Net::IMAP).to receive(:new).and_return(imap)
|
||||
allow(imap).to receive(:authenticate)
|
||||
allow(imap).to receive(:select)
|
||||
allow(imap).to receive(:search).and_return([1])
|
||||
allow(imap).to receive(:fetch).and_return([imap_fetch_mail])
|
||||
|
||||
read_mail = Mail::Message.new(date: DateTime.now, from: 'testemail@gmail.com', to: 'imap@outlook.com', subject: 'Hello!')
|
||||
allow(Mail).to receive(:read_from_string).and_return(inbound_mail.mail)
|
||||
|
||||
imap_mailbox = double
|
||||
|
||||
allow(Imap::ImapMailbox).to receive(:new).and_return(imap_mailbox)
|
||||
expect(imap_mailbox).to receive(:process).with(read_mail, imap_email_channel).once
|
||||
|
||||
described_class.perform_now(imap_email_channel)
|
||||
describe '#perform' do
|
||||
it 'enqueues the job' do
|
||||
expect do
|
||||
described_class.perform_later
|
||||
end.to have_enqueued_job(described_class).on_queue('scheduled_jobs')
|
||||
end
|
||||
|
||||
it 'process the email with no date' do
|
||||
fixture_path = Rails.root.join('spec/fixtures/files/mail_with_no_date.eml')
|
||||
eml_content = File.read(fixture_path)
|
||||
inbound_mail_with_no_date = create_inbound_email_from_fixture('mail_with_no_date.eml')
|
||||
email_header = Net::IMAP::FetchData.new(1, 'BODY[HEADER]' => eml_content)
|
||||
imap_fetch_mail = Net::IMAP::FetchData.new(1, 'RFC822' => eml_content)
|
||||
|
||||
imap = double
|
||||
allow(Net::IMAP).to receive(:new).and_return(imap)
|
||||
allow(imap).to receive(:authenticate)
|
||||
allow(imap).to receive(:select)
|
||||
allow(imap).to receive(:search).and_return([1])
|
||||
allow(imap).to receive(:fetch).with([1], 'BODY.PEEK[HEADER]').and_return([email_header])
|
||||
allow(imap).to receive(:fetch).with(1, 'RFC822').and_return([imap_fetch_mail])
|
||||
|
||||
imap_mailbox = double
|
||||
|
||||
allow(Imap::ImapMailbox).to receive(:new).and_return(imap_mailbox)
|
||||
expect(imap_mailbox).to receive(:process).with(inbound_mail_with_no_date.mail, imap_email_channel).once
|
||||
|
||||
described_class.perform_now(imap_email_channel)
|
||||
context 'when IMAP is disabled' do
|
||||
it 'does not fetch emails' do
|
||||
expect(Imap::FetchEmailService).not_to receive(:new)
|
||||
expect(Imap::MicrosoftFetchEmailService).not_to receive(:new)
|
||||
described_class.perform_now(channel_with_imap_disabled)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when imap fetch new emails with more than 15 attachments' do
|
||||
it 'process the email' do
|
||||
email = Mail.new do
|
||||
to 'test@outlook.com'
|
||||
from 'test@gmail.com'
|
||||
subject :test.to_s
|
||||
body 'hello'
|
||||
context 'when IMAP reauthorization is required' do
|
||||
it 'does not fetch emails' do
|
||||
10.times do
|
||||
imap_email_channel.authorization_error!
|
||||
end
|
||||
|
||||
expect(Imap::FetchEmailService).not_to receive(:new)
|
||||
# Confirm the imap_enabled flag is true to avoid false positives.
|
||||
expect(imap_email_channel.imap_enabled?).to be true
|
||||
|
||||
described_class.perform_now(imap_email_channel)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the channel is regular imap' do
|
||||
it 'calls the imap fetch service' do
|
||||
fetch_service = double
|
||||
allow(Imap::FetchEmailService).to receive(:new).with(channel: imap_email_channel).and_return(fetch_service)
|
||||
allow(fetch_service).to receive(:perform).and_return([])
|
||||
|
||||
described_class.perform_now(imap_email_channel)
|
||||
expect(fetch_service).to have_received(:perform)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the channel is Microsoft' do
|
||||
it 'calls the Microsoft fetch service' do
|
||||
fetch_service = double
|
||||
allow(Imap::MicrosoftFetchEmailService).to receive(:new).with(channel: microsoft_imap_email_channel).and_return(fetch_service)
|
||||
allow(fetch_service).to receive(:perform).and_return([])
|
||||
|
||||
described_class.perform_now(microsoft_imap_email_channel)
|
||||
expect(fetch_service).to have_received(:perform)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when IMAP connection errors out' do
|
||||
it 'mark the connection for authorization required' do
|
||||
allow(Imap::FetchEmailService).to receive(:new).with(channel: imap_email_channel).and_raise(Errno::ECONNREFUSED)
|
||||
allow(Redis::Alfred).to receive(:incr)
|
||||
|
||||
expect(Redis::Alfred).to receive(:incr).with("AUTHORIZATION_ERROR_COUNT:channel_email:#{imap_email_channel.id}")
|
||||
described_class.perform_now(imap_email_channel)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the fetch service returns the email objects' do
|
||||
let(:inbound_mail) { create_inbound_email_from_fixture('welcome.eml').mail }
|
||||
let(:mailbox) { double }
|
||||
let(:exception_tracker) { double }
|
||||
let(:fetch_service) { double }
|
||||
|
||||
before do
|
||||
allow(Imap::ImapMailbox).to receive(:new).and_return(mailbox)
|
||||
allow(ChatwootExceptionTracker).to receive(:new).and_return(exception_tracker)
|
||||
|
||||
allow(Imap::FetchEmailService).to receive(:new).with(channel: imap_email_channel).and_return(fetch_service)
|
||||
allow(fetch_service).to receive(:perform).and_return([inbound_mail])
|
||||
end
|
||||
|
||||
imap_fetch_mail = Net::IMAP::FetchData.new
|
||||
imap_fetch_mail.attr = { seqno: 1, RFC822: email }.with_indifferent_access
|
||||
it 'calls the mailbox to create emails' do
|
||||
allow(mailbox).to receive(:process)
|
||||
|
||||
imap = double
|
||||
expect(Imap::FetchEmailService).to receive(:new).with(channel: imap_email_channel).and_return(fetch_service)
|
||||
expect(fetch_service).to receive(:perform).and_return([inbound_mail])
|
||||
expect(mailbox).to receive(:process).with(inbound_mail, imap_email_channel)
|
||||
|
||||
allow(Net::IMAP).to receive(:new).and_return(imap)
|
||||
allow(imap).to receive(:authenticate)
|
||||
allow(imap).to receive(:select)
|
||||
allow(imap).to receive(:search).and_return([1])
|
||||
allow(imap).to receive(:fetch).and_return([imap_fetch_mail])
|
||||
|
||||
inbound_mail_with_attachments.mail.date = DateTime.now
|
||||
|
||||
allow(Mail).to receive(:read_from_string).and_return(inbound_mail_with_attachments.mail)
|
||||
|
||||
imap_mailbox = Imap::ImapMailbox.new
|
||||
|
||||
allow(Imap::ImapMailbox).to receive(:new).and_return(imap_mailbox)
|
||||
|
||||
described_class.perform_now(imap_email_channel)
|
||||
expect(Message.last.attachments.count).to eq(15)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when imap fetch new emails for microsoft mailer' do
|
||||
it 'fetch and process all emails' do
|
||||
email = Mail.new do
|
||||
to 'test@outlook.com'
|
||||
from 'test@gmail.com'
|
||||
subject :test.to_s
|
||||
body 'hello'
|
||||
end
|
||||
imap_fetch_mail = Net::IMAP::FetchData.new
|
||||
imap_fetch_mail.attr = { RFC822: email }.with_indifferent_access
|
||||
|
||||
ms_imap = double
|
||||
|
||||
allow(Net::IMAP).to receive(:new).and_return(ms_imap)
|
||||
allow(ms_imap).to receive(:authenticate)
|
||||
allow(ms_imap).to receive(:select)
|
||||
allow(ms_imap).to receive(:search).and_return([1])
|
||||
allow(ms_imap).to receive(:fetch).and_return([imap_fetch_mail])
|
||||
allow(Mail).to receive(:read_from_string).and_return(inbound_mail)
|
||||
|
||||
ms_imap_email_inbox = double
|
||||
|
||||
allow(Imap::ImapMailbox).to receive(:new).and_return(ms_imap_email_inbox)
|
||||
expect(ms_imap_email_inbox).to receive(:process).with(inbound_mail, microsoft_imap_email_channel).once
|
||||
|
||||
described_class.perform_now(microsoft_imap_email_channel)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when imap fetch existing emails' do
|
||||
it 'does not process the email' do
|
||||
email = Mail.new do
|
||||
to 'test@outlook.com'
|
||||
from 'test@gmail.com'
|
||||
subject :test.to_s
|
||||
body 'hello'
|
||||
message_id '<messageId@example.com>'
|
||||
described_class.perform_now(imap_email_channel)
|
||||
end
|
||||
|
||||
create(:message, message_type: 'incoming', source_id: email.message_id, account: account, inbox: imap_email_channel.inbox,
|
||||
conversation: conversation)
|
||||
it 'logs errors if mailbox returns errors' do
|
||||
allow(mailbox).to receive(:process).and_raise(StandardError)
|
||||
|
||||
allow(Mail).to receive(:find).and_return([email])
|
||||
imap_mailbox = double
|
||||
allow(Imap::ImapMailbox).to receive(:new).and_return(imap_mailbox)
|
||||
expect(imap_mailbox).not_to receive(:process).with(email, imap_email_channel)
|
||||
expect(exception_tracker).to receive(:capture_exception)
|
||||
|
||||
described_class.perform_now(imap_email_channel)
|
||||
described_class.perform_now(imap_email_channel)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,14 +3,10 @@ require 'rails_helper'
|
||||
RSpec.describe Imap::ImapMailbox do
|
||||
include ActionMailbox::TestHelper
|
||||
|
||||
describe 'add mail as a new conversation in the email inbox' do
|
||||
describe '#process' do
|
||||
let(:account) { create(:account) }
|
||||
let(:agent) { create(:user, email: 'agent@example.com', account: account) }
|
||||
let(:channel) do
|
||||
create(:channel_email, imap_enabled: true, imap_address: 'imap.gmail.com',
|
||||
imap_port: 993, imap_login: 'imap@gmail.com', imap_password: 'password',
|
||||
account: account)
|
||||
end
|
||||
let(:channel) { create(:channel_email, :imap_email) }
|
||||
let(:inbox) { channel.inbox }
|
||||
let!(:contact) { create(:contact, email: 'email@gmail.com', phone_number: '+919584546666', account: account, identifier: '123') }
|
||||
let(:conversation) { Conversation.where(inbox_id: channel.inbox).last }
|
||||
@@ -20,17 +16,33 @@ RSpec.describe Imap::ImapMailbox do
|
||||
create(:contact_inbox, contact_id: contact.id, inbox_id: channel.inbox.id)
|
||||
end
|
||||
|
||||
context 'when a new email from non existing contact' do
|
||||
context 'when the email is from a new contact' do
|
||||
let(:inbound_mail) { create_inbound_email_from_mail(from: 'testemail@gmail.com', to: 'imap@gmail.com', subject: 'Hello!') }
|
||||
|
||||
it 'creates the contact and conversation with message' do
|
||||
class_instance.process(inbound_mail.mail, channel)
|
||||
expect do
|
||||
class_instance.process(inbound_mail.mail, channel)
|
||||
end.to change(Conversation, :count).by(1)
|
||||
|
||||
expect(conversation.contact.email).to eq(inbound_mail.mail.from.first)
|
||||
expect(conversation.additional_attributes['source']).to eq('email')
|
||||
expect(conversation.messages.empty?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the email has 15 or more attachments' do
|
||||
let(:inbound_mail) { create_inbound_email_from_fixture('multiple_attachments.eml') }
|
||||
|
||||
it 'creates a converstation and a message properly' do
|
||||
expect do
|
||||
class_instance.process(inbound_mail.mail, channel)
|
||||
end.to change(Conversation, :count).by(1)
|
||||
|
||||
expect(conversation.contact.email).to eq(inbound_mail.mail.from.first)
|
||||
expect(conversation.messages.last.attachments.count).to be 15
|
||||
end
|
||||
end
|
||||
|
||||
context 'when a new email from existing contact' do
|
||||
let(:inbound_mail) { create_inbound_email_from_mail(from: 'email@gmail.com', to: 'imap@gmail.com', subject: 'Hello!') }
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Imap::FetchEmailService do
|
||||
include ActionMailbox::TestHelper
|
||||
let(:logger) { instance_double(ActiveSupport::Logger, info: true, error: true) }
|
||||
let(:account) { create(:account) }
|
||||
let(:imap_email_channel) { create(:channel_email, :imap_email, account: account) }
|
||||
let(:imap) { instance_double(Net::IMAP) }
|
||||
let(:eml_content_with_message_id) { Rails.root.join('spec/fixtures/files/only_text.eml').read }
|
||||
|
||||
describe '#perform' do
|
||||
before do
|
||||
allow(Rails).to receive(:logger).and_return(logger)
|
||||
allow(Net::IMAP).to receive(:new).with(
|
||||
imap_email_channel.imap_address, port: imap_email_channel.imap_port, ssl: true
|
||||
).and_return(imap)
|
||||
allow(imap).to receive(:authenticate).with(
|
||||
'PLAIN', imap_email_channel.imap_login, imap_email_channel.imap_password
|
||||
)
|
||||
allow(imap).to receive(:select).with('INBOX')
|
||||
end
|
||||
|
||||
context 'when new emails are available in the mailbox' do
|
||||
it 'fetches the emails and returns the emails that are not present in the db' do
|
||||
travel_to '26.10.2020 10:00'.to_datetime do
|
||||
email_object = create_inbound_email_from_fixture('only_text.eml')
|
||||
email_header = Net::IMAP::FetchData.new(1, 'BODY[HEADER]' => eml_content_with_message_id)
|
||||
imap_fetch_mail = Net::IMAP::FetchData.new(1, 'RFC822' => eml_content_with_message_id)
|
||||
|
||||
allow(imap).to receive(:search).with(%w[SINCE 25-Oct-2020]).and_return([1])
|
||||
allow(imap).to receive(:fetch).with([1], 'BODY.PEEK[HEADER]').and_return([email_header])
|
||||
allow(imap).to receive(:fetch).with(1, 'RFC822').and_return([imap_fetch_mail])
|
||||
|
||||
result = described_class.new(channel: imap_email_channel).perform
|
||||
|
||||
expect(result.length).to eq 1
|
||||
expect(result[0].message_id).to eq email_object.message_id
|
||||
expect(imap).to have_received(:search).with(%w[SINCE 25-Oct-2020])
|
||||
expect(imap).to have_received(:fetch).with([1], 'BODY.PEEK[HEADER]')
|
||||
expect(imap).to have_received(:fetch).with(1, 'RFC822')
|
||||
expect(logger).to have_received(:info).with("[IMAP::FETCH_EMAIL_SERVICE] Fetching mails from #{imap_email_channel.email}, found 1.")
|
||||
end
|
||||
end
|
||||
|
||||
it 'fetches the emails and returns the mail objects that are not present in the db' do
|
||||
travel_to '26.10.2020 10:00'.to_datetime do
|
||||
email_object = create_inbound_email_from_fixture('only_text.eml')
|
||||
create(:message, source_id: email_object.message_id, account: account, inbox: imap_email_channel.inbox)
|
||||
|
||||
email_header = Net::IMAP::FetchData.new(1, 'BODY[HEADER]' => eml_content_with_message_id)
|
||||
|
||||
allow(imap).to receive(:search).with(%w[SINCE 25-Oct-2020]).and_return([1])
|
||||
allow(imap).to receive(:fetch).with([1], 'BODY.PEEK[HEADER]').and_return([email_header])
|
||||
|
||||
result = described_class.new(channel: imap_email_channel).perform
|
||||
|
||||
expect(result.length).to eq 0
|
||||
expect(imap).to have_received(:search).with(%w[SINCE 25-Oct-2020])
|
||||
expect(imap).to have_received(:fetch).with([1], 'BODY.PEEK[HEADER]')
|
||||
expect(imap).not_to have_received(:fetch).with(1, 'RFC822')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,53 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Imap::MicrosoftFetchEmailService do
|
||||
include ActionMailbox::TestHelper
|
||||
let(:logger) { instance_double(ActiveSupport::Logger, info: true, error: true) }
|
||||
let(:account) { create(:account) }
|
||||
let(:microsoft_channel) { create(:channel_email, :microsoft_email, account: account) }
|
||||
let(:imap) { instance_double(Net::IMAP) }
|
||||
let(:refresh_token_service) { double }
|
||||
let(:eml_content_with_message_id) { Rails.root.join('spec/fixtures/files/only_text.eml').read }
|
||||
|
||||
describe '#perform' do
|
||||
before do
|
||||
allow(Rails).to receive(:logger).and_return(logger)
|
||||
|
||||
allow(Net::IMAP).to receive(:new).with(
|
||||
microsoft_channel.imap_address, port: microsoft_channel.imap_port, ssl: true
|
||||
).and_return(imap)
|
||||
allow(imap).to receive(:authenticate).with(
|
||||
'XOAUTH2', microsoft_channel.imap_login, microsoft_channel.provider_config['access_token']
|
||||
)
|
||||
allow(imap).to receive(:select).with('INBOX')
|
||||
|
||||
allow(Microsoft::RefreshOauthTokenService).to receive(:new).and_return(refresh_token_service)
|
||||
allow(refresh_token_service).to receive(:access_token).and_return(microsoft_channel.provider_config['access_token'])
|
||||
end
|
||||
|
||||
context 'when new emails are available in the mailbox' do
|
||||
it 'fetches the emails and returns the emails that are not present in the db' do
|
||||
travel_to '26.10.2020 10:00'.to_datetime do
|
||||
email_object = create_inbound_email_from_fixture('only_text.eml')
|
||||
email_header = Net::IMAP::FetchData.new(1, 'BODY[HEADER]' => eml_content_with_message_id)
|
||||
imap_fetch_mail = Net::IMAP::FetchData.new(1, 'RFC822' => eml_content_with_message_id)
|
||||
|
||||
allow(imap).to receive(:search).with(%w[SINCE 25-Oct-2020]).and_return([1])
|
||||
allow(imap).to receive(:fetch).with([1], 'BODY.PEEK[HEADER]').and_return([email_header])
|
||||
allow(imap).to receive(:fetch).with(1, 'RFC822').and_return([imap_fetch_mail])
|
||||
|
||||
result = described_class.new(channel: microsoft_channel).perform
|
||||
|
||||
expect(refresh_token_service).to have_received(:access_token)
|
||||
|
||||
expect(result.length).to eq 1
|
||||
expect(result[0].message_id).to eq email_object.message_id
|
||||
expect(imap).to have_received(:search).with(%w[SINCE 25-Oct-2020])
|
||||
expect(imap).to have_received(:fetch).with([1], 'BODY.PEEK[HEADER]')
|
||||
expect(imap).to have_received(:fetch).with(1, 'RFC822')
|
||||
expect(logger).to have_received(:info).with("[IMAP::FETCH_EMAIL_SERVICE] Fetching mails from #{microsoft_channel.email}, found 1.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user