diff --git a/app/controllers/api/v1/accounts/microsoft/authorizations_controller.rb b/app/controllers/api/v1/accounts/microsoft/authorizations_controller.rb index a300b5f59..c65a3031d 100644 --- a/app/controllers/api/v1/accounts/microsoft/authorizations_controller.rb +++ b/app/controllers/api/v1/accounts/microsoft/authorizations_controller.rb @@ -6,8 +6,7 @@ class Api::V1::Accounts::Microsoft::AuthorizationsController < Api::V1::Accounts { redirect_uri: "#{base_url}/microsoft/callback", scope: scope, - state: state, - prompt: 'consent' + state: state } ) if redirect_url diff --git a/app/services/messages/webhook_content_normalizer.rb b/app/services/messages/webhook_content_normalizer.rb index b45f83ad0..5fa66fe9c 100644 --- a/app/services/messages/webhook_content_normalizer.rb +++ b/app/services/messages/webhook_content_normalizer.rb @@ -1,10 +1,11 @@ # Strips CommonMark hard line breaks from stored markdown source (backslash before newline). # ProseMirror / the dashboard editor emits this form so soft breaks survive as markdown; # webhook consumers expect plain newlines without a visible backslash (e.g. WhatsApp gateways). +# Also strips trailing newlines introduced by TipTap/ProseMirror trailing paragraph nodes. class Messages::WebhookContentNormalizer def self.normalize(text) return text if text.blank? - text.gsub(/\\\r?\n/, "\n") + text.gsub(/\\\r?\n/, "\n").sub(/(\r?\n)+\z/, '') end end diff --git a/app/services/whatsapp/incoming_message_base_service.rb b/app/services/whatsapp/incoming_message_base_service.rb index 722ac3e4d..9e0720f74 100644 --- a/app/services/whatsapp/incoming_message_base_service.rb +++ b/app/services/whatsapp/incoming_message_base_service.rb @@ -147,7 +147,7 @@ class Whatsapp::IncomingMessageBaseService def attach_location location = messages_data.first['location'] - location_name = location['name'] ? "#{location['name']}, #{location['address']}" : '' + location_name = (location['name'] ? "#{location['name']}, #{location['address']}" : '').first(255) @message.attachments.new( account_id: @message.account_id, file_type: file_content_type(message_type), diff --git a/spec/controllers/api/v1/accounts/microsoft/authorization_controller_spec.rb b/spec/controllers/api/v1/accounts/microsoft/authorization_controller_spec.rb index 60b05b36c..18a26a393 100644 --- a/spec/controllers/api/v1/accounts/microsoft/authorization_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/microsoft/authorization_controller_spec.rb @@ -43,6 +43,7 @@ RSpec.describe 'Microsoft Authorization API', type: :request do ] expect(params['scope']).to eq(expected_scope) expect(params['redirect_uri']).to eq(["#{ENV.fetch('FRONTEND_URL', 'http://localhost:3000')}/microsoft/callback"]) + expect(url).not_to match(/(?:\?|&)prompt=/) # Validate state parameter exists and can be decoded back to the account expect(params['state']).to be_present diff --git a/spec/services/messages/webhook_content_normalizer_spec.rb b/spec/services/messages/webhook_content_normalizer_spec.rb new file mode 100644 index 000000000..ca5023b9d --- /dev/null +++ b/spec/services/messages/webhook_content_normalizer_spec.rb @@ -0,0 +1,41 @@ +require 'rails_helper' + +RSpec.describe Messages::WebhookContentNormalizer do + describe '.normalize' do + it 'returns nil unchanged' do + expect(described_class.normalize(nil)).to be_nil + end + + it 'returns blank string unchanged' do + expect(described_class.normalize('')).to eq('') + end + + it 'strips trailing newlines added by TipTap/ProseMirror' do + expect(described_class.normalize("hello\n\n\n")).to eq('hello') + end + + it 'preserves intentional trailing spaces' do + expect(described_class.normalize("hello \n\n")).to eq('hello ') + end + + it 'replaces CommonMark hard line breaks (backslash-newline) with plain newlines' do + expect(described_class.normalize("hello\\\nworld")).to eq("hello\nworld") + end + + it 'replaces CommonMark hard line breaks with CRLF with plain newlines' do + expect(described_class.normalize("hello\\\r\nworld")).to eq("hello\nworld") + end + + it 'preserves intentional internal newlines' do + expect(described_class.normalize("line one\nline two")).to eq("line one\nline two") + end + + it 'strips trailing CRLF newlines without leaving dangling carriage returns' do + expect(described_class.normalize("hello\r\n\r\n")).to eq('hello') + end + + it 'handles both hard line breaks and trailing newlines together' do + expect(described_class.normalize("hello\\\nworld\n\n\n")).to eq("hello\nworld") + end + end +end diff --git a/spec/services/whatsapp/incoming_message_service_spec.rb b/spec/services/whatsapp/incoming_message_service_spec.rb index fe6b179c1..430aa1561 100644 --- a/spec/services/whatsapp/incoming_message_service_spec.rb +++ b/spec/services/whatsapp/incoming_message_service_spec.rb @@ -381,6 +381,32 @@ describe Whatsapp::IncomingMessageService do expect(location_attachment.coordinates_long).to eq(-122.3895553) expect(location_attachment.external_url).to eq('http://location_url.test') end + + it 'truncates long fallback titles to avoid dropping location messages' do + long_place_name = [ + 'Gremi de Fusters, 33, Edificio VIP Asima, Piso 2, Local 2, Norte', + '07009 Poligon industrial de Son Castello, Illes Balears, Espana' + ].join(', ') + source_id = 'wamid.long-location-fallback-title' + params = { + 'contacts' => [{ 'profile' => { 'name' => 'Sojan Jose' }, 'wa_id' => '2423423243' }], + 'messages' => [{ 'from' => '2423423243', 'id' => source_id, + 'location' => { 'id' => 'b1c68f38-8734-4ad3-b4a1-ef0c10d683', + :address => long_place_name, + :latitude => 37.7893768, + :longitude => -122.3895553, + :name => long_place_name, + :url => 'http://location_url.test' }, + 'timestamp' => '1633034394', 'type' => 'location' }] + }.with_indifferent_access + + expect { described_class.new(inbox: whatsapp_channel.inbox, params: params).perform } + .to change { Message.where(source_id: source_id).count }.from(0).to(1) + + location_attachment = Message.find_by!(source_id: source_id).attachments.first + expect(location_attachment.fallback_title).to eq("#{long_place_name}, #{long_place_name}".first(255)) + expect(location_attachment.fallback_title.length).to eq(255) + end end context 'when valid contact message params' do