chore: Add validations to campaign model

- Add validations to campaign model ensuring that the associated inbox belongs to one with in the campaign account.
This commit is contained in:
Shivam Mishra
2025-03-20 05:59:08 +05:30
committed by GitHub
parent cc8c05834a
commit 51ad80a61e
7 changed files with 60 additions and 24 deletions

View File

@@ -38,6 +38,7 @@ class Campaign < ApplicationRecord
validate :validate_url
validate :prevent_completed_campaign_from_update, on: :update
validate :sender_must_belong_to_account
validate :inbox_must_belong_to_account
belongs_to :account
belongs_to :inbox
@@ -92,6 +93,14 @@ class Campaign < ApplicationRecord
errors.add(:url, 'invalid') if inbox.inbox_type == 'Website' && !use_http_protocol
end
def inbox_must_belong_to_account
return unless inbox
return if inbox.account_id == account_id
errors.add(:inbox_id, 'must belong to the same account as the campaign')
end
def sender_must_belong_to_account
return unless sender

View File

@@ -15,7 +15,8 @@ RSpec.describe 'Campaigns API', type: :request do
context 'when it is an authenticated user' do
let(:agent) { create(:user, account: account, role: :agent) }
let(:administrator) { create(:user, account: account, role: :administrator) }
let!(:campaign) { create(:campaign, account: account, trigger_rules: { url: 'https://test.com' }) }
let(:inbox) { create(:inbox, account: account) }
let!(:campaign) { create(:campaign, account: account, inbox: inbox, trigger_rules: { url: 'https://test.com' }) }
it 'returns unauthorized for agents' do
get "/api/v1/accounts/#{account.id}/campaigns",
@@ -128,7 +129,7 @@ RSpec.describe 'Campaigns API', type: :request do
it 'creates a new oneoff campaign' do
twilio_sms = create(:channel_twilio_sms, account: account)
twilio_inbox = create(:inbox, channel: twilio_sms)
twilio_inbox = create(:inbox, channel: twilio_sms, account: account)
label1 = create(:label, account: account)
label2 = create(:label, account: account)
scheduled_at = 2.days.from_now

View File

@@ -2,12 +2,14 @@ require 'rails_helper'
RSpec.describe Campaigns::TriggerOneoffCampaignJob do
let(:account) { create(:account) }
let!(:twilio_sms) { create(:channel_twilio_sms) }
let!(:twilio_inbox) { create(:inbox, channel: twilio_sms) }
let!(:twilio_sms) { create(:channel_twilio_sms, account: account) }
let!(:twilio_inbox) { create(:inbox, channel: twilio_sms, account: account) }
let(:label1) { create(:label, account: account) }
let(:label2) { create(:label, account: account) }
let!(:campaign) { create(:campaign, inbox: twilio_inbox, audience: [{ type: 'Label', id: label1.id }, { type: 'Label', id: label2.id }]) }
let!(:campaign) do
create(:campaign, inbox: twilio_inbox, account: account, audience: [{ type: 'Label', id: label1.id }, { type: 'Label', id: label2.id }])
end
it 'enqueues the job' do
expect { described_class.perform_later(campaign) }.to have_enqueued_job(described_class)

View File

@@ -36,12 +36,12 @@ RSpec.describe TriggerScheduledItemsJob do
end
context 'when unexecuted Scheduled campaign jobs' do
let!(:twilio_sms) { create(:channel_twilio_sms) }
let!(:twilio_inbox) { create(:inbox, channel: twilio_sms) }
let!(:twilio_sms) { create(:channel_twilio_sms, account: account) }
let!(:twilio_inbox) { create(:inbox, channel: twilio_sms, account: account) }
it 'triggers Campaigns::TriggerOneoffCampaignJob' do
campaign = create(:campaign, inbox: twilio_inbox)
create(:campaign, inbox: twilio_inbox, scheduled_at: 10.days.after)
campaign = create(:campaign, inbox: twilio_inbox, account: account)
create(:campaign, inbox: twilio_inbox, account: account, scheduled_at: 10.days.after)
expect(Campaigns::TriggerOneoffCampaignJob).to receive(:perform_later).with(campaign).once
described_class.perform_now
end

View File

