diff --git a/app/controllers/api/v1/accounts/macros_controller.rb b/app/controllers/api/v1/accounts/macros_controller.rb index e7946a54e..3e812ed63 100644 --- a/app/controllers/api/v1/accounts/macros_controller.rb +++ b/app/controllers/api/v1/accounts/macros_controller.rb @@ -14,6 +14,8 @@ class Api::V1::Accounts::MacrosController < Api::V1::Accounts::BaseController render json: { error: @macro.errors.messages }, status: :unprocessable_entity and return unless @macro.valid? @macro.save! + process_attachments + @macro end def show @@ -25,10 +27,21 @@ class Api::V1::Accounts::MacrosController < Api::V1::Accounts::BaseController head :ok end + def attach_file + file_blob = ActiveStorage::Blob.create_and_upload!( + key: nil, + io: params[:attachment].tempfile, + filename: params[:attachment].original_filename, + content_type: params[:attachment].content_type + ) + render json: { blob_key: file_blob.key, blob_id: file_blob.id } + end + def update ActiveRecord::Base.transaction do @macro.update!(macros_with_user) @macro.set_visibility(current_user, permitted_params) + process_attachments @macro.save! rescue StandardError => e Rails.logger.error e @@ -42,6 +55,17 @@ class Api::V1::Accounts::MacrosController < Api::V1::Accounts::BaseController head :ok end + def process_attachments + actions = @macro.actions.filter_map { |k, _v| k if k['action_name'] == 'send_attachment' } + return if actions.blank? + + actions.each do |action| + blob_id = action['action_params'] + blob = ActiveStorage::Blob.find_by(id: blob_id) + @macro.files.attach(blob) + end + end + def permitted_params params.permit( :name, :account_id, :visibility, diff --git a/app/models/macro.rb b/app/models/macro.rb index df002d680..188c98ab7 100644 --- a/app/models/macro.rb +++ b/app/models/macro.rb @@ -19,17 +19,21 @@ # index_macros_on_updated_by_id (updated_by_id) # class Macro < ApplicationRecord + include Rails.application.routes.url_helpers + belongs_to :account belongs_to :created_by, class_name: :User belongs_to :updated_by, class_name: :User + has_many_attached :files + enum visibility: { personal: 0, global: 1 } validate :json_actions_format - ACTIONS_ATTRS = %w[send_message add_label send_email_to_team assign_team assign_best_agent send_webhook_event mute_conversation change_status - resolve_conversation snooze_conversation].freeze + ACTIONS_ATTRS = %w[send_message add_label assign_team assign_best_agent mute_conversation change_status + resolve_conversation snooze_conversation send_email_transcript send_attachment].freeze def set_visibility(user, params) self.visibility = params[:visibility] @@ -47,6 +51,20 @@ class Macro < ApplicationRecord params[:page] || 1 end + def file_base_data + files.map do |file| + { + id: file.id, + macro_id: id, + file_type: file.content_type, + account_id: account_id, + file_url: url_for(file), + blob_id: file.blob_id, + filename: file.filename.to_s + } + end + end + private def json_actions_format diff --git a/app/policies/macro_policy.rb b/app/policies/macro_policy.rb index f1a83bc4a..3c67424a5 100644 --- a/app/policies/macro_policy.rb +++ b/app/policies/macro_policy.rb @@ -22,4 +22,8 @@ class MacroPolicy < ApplicationPolicy def execute? true end + + def attach_file? + true + end end diff --git a/app/services/action_service.rb b/app/services/action_service.rb index 2983b3050..192979da5 100644 --- a/app/services/action_service.rb +++ b/app/services/action_service.rb @@ -39,6 +39,12 @@ class ActionService @conversation.update!(team_id: team_ids[0]) end + def send_email_transcript(emails) + emails.each do |email| + ConversationReplyMailer.with(account: @conversation.account).conversation_transcript(@conversation, email)&.deliver_later + end + end + private def agent_belongs_to_account?(agent_ids) diff --git a/app/services/automation_rules/action_service.rb b/app/services/automation_rules/action_service.rb index c28285fe7..ac7ce5d96 100644 --- a/app/services/automation_rules/action_service.rb +++ b/app/services/automation_rules/action_service.rb @@ -26,21 +26,15 @@ class AutomationRules::ActionService < ActionService return unless @rule.files.attached? - blob = ActiveStorage::Blob.find(blob_ids) + blobs = ActiveStorage::Blob.where(id: blob_ids) - return if blob.blank? + return if blobs.blank? - params = { content: nil, private: false, attachments: blob } + params = { content: nil, private: false, attachments: blobs } mb = Messages::MessageBuilder.new(nil, @conversation, params) mb.perform end - def send_email_transcript(emails) - emails.each do |email| - ConversationReplyMailer.with(account: @conversation.account).conversation_transcript(@conversation, email)&.deliver_later - end - end - def send_webhook_event(webhook_url) payload = @conversation.webhook_data.merge(event: "automation_event.#{@rule.event_name}") WebhookJob.perform_later(webhook_url[0], payload) diff --git a/app/services/macros/execution_service.rb b/app/services/macros/execution_service.rb index f9a32ef68..1fdbbb103 100644 --- a/app/services/macros/execution_service.rb +++ b/app/services/macros/execution_service.rb @@ -21,18 +21,29 @@ class Macros::ExecutionService < ActionService private - def send_webhook_event(webhook_url) - payload = @conversation.webhook_data.merge(event: "macro_event.#{@macro.name}") - WebhookJob.perform_later(webhook_url[0], payload) - end - def send_message(message) return if conversation_a_tweet? - params = { content: message[0], private: false, content_attributes: { macro_id: @macro.id } } - mb = Messages::MessageBuilder.new(nil, @conversation, params) + params = { content: message[0], private: false } + + # Added reload here to ensure conversation us persistent with the latest updates + mb = Messages::MessageBuilder.new(nil, @conversation.reload, params) mb.perform end - def send_email_to_team(_params); end + def send_attachment(blob_ids) + return if conversation_a_tweet? + + return unless @macro.files.attached? + + blobs = ActiveStorage::Blob.where(id: blob_ids) + + return if blobs.blank? + + params = { content: nil, private: false, attachments: blobs } + + # Added reload here to ensure conversation us persistent with the latest updates + mb = Messages::MessageBuilder.new(nil, @conversation.reload, params) + mb.perform + end end diff --git a/app/views/api/v1/models/_macro.json.jbuilder b/app/views/api/v1/models/_macro.json.jbuilder index 5a13d1ba6..c1a420508 100644 --- a/app/views/api/v1/models/_macro.json.jbuilder +++ b/app/views/api/v1/models/_macro.json.jbuilder @@ -16,3 +16,4 @@ end json.account_id macro.account_id json.actions macro.actions +json.files macro.file_base_data if macro.files.any? diff --git a/config/routes.rb b/config/routes.rb index 23c6f1f8a..d11269a97 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -58,9 +58,8 @@ Rails.application.routes.draw do post :attach_file, on: :collection end resources :macros, only: [:index, :create, :show, :update, :destroy] do - member do - post :execute - end + post :execute, on: :member + post :attach_file, on: :collection end resources :campaigns, only: [:index, :create, :show, :update, :destroy] resources :dashboard_apps, only: [:index, :show, :create, :update, :destroy] diff --git a/spec/controllers/api/v1/accounts/macros_controller_spec.rb b/spec/controllers/api/v1/accounts/macros_controller_spec.rb index 7e4be84b1..0bc86ca67 100644 --- a/spec/controllers/api/v1/accounts/macros_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/macros_controller_spec.rb @@ -117,6 +117,40 @@ RSpec.describe 'Api::V1::Accounts::MacrosController', type: :request do expect(json_response['payload']['visibility']).to eql('personal') expect(json_response['payload']['created_by']['id']).to eql(agent.id) end + + it 'Saves file in the macros actions to send an attachments' do + file = fixture_file_upload(Rails.root.join('spec/assets/avatar.png'), 'image/png') + + post "/api/v1/accounts/#{account.id}/macros/attach_file", + headers: administrator.create_new_auth_token, + params: { attachment: file } + + expect(response).to have_http_status(:success) + + blob = JSON.parse(response.body) + + expect(blob['blob_key']).to be_present + expect(blob['blob_id']).to be_present + + params[:actions] = [ + { + 'action_name': :send_message, + 'action_params': ['Welcome to the chatwoot platform.'] + }, + { + 'action_name': :send_attachment, + 'action_params': [blob['blob_id']] + } + ] + + post "/api/v1/accounts/#{account.id}/macros", + headers: administrator.create_new_auth_token, + params: params + + macro = account.macros.last + expect(macro.files.presence).to be_truthy + expect(macro.files.count).to eq(1) + end end end @@ -196,12 +230,6 @@ RSpec.describe 'Api::V1::Accounts::MacrosController', type: :request do create(:account_user, user: user_1, account: account) macro.update!(actions: [ - { - 'action_name' => 'send_email_to_team', 'action_params' => [{ - 'message' => 'Please pay attention to this conversation, its from high priority customer', - 'team_ids' => [team.id] - }] - }, { 'action_name' => 'assign_team', 'action_params' => [team.id] }, { 'action_name' => 'add_label', 'action_params' => %w[support priority_customer] }, { 'action_name' => 'snooze_conversation' },