diff --git a/app/javascript/dashboard/store/utils/api.js b/app/javascript/dashboard/store/utils/api.js index 79e8bc6c3..1da4c6643 100644 --- a/app/javascript/dashboard/store/utils/api.js +++ b/app/javascript/dashboard/store/utils/api.js @@ -27,7 +27,7 @@ export const getHeaderExpiry = response => export const setAuthCredentials = response => { const expiryDate = getHeaderExpiry(response); - Cookies.set('cw_d_session_info', response.headers, { + Cookies.set('cw_d_session_info', JSON.stringify(response.headers), { expires: differenceInDays(expiryDate, new Date()), }); setUser(response.data.data, expiryDate); diff --git a/app/javascript/sdk/cookieHelpers.js b/app/javascript/sdk/cookieHelpers.js index 64a7afbf7..286e01317 100644 --- a/app/javascript/sdk/cookieHelpers.js +++ b/app/javascript/sdk/cookieHelpers.js @@ -34,5 +34,12 @@ export const setCookieWithDomain = ( domain: baseDomain, }; + // if type of value is object, stringify it + // this is because js-cookies 3.0 removed builtin json support + // ref: https://github.com/js-cookie/js-cookie/releases/tag/v3.0.0 + if (typeof value === 'object') { + value = JSON.stringify(value); + } + Cookies.set(name, value, cookieOptions); }; diff --git a/app/javascript/sdk/specs/cookieHelpers.spec.js b/app/javascript/sdk/specs/cookieHelpers.spec.js index 7da09315f..6ede978fc 100644 --- a/app/javascript/sdk/specs/cookieHelpers.spec.js +++ b/app/javascript/sdk/specs/cookieHelpers.spec.js @@ -107,6 +107,26 @@ describe('setCookieWithDomain', () => { ); }); + it('should stringify the cookie value when setting', () => { + setCookieWithDomain( + 'myCookie', + { value: 'cookieValue' }, + { + baseDomain: 'example.com', + } + ); + + expect(Cookies.set).toHaveBeenCalledWith( + 'myCookie', + JSON.stringify({ value: 'cookieValue' }), + expect.objectContaining({ + expires: 365, + sameSite: 'Lax', + domain: 'example.com', + }) + ); + }); + it('should set a cookie with custom expiration, sameSite attribute, and specific base domain', () => { setCookieWithDomain('myCookie', 'cookieValue', { expires: 7, diff --git a/app/models/concerns/account_cache_revalidator.rb b/app/models/concerns/account_cache_revalidator.rb index 32d7500f8..b5ff5a473 100644 --- a/app/models/concerns/account_cache_revalidator.rb +++ b/app/models/concerns/account_cache_revalidator.rb @@ -2,7 +2,7 @@ module AccountCacheRevalidator extend ActiveSupport::Concern included do - after_commit :update_account_cache, on: [:create, :update] + after_commit :update_account_cache, on: [:create, :update, :destroy] end def update_account_cache diff --git a/app/models/concerns/cache_keys.rb b/app/models/concerns/cache_keys.rb index 7a01f0925..3ad9bbadc 100644 --- a/app/models/concerns/cache_keys.rb +++ b/app/models/concerns/cache_keys.rb @@ -4,6 +4,8 @@ module CacheKeys include CacheKeysHelper include Events::Types + CACHE_KEYS_EXPIRY = 72.hours + included do class_attribute :cacheable_models self.cacheable_models = [Label, Inbox, Team] @@ -18,26 +20,26 @@ module CacheKeys keys end - def invalidate_cache_key_for(key) - prefixed_cache_key = get_prefixed_cache_key(id, key) - Redis::Alfred.delete(prefixed_cache_key) - dispatch_cache_update_event - end - def update_cache_key(key) - prefixed_cache_key = get_prefixed_cache_key(id, key) - Redis::Alfred.set(prefixed_cache_key, Time.now.utc.to_i) + update_cache_key_for_account(id, key) dispatch_cache_update_event end def reset_cache_keys self.class.cacheable_models.each do |model| - invalidate_cache_key_for(model.name.underscore) + update_cache_key_for_account(id, model.name.underscore) end + + dispatch_cache_update_event end private + def update_cache_key_for_account(account_id, key) + prefixed_cache_key = get_prefixed_cache_key(account_id, key) + Redis::Alfred.setex(prefixed_cache_key, Time.now.utc.to_i, CACHE_KEYS_EXPIRY) + end + def dispatch_cache_update_event Rails.configuration.dispatcher.dispatch(ACCOUNT_CACHE_INVALIDATED, Time.zone.now, cache_keys: cache_keys, account: self) end diff --git a/app/services/instagram/webhooks_base_service.rb b/app/services/instagram/webhooks_base_service.rb index 26e2e486f..419270d70 100644 --- a/app/services/instagram/webhooks_base_service.rb +++ b/app/services/instagram/webhooks_base_service.rb @@ -24,7 +24,9 @@ class Instagram::WebhooksBaseService def update_instagram_profile_link(user) return unless user['username'] + # TODO: Remove this once we show the social_instagram_user_name in the UI instead of the username @contact.additional_attributes = @contact.additional_attributes.merge({ 'social_profiles': { 'instagram': user['username'] } }) + @contact.additional_attributes = @contact.additional_attributes.merge({ 'social_instagram_user_name': user['username'] }) @contact.save end end diff --git a/app/services/line/incoming_message_service.rb b/app/services/line/incoming_message_service.rb index 121ba1ce2..2d6988476 100644 --- a/app/services/line/incoming_message_service.rb +++ b/app/services/line/incoming_message_service.rb @@ -139,7 +139,14 @@ class Line::IncomingMessageService def contact_attributes { name: line_contact_info['displayName'], - avatar_url: line_contact_info['pictureUrl'] + avatar_url: line_contact_info['pictureUrl'], + additional_attributes: additional_attributes + } + end + + def additional_attributes + { + social_line_user_id: line_contact_info['userId'] } end diff --git a/app/services/telegram/incoming_message_service.rb b/app/services/telegram/incoming_message_service.rb index d4eeb53d5..d18994a00 100644 --- a/app/services/telegram/incoming_message_service.rb +++ b/app/services/telegram/incoming_message_service.rb @@ -78,8 +78,11 @@ class Telegram::IncomingMessageService def additional_attributes { + # TODO: Remove this once we show the social_telegram_user_name in the UI instead of the username username: telegram_params_username, - language_code: telegram_params_language_code + language_code: telegram_params_language_code, + social_telegram_user_id: telegram_params_from_id, + social_telegram_user_name: telegram_params_username } end diff --git a/spec/controllers/super_admin/accounts_controller_spec.rb b/spec/controllers/super_admin/accounts_controller_spec.rb index ee295472d..63cad0447 100644 --- a/spec/controllers/super_admin/accounts_controller_spec.rb +++ b/spec/controllers/super_admin/accounts_controller_spec.rb @@ -43,10 +43,13 @@ RSpec.describe 'Super Admin accounts API', type: :request do it 'shows the list of accounts' do expect(account.cache_keys.keys).to contain_exactly(:inbox, :label, :team) sign_in(super_admin, scope: :super_admin) + + now_timestamp = Time.now.utc.to_i post "/super_admin/accounts/#{account.id}/reset_cache" expect(response).to have_http_status(:redirect) expect(flash[:notice]).to eq('Cache keys cleared') - expect(account.reload.cache_keys.values.map(&:to_i)).to eq([0, 0, 0]) + + expect(account.reload.cache_keys.values.all? { |v| v.to_i == now_timestamp }).to be(true) end end end diff --git a/spec/jobs/webhooks/instagram_events_job_spec.rb b/spec/jobs/webhooks/instagram_events_job_spec.rb index e77d654cc..84bff722b 100644 --- a/spec/jobs/webhooks/instagram_events_job_spec.rb +++ b/spec/jobs/webhooks/instagram_events_job_spec.rb @@ -43,7 +43,7 @@ describe Webhooks::InstagramEventsJob do instagram_inbox.reload expect(instagram_inbox.contacts.count).to be 1 - expect(instagram_inbox.contacts.last.additional_attributes['social_profiles']['instagram']).to eq 'some_user_name' + expect(instagram_inbox.contacts.last.additional_attributes['social_instagram_user_name']).to eq 'some_user_name' expect(instagram_inbox.conversations.count).to be 1 expect(instagram_inbox.messages.count).to be 1 expect(instagram_inbox.messages.last.content_attributes['is_unsupported']).to be_nil @@ -59,7 +59,7 @@ describe Webhooks::InstagramEventsJob do instagram_inbox.reload expect(instagram_inbox.contacts.count).to be 1 - expect(instagram_inbox.contacts.last.additional_attributes['social_profiles']['instagram']).to eq 'some_user_name' + expect(instagram_inbox.contacts.last.additional_attributes['social_instagram_user_name']).to eq 'some_user_name' expect(instagram_inbox.conversations.count).to be 1 expect(instagram_inbox.messages.count).to be 1 @@ -170,7 +170,7 @@ describe Webhooks::InstagramEventsJob do instagram_inbox.reload expect(instagram_inbox.contacts.count).to be 1 - expect(instagram_inbox.contacts.last.additional_attributes['social_profiles']['instagram']).to eq 'some_user_name' + expect(instagram_inbox.contacts.last.additional_attributes['social_instagram_user_name']).to eq 'some_user_name' expect(instagram_inbox.conversations.count).to be 1 expect(instagram_inbox.messages.count).to be 1 expect(instagram_inbox.messages.last.content_attributes['is_unsupported']).to be true diff --git a/spec/models/concerns/cache_keys_spec.rb b/spec/models/concerns/cache_keys_spec.rb index 51d3c8b7a..33f2dc6b7 100644 --- a/spec/models/concerns/cache_keys_spec.rb +++ b/spec/models/concerns/cache_keys_spec.rb @@ -14,6 +14,7 @@ RSpec.describe CacheKeys do before do allow(Redis::Alfred).to receive(:delete) allow(Redis::Alfred).to receive(:set) + allow(Redis::Alfred).to receive(:setex) allow(Rails.configuration.dispatcher).to receive(:dispatch) end @@ -27,28 +28,11 @@ RSpec.describe CacheKeys do end end - describe '#invalidate_cache_key_for' do - it 'deletes the cache key' do - test_model.invalidate_cache_key_for('label') - expect(Redis::Alfred).to have_received(:delete).with('idb-cache-key-account-1-label') - end - - it 'dispatches a cache update event' do - test_model.invalidate_cache_key_for('label') - expect(Rails.configuration.dispatcher).to have_received(:dispatch).with( - CacheKeys::ACCOUNT_CACHE_INVALIDATED, - kind_of(ActiveSupport::TimeWithZone), - cache_keys: test_model.cache_keys, - account: test_model - ) - end - end - describe '#update_cache_key' do it 'updates the cache key' do allow(Time).to receive(:now).and_return(Time.parse('2023-05-29 00:00:00 UTC')) test_model.update_cache_key('label') - expect(Redis::Alfred).to have_received(:set).with('idb-cache-key-account-1-label', Time.now.utc.to_i) + expect(Redis::Alfred).to have_received(:setex).with('idb-cache-key-account-1-label', kind_of(Integer), CacheKeys::CACHE_KEYS_EXPIRY) end it 'dispatches a cache update event' do @@ -66,8 +50,20 @@ RSpec.describe CacheKeys do it 'invalidates all cache keys for cacheable models' do test_model.reset_cache_keys test_model.class.cacheable_models.each do |model| - expect(Redis::Alfred).to have_received(:delete).with("idb-cache-key-account-1-#{model.name.underscore}") + expect(Redis::Alfred).to have_received(:setex).with("idb-cache-key-account-1-#{model.name.underscore}", kind_of(Integer), + CacheKeys::CACHE_KEYS_EXPIRY) end end + + it 'dispatches a cache update event' do + test_model.reset_cache_keys + + expect(Rails.configuration.dispatcher).to have_received(:dispatch).with( + CacheKeys::ACCOUNT_CACHE_INVALIDATED, + kind_of(ActiveSupport::TimeWithZone), + cache_keys: test_model.cache_keys, + account: test_model + ) + end end end diff --git a/spec/models/inbox_spec.rb b/spec/models/inbox_spec.rb index 8bcb6bcd5..2a56d1fbc 100644 --- a/spec/models/inbox_spec.rb +++ b/spec/models/inbox_spec.rb @@ -250,5 +250,15 @@ RSpec.describe Inbox do cache_keys: inbox.account.cache_keys ) end + + it 'updates the cache key after update' do + expect(inbox.account).to receive(:update_cache_key).with('inbox') + inbox.update(name: 'New Name') + end + + it 'updates the cache key after touch' do + expect(inbox.account).to receive(:update_cache_key).with('inbox') + inbox.touch # rubocop:disable Rails/SkipsModelValidations + end end end diff --git a/spec/services/line/incoming_message_service_spec.rb b/spec/services/line/incoming_message_service_spec.rb index 0547cfbd0..037aa18fa 100644 --- a/spec/services/line/incoming_message_service_spec.rb +++ b/spec/services/line/incoming_message_service_spec.rb @@ -157,6 +157,7 @@ describe Line::IncomingMessageService do described_class.new(inbox: line_channel.inbox, params: params).perform expect(line_channel.inbox.conversations).not_to eq(0) expect(Contact.all.first.name).to eq('LINE Test') + expect(Contact.all.first.additional_attributes['social_line_user_id']).to eq('U4af4980629') expect(line_channel.inbox.messages.first.content).to eq('Hello, world') end end @@ -204,6 +205,7 @@ describe Line::IncomingMessageService do described_class.new(inbox: line_channel.inbox, params: image_params).perform expect(line_channel.inbox.conversations).not_to eq(0) expect(Contact.all.first.name).to eq('LINE Test') + expect(Contact.all.first.additional_attributes['social_line_user_id']).to eq('U4af4980629') expect(line_channel.inbox.messages.first.content).to be_nil expect(line_channel.inbox.messages.first.attachments.first.file_type).to eq('image') expect(line_channel.inbox.messages.first.attachments.first.file.blob.filename.to_s).to eq('media-354718.png') @@ -233,6 +235,7 @@ describe Line::IncomingMessageService do described_class.new(inbox: line_channel.inbox, params: video_params).perform expect(line_channel.inbox.conversations).not_to eq(0) expect(Contact.all.first.name).to eq('LINE Test') + expect(Contact.all.first.additional_attributes['social_line_user_id']).to eq('U4af4980629') expect(line_channel.inbox.messages.first.content).to be_nil expect(line_channel.inbox.messages.first.attachments.first.file_type).to eq('video') expect(line_channel.inbox.messages.first.attachments.first.file.blob.filename.to_s).to eq('media-354718.mp4') diff --git a/spec/services/telegram/incoming_message_service_spec.rb b/spec/services/telegram/incoming_message_service_spec.rb index c169f33fc..795202894 100644 --- a/spec/services/telegram/incoming_message_service_spec.rb +++ b/spec/services/telegram/incoming_message_service_spec.rb @@ -65,6 +65,8 @@ describe Telegram::IncomingMessageService do described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') + expect(Contact.all.first.additional_attributes['social_telegram_user_id']).to eq(23) + expect(Contact.all.first.additional_attributes['social_telegram_user_name']).to eq('sojan') expect(telegram_channel.inbox.messages.first.content).to eq('test') end end @@ -105,6 +107,8 @@ describe Telegram::IncomingMessageService do described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') + expect(Contact.all.first.additional_attributes['social_telegram_user_id']).to eq(23) + expect(Contact.all.first.additional_attributes['social_telegram_user_name']).to eq('sojan') expect(telegram_channel.inbox.messages.first.attachments.first.file_type).to eq('audio') end end @@ -277,6 +281,7 @@ describe Telegram::IncomingMessageService do described_class.new(inbox: telegram_channel.inbox, params: params).perform expect(telegram_channel.inbox.conversations.count).not_to eq(0) expect(Contact.all.first.name).to eq('Sojan Jose') + expect(Contact.all.first.additional_attributes['social_telegram_user_id']).to eq(5_171_248) expect(telegram_channel.inbox.messages.first.content).to eq('Option 1') end end