@@ -12,7 +12,7 @@ RSpec.describe Campaign do
let(:account) { create(:account) }
let(:website_channel) { create(:channel_widget, account: account) }
let(:website_inbox) { create(:inbox, channel: website_channel, account: account) }
let(:campaign) { build(:campaign, inbox: website_inbox, display_id: nil, trigger_rules: { url: 'https://test.com' }) }
let(:campaign) { build(:campaign, account: account, inbox: website_inbox, display_id: nil, trigger_rules: { url: 'https://test.com' }) }
before do
campaign.save!
@@ -29,9 +29,10 @@ RSpec.describe Campaign do
stub_request(:post, /graph.facebook.com/)
end
let!(:facebook_channel) { create(:channel_facebook_page) }
let!(:facebook_inbox) { create(:inbox, channel: facebook_channel) }
let(:campaign) { build(:campaign, inbox: facebook_inbox) }
let(:account) { create(:account) }
let!(:facebook_channel) { create(:channel_facebook_page, account: account) }
let!(:facebook_inbox) { create(:inbox, channel: facebook_channel, account: account) }
let(:campaign) { build(:campaign, inbox: facebook_inbox, account: account) }
it 'would not save the campaigns' do
expect(campaign.save).to be false
@@ -42,7 +43,7 @@ RSpec.describe Campaign do
context 'when a campaign is completed' do
let(:account) { create(:account) }
let(:web_widget) { create(:channel_widget, account: account) }
let!(:campaign) { create(:campaign, inbox: web_widget.inbox, campaign_status: :completed, trigger_rules: { url: 'https://test.com' }) }
let!(:campaign) { create(:campaign, account: account, inbox: web_widget.inbox, campaign_status: :completed, trigger_rules: { url: 'https://test.com' }) }
it 'would prevent further updates' do
campaign.title = 'new name'
@@ -63,9 +64,10 @@ RSpec.describe Campaign do
describe 'ensure_correct_campaign_attributes' do
context 'when Twilio SMS campaign' do
let!(:twilio_sms) { create(:channel_twilio_sms) }
let!(:twilio_inbox) { create(:inbox, channel: twilio_sms) }
let(:campaign) { build(:campaign, inbox: twilio_inbox) }
let(:account) { create(:account) }
let!(:twilio_sms) { create(:channel_twilio_sms, account: account) }
let!(:twilio_inbox) { create(:inbox, channel: twilio_sms, account: account) }
let(:campaign) { build(:campaign, account: account, inbox: twilio_inbox) }
it 'only saves campaign type as oneoff and wont leave scheduled_at empty' do
campaign.campaign_type = 'ongoing'
@@ -84,9 +86,10 @@ RSpec.describe Campaign do
end
context 'when SMS campaign' do
let!(:sms_channel) { create(:channel_sms) }
let!(:sms_inbox) { create(:inbox, channel: sms_channel) }
let(:campaign) { build(:campaign, inbox: sms_inbox) }
let(:account) { create(:account) }
let!(:sms_channel) { create(:channel_sms, account: account) }
let!(:sms_inbox) { create(:inbox, channel: sms_channel, account: account) }
let(:campaign) { build(:campaign, account: account, inbox: sms_inbox) }
it 'only saves campaign type as oneoff and wont leave scheduled_at empty' do
campaign.campaign_type = 'ongoing'
@@ -136,4 +139,25 @@ RSpec.describe Campaign do
)
end
end
context 'when validating inbox' do
let(:account) { create(:account) }
let(:other_account) { create(:account) }
let(:web_widget) { create(:channel_widget, account: account) }
let(:inbox) { create(:inbox, channel: web_widget, account: account) }
let(:other_account_inbox) { create(:inbox, account: other_account) }
it 'allows inbox from the same account' do
campaign = build(:campaign, inbox: inbox, account: account)
expect(campaign).to be_valid
end
it 'does not allow inbox from different account' do
campaign = build(:campaign, inbox: other_account_inbox, account: account)
expect(campaign).not_to be_valid
expect(campaign.errors[:inbox_id]).to include(
'must belong to the same account as the campaign'
)
end
end
end

View File

@@ -4,8 +4,8 @@ describe Sms::OneoffSmsCampaignService do
subject(:sms_campaign_service) { described_class.new(campaign: campaign) }
let(:account) { create(:account) }
let!(:sms_channel) { create(:channel_sms) }
let!(:sms_inbox) { create(:inbox, channel: sms_channel) }
let!(:sms_channel) { create(:channel_sms, account: account) }
let!(:sms_inbox) { create(:inbox, channel: sms_channel, account: account) }
let(:label1) { create(:label, account: account) }
let(:label2) { create(:label, account: account) }
let!(:campaign) do

View File

@@ -4,8 +4,8 @@ describe Twilio::OneoffSmsCampaignService do
subject(:sms_campaign_service) { described_class.new(campaign: campaign) }
let(:account) { create(:account) }
let!(:twilio_sms) { create(:channel_twilio_sms) }
let!(:twilio_inbox) { create(:inbox, channel: twilio_sms) }
let!(:twilio_sms) { create(:channel_twilio_sms, account: account) }
let!(:twilio_inbox) { create(:inbox, channel: twilio_sms, account: account) }
let(:label1) { create(:label, account: account) }
let(:label2) { create(:label, account: account) }
let!(:campaign) do