diff --git a/app/drops/contact_drop.rb b/app/drops/contact_drop.rb new file mode 100644 index 000000000..7d5a85f0e --- /dev/null +++ b/app/drops/contact_drop.rb @@ -0,0 +1,17 @@ +class ContactDrop < BaseDrop + def email + @obj.try(:email) + end + + def phone_number + @obj.try(:phone_number) + end + + def first_name + @obj.try(:name).try(:split).try(:first) + end + + def last_name + @obj.try(:name).try(:split).try(:last) if @obj.try(:name).try(:split).try(:size) > 1 + end +end diff --git a/app/drops/user_drop.rb b/app/drops/user_drop.rb index f10eee131..0ef91a724 100644 --- a/app/drops/user_drop.rb +++ b/app/drops/user_drop.rb @@ -2,4 +2,12 @@ class UserDrop < BaseDrop def available_name @obj.try(:available_name) end + + def first_name + @obj.try(:name).try(:split).try(:first) + end + + def last_name + @obj.try(:name).try(:split).try(:last) if @obj.try(:name).try(:split).try(:size) > 1 + end end diff --git a/app/models/concerns/liquidable.rb b/app/models/concerns/liquidable.rb new file mode 100644 index 000000000..0ef1594d1 --- /dev/null +++ b/app/models/concerns/liquidable.rb @@ -0,0 +1,36 @@ +module Liquidable + extend ActiveSupport::Concern + + included do + acts_as_taggable_on :labels + before_create :process_liquid_in_content + end + + private + + def message_drops + { + 'contact' => ContactDrop.new(conversation.contact), + 'agent' => UserDrop.new(sender), + 'conversation' => ConversationDrop.new(conversation), + 'inbox' => InboxDrop.new(inbox) + } + end + + def liquid_processable_message? + content.present? && message_type == 'outgoing' + end + + def process_liquid_in_content + return unless liquid_processable_message? + + template = Liquid::Template.parse(modified_liquid_content) + self.content = template.render(message_drops) + end + + def modified_liquid_content + # This regex is used to match the code blocks in the content + # We don't want to process liquid in code blocks + content.gsub(/`(.*?)`/m, '{% raw %}`\\1`{% endraw %}') + end +end diff --git a/app/models/message.rb b/app/models/message.rb index 50b8661f7..b7ee24a8b 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -32,6 +32,7 @@ class Message < ApplicationRecord include MessageFilterHelpers + include Liquidable NUMBER_OF_PERMITTED_ATTACHMENTS = 15 before_validation :ensure_content_type diff --git a/spec/drops/contact_drop_spec.rb b/spec/drops/contact_drop_spec.rb new file mode 100644 index 000000000..c87eb4e2a --- /dev/null +++ b/spec/drops/contact_drop_spec.rb @@ -0,0 +1,26 @@ +require 'rails_helper' + +describe ::ContactDrop do + subject(:contact_drop) { described_class.new(contact) } + + let!(:contact) { create(:contact) } + + context 'when first name' do + it 'returns first name' do + contact.update!(name: 'John Doe') + expect(subject.first_name).to eq 'John' + end + end + + context 'when last name' do + it 'returns the last name' do + contact.update!(name: 'John Doe') + expect(subject.last_name).to eq 'Doe' + end + + it 'returns empty when last name not present' do + contact.update!(name: 'John') + expect(subject.last_name).to be_nil + end + end +end diff --git a/spec/drops/user_drop_spec.rb b/spec/drops/user_drop_spec.rb new file mode 100644 index 000000000..1af9f3014 --- /dev/null +++ b/spec/drops/user_drop_spec.rb @@ -0,0 +1,26 @@ +require 'rails_helper' + +describe ::UserDrop do + subject(:user_drop) { described_class.new(user) } + + let!(:user) { create(:user) } + + context 'when first name' do + it 'returns first name' do + user.update!(name: 'John Doe') + expect(subject.first_name).to eq 'John' + end + end + + context 'when last name' do + it 'returns the last name' do + user.update!(name: 'John Doe') + expect(subject.last_name).to eq 'Doe' + end + + it 'returns empty when last name not present' do + user.update!(name: 'John') + expect(subject.last_name).to be_nil + end + end +end diff --git a/spec/models/concerns/liquidable_shared.rb b/spec/models/concerns/liquidable_shared.rb new file mode 100644 index 000000000..530b76a59 --- /dev/null +++ b/spec/models/concerns/liquidable_shared.rb @@ -0,0 +1,58 @@ +require 'rails_helper' + +shared_examples_for 'liqudable' do + context 'when liquid is present in content' do + let(:contact) { create(:contact, name: 'john', phone_number: '+912883') } + let(:conversation) { create(:conversation, id: 1, contact: contact) } + + context 'when message is incoming' do + let(:message) { build(:message, conversation: conversation, message_type: 'incoming') } + + it 'will not process liquid in content' do + message.content = 'hey {{contact.name}} how are you?' + message.save! + expect(message.content).to eq 'hey {{contact.name}} how are you?' + end + end + + context 'when message is outgoing' do + let(:message) { build(:message, conversation: conversation, message_type: 'outgoing') } + + it 'set replaces liquid variables in message' do + message.content = 'hey {{contact.name}} how are you?' + message.save! + expect(message.content).to eq 'hey john how are you?' + end + + it 'process liquid operators like default value' do + message.content = 'Can we send you an email at {{ contact.email | default: "default" }} ?' + message.save! + expect(message.content).to eq 'Can we send you an email at default ?' + end + + it 'return empty string when value is not available' do + message.content = 'Can we send you an email at {{contact.email}}?' + message.save! + expect(message.content).to eq 'Can we send you an email at ?' + end + + it 'will not process liquid tags in multiple code blocks' do + message.content = 'hey {{contact.name}} how are you? ```code: {{contact.name}}``` ``` code: {{contact.name}} ``` test `{{contact.name}}`' + message.save! + expect(message.content).to eq 'hey john how are you? ```code: {{contact.name}}``` ``` code: {{contact.name}} ``` test `{{contact.name}}`' + end + + it 'will not process liquid tags in single ticks' do + message.content = 'hey {{contact.name}} how are you? ` code: {{contact.name}} ` ` code: {{contact.name}} ` test' + message.save! + expect(message.content).to eq 'hey john how are you? ` code: {{contact.name}} ` ` code: {{contact.name}} ` test' + end + + it 'will not throw error for broken quotes' do + message.content = 'hey {{contact.name}} how are you? ` code: {{contact.name}} ` ` code: {{contact.name}} test' + message.save! + expect(message.content).to eq 'hey john how are you? ` code: {{contact.name}} ` ` code: john test' + end + end + end +end diff --git a/spec/models/message_spec.rb b/spec/models/message_spec.rb index de237415f..f88214fbe 100644 --- a/spec/models/message_spec.rb +++ b/spec/models/message_spec.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'rails_helper' +require Rails.root.join 'spec/models/concerns/liquidable_shared.rb' RSpec.describe Message, type: :model do context 'with validations' do @@ -9,6 +10,10 @@ RSpec.describe Message, type: :model do it { is_expected.to validate_presence_of(:account_id) } end + describe 'concerns' do + it_behaves_like 'liqudable' + end + describe '#reopen_conversation' do let(:conversation) { create(:conversation) } let(:message) { build(:message, message_type: :incoming, conversation: conversation) }