Merge remote-tracking branch 'origin/feat/whatsapp-embedded-signup' into test-pdf-support-captain

This commit is contained in:
Tanmay Deep Sharma
2025-07-11 18:09:26 +07:00
18 changed files with 570 additions and 96 deletions
+1 -1
View File
@@ -283,7 +283,7 @@ Rails/RedundantActiveRecordAllMethod:
Enabled: false
Layout/TrailingEmptyLines:
Enabled: false
Enabled: true
Style/SafeNavigationChainLength:
Enabled: false
@@ -0,0 +1,64 @@
class Api::V1::Accounts::Whatsapp::AuthorizationsController < Api::V1::Accounts::BaseController
before_action :validate_feature_enabled!
# POST /api/v1/accounts/:account_id/whatsapp/authorization
# Handles the embedded signup callback data from the Facebook SDK
def create
validate_embedded_signup_params!
channel = process_embedded_signup
render_success_response(channel.inbox)
rescue StandardError => e
render_error_response(e)
end
private
def process_embedded_signup
service = Whatsapp::EmbeddedSignupService.new(
account: Current.account,
code: params[:code],
business_id: params[:business_id],
waba_id: params[:waba_id],
phone_number_id: params[:phone_number_id]
)
service.perform
end
def render_success_response(inbox)
render json: {
success: true,
id: inbox.id,
name: inbox.name,
channel_type: 'whatsapp'
}
end
def render_error_response(error)
Rails.logger.error "[WHATSAPP AUTHORIZATION] Embedded signup error: #{error.message}"
Rails.logger.error error.backtrace.join("\n")
render json: {
success: false,
error: error.message
}, status: :unprocessable_entity
end
def validate_feature_enabled!
return if Current.account.feature_whatsapp_embedded_signup?
render json: {
success: false,
error: 'WhatsApp embedded signup is not enabled for this account'
}, status: :forbidden
end
def validate_embedded_signup_params!
missing_params = []
missing_params << 'code' if params[:code].blank?
missing_params << 'business_id' if params[:business_id].blank?
missing_params << 'waba_id' if params[:waba_id].blank?
return if missing_params.empty?
raise ArgumentError, "Required parameters are missing: #{missing_params.join(', ')}"
end
end
@@ -1,54 +0,0 @@
class Api::V1::Accounts::Whatsapp::CallbacksController < Api::V1::Accounts::BaseController
before_action :validate_whatsapp_params, only: [:embedded_signup]
def embedded_signup
channel = process_signup
@inbox = channel.inbox
rescue StandardError => e
handle_signup_error(e)
end
def config
render json: {
status: 'ready',
app_id: GlobalConfigService.load('WHATSAPP_APP_ID', ''),
config_id: GlobalConfigService.load('WHATSAPP_CONFIGURATION_ID', '')
}
end
private
def validate_whatsapp_params
return render_error('Missing authorization code', 'Authorization code is required') if params[:code].blank?
return render_error('Missing business_id', 'business_id is required') if params[:business_id].blank?
return render_error('Missing waba_id', 'waba_id is required') if params[:waba_id].blank?
end
def render_error(error, message)
render json: {
error: error,
message: message
}, status: :bad_request
end
def process_signup
service = Whatsapp::EmbeddedSignupService.new(
account: Current.account,
code: params[:code],
business_id: params[:business_id],
waba_id: params[:waba_id],
phone_number_id: params[:phone_number_id]
)
service.perform
end
def handle_signup_error(error)
Rails.logger.error("[WHATSAPP] Embedded signup processing error: #{error.message}")
Rails.logger.error(error.backtrace.join("\n"))
render json: {
error: 'signup_failed',
message: error.message
}, status: :bad_request
end
end
@@ -123,7 +123,7 @@ export function useWhatsappEmbeddedSignup() {
// Send both auth code and business info together (synchronous flow)
const accountId = store.getters.getCurrentAccountId;
const response = await fetch(
`/api/v1/accounts/${accountId}/whatsapp/callbacks/embedded_signup`,
`/api/v1/accounts/${accountId}/whatsapp/authorization`,
{
method: 'POST',
headers: {
@@ -1,12 +1,16 @@
<script setup>
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useAlert } from 'dashboard/composables';
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
import { useAdmin } from 'dashboard/composables/useAdmin';
import SettingsLayout from '../SettingsLayout.vue';
import BaseSettingsHeader from '../components/BaseSettingsHeader.vue';
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useStoreGetters, useStore } from 'dashboard/composables/store';
import {
useMapGetter,
useStoreGetters,
useStore,
} from 'dashboard/composables/store';
import ChannelName from './components/ChannelName.vue';
import ChannelIcon from 'next/icon/ChannelIcon.vue';
import Button from 'dashboard/components-next/button/Button.vue';
@@ -19,7 +23,12 @@ const { isAdmin } = useAdmin();
const showDeletePopup = ref(false);
const selectedInbox = ref({});
const inboxesList = computed(() => getters['inboxes/getInboxes'].value);
const inboxes = useMapGetter('inboxes/getInboxes');
const inboxesList = computed(() => {
return inboxes.value?.slice().sort((a, b) => a.name.localeCompare(b.name));
});
const uiFlags = computed(() => getters['inboxes/getUIFlags'].value);
const deleteConfirmText = computed(
@@ -2,6 +2,7 @@
import { computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { useStore } from 'vuex';
import Twilio from './Twilio.vue';
import ThreeSixtyDialogWhatsapp from './360DialogWhatsapp.vue';
import CloudWhatsapp from './CloudWhatsapp.vue';
@@ -12,6 +13,7 @@ import twilioIcon from 'dashboard/assets/images/twilio.png';
const route = useRoute();
const router = useRouter();
const { t } = useI18n();
const store = useStore();
const PROVIDER_TYPES = {
WHATSAPP: 'whatsapp',
@@ -28,6 +30,14 @@ const hasWhatsappAppId = computed(() => {
);
});
const isWhatsappEmbeddedSignupEnabled = computed(() => {
const accountId = route.params.accountId;
return store.getters['accounts/isFeatureEnabledonAccount'](
accountId,
'whatsapp_embedded_signup'
);
});
const selectedProvider = computed(() => route.query.provider);
const showProviderSelection = computed(() => !selectedProvider.value);
@@ -58,6 +68,11 @@ const selectProvider = providerValue => {
};
const shouldShowEmbeddedSignup = provider => {
// Check if the feature is enabled for the account
if (!isWhatsappEmbeddedSignupEnabled.value) {
return false;
}
return (
(provider === PROVIDER_TYPES.WHATSAPP && hasWhatsappAppId.value) ||
provider === PROVIDER_TYPES.WHATSAPP_EMBEDDED
@@ -65,7 +80,15 @@ const shouldShowEmbeddedSignup = provider => {
};
const shouldShowCloudWhatsapp = provider => {
return provider === PROVIDER_TYPES.WHATSAPP && !hasWhatsappAppId.value;
// If embedded signup feature is enabled and app ID is configured, don't show cloud whatsapp
if (isWhatsappEmbeddedSignupEnabled.value && hasWhatsappAppId.value) {
return false;
}
// Show cloud whatsapp when:
// 1. Provider is whatsapp AND
// 2. Either no app ID is configured OR embedded signup feature is disabled
return provider === PROVIDER_TYPES.WHATSAPP;
};
</script>
-1
View File
@@ -5,7 +5,6 @@ class Webhooks::WhatsappEventsJob < ApplicationJob
channel = find_channel_from_whatsapp_business_payload(params)
if channel_is_inactive?(channel)
Rails.logger.info("Channel is inactive: #{channel.inspect}")
Rails.logger.warn("Inactive WhatsApp channel: #{channel&.phone_number || "unknown - #{params[:phone_number]}"}")
return
end
+21
View File
@@ -33,6 +33,7 @@ class Channel::Whatsapp < ApplicationRecord
validate :validate_provider_config
after_create :sync_templates
after_create_commit :setup_webhooks
def name
'Whatsapp'
@@ -67,4 +68,24 @@ class Channel::Whatsapp < ApplicationRecord
def validate_provider_config
errors.add(:provider_config, 'Invalid Credentials') unless provider_service.validate_provider_config?
end
def setup_webhooks
return unless provider == 'whatsapp_cloud'
# Only setup webhooks for embedded signup flow
# Manual setup flow expects users to configure webhooks themselves
return unless provider_config['source'] == 'embedded_signup'
# Only setup webhooks if we have the necessary configuration
business_account_id = provider_config['business_account_id']
api_key = provider_config['api_key']
return if business_account_id.blank? || api_key.blank?
Whatsapp::WebhookSetupService.new(self, business_account_id, api_key).perform
rescue StandardError => e
Rails.logger.error "[WHATSAPP] Webhook setup failed: #{e.message}"
# Don't raise the error to prevent channel creation from failing
# Webhooks can be retried later
end
end
@@ -72,4 +72,4 @@ class Whatsapp::ChannelCreationService
business_name = @phone_info[:business_name] || @waba_info[:business_name]
"#{business_name} WhatsApp"
end
end
end
@@ -10,8 +10,6 @@ class Whatsapp::EmbeddedSignupService
def perform
validate_parameters!
GlobalConfig.clear_cache
# Exchange code for user access token
access_token = Whatsapp::TokenExchangeService.new(@code).perform
@@ -23,12 +21,9 @@ class Whatsapp::EmbeddedSignupService
# Create channel
waba_info = { waba_id: @waba_id, business_name: phone_info[:business_name] }
channel = Whatsapp::ChannelCreationService.new(@account, waba_info, phone_info, access_token).perform
# Setup webhook
Whatsapp::WebhookSetupService.new(channel, @waba_id, access_token).perform
channel
# Webhook setup is now handled in the channel after_create_commit callback
Whatsapp::ChannelCreationService.new(@account, waba_info, phone_info, access_token).perform
rescue StandardError => e
Rails.logger.error("[WHATSAPP] Embedded signup failed: #{e.message}")
raise e
+12 -13
View File
@@ -1,6 +1,5 @@
class Whatsapp::FacebookApiClient
include HTTParty
base_uri 'https://graph.facebook.com'
BASE_URI = 'https://graph.facebook.com'.freeze
def initialize(access_token = nil)
@access_token = access_token
@@ -8,8 +7,8 @@ class Whatsapp::FacebookApiClient
end
def exchange_code_for_token(code)
response = self.class.get(
"/#{@api_version}/oauth/access_token",
response = HTTParty.get(
"#{BASE_URI}/#{@api_version}/oauth/access_token",
query: {
client_id: GlobalConfigService.load('WHATSAPP_APP_ID', ''),
client_secret: GlobalConfigService.load('WHATSAPP_APP_SECRET', ''),
@@ -21,8 +20,8 @@ class Whatsapp::FacebookApiClient
end
def fetch_phone_numbers(waba_id)
response = self.class.get(
"/#{@api_version}/#{waba_id}/phone_numbers",
response = HTTParty.get(
"#{BASE_URI}/#{@api_version}/#{waba_id}/phone_numbers",
query: { access_token: @access_token }
)
@@ -30,8 +29,8 @@ class Whatsapp::FacebookApiClient
end
def debug_token(input_token)
response = self.class.get(
"/#{@api_version}/debug_token",
response = HTTParty.get(
"#{BASE_URI}/#{@api_version}/debug_token",
query: {
input_token: input_token,
access_token: build_app_access_token
@@ -42,8 +41,8 @@ class Whatsapp::FacebookApiClient
end
def register_phone_number(phone_number_id, pin)
response = self.class.post(
"/#{@api_version}/#{phone_number_id}/register",
response = HTTParty.post(
"#{BASE_URI}/#{@api_version}/#{phone_number_id}/register",
headers: request_headers,
body: { messaging_product: 'whatsapp', pin: pin.to_s }.to_json
)
@@ -52,8 +51,8 @@ class Whatsapp::FacebookApiClient
end
def subscribe_waba_webhook(waba_id, callback_url, verify_token)
response = self.class.post(
"/#{@api_version}/#{waba_id}/subscribed_apps",
response = HTTParty.post(
"#{BASE_URI}/#{@api_version}/#{waba_id}/subscribed_apps",
headers: request_headers,
body: {
override_callback_uri: callback_url,
@@ -84,4 +83,4 @@ class Whatsapp::FacebookApiClient
response.parsed_response
end
end
end
+3
View File
@@ -176,3 +176,6 @@
- name: notion_integration
display_name: Notion Integration
enabled: false
- name: whatsapp_embedded_signup
display_name: WhatsApp Embedded Signup
enabled: false
+1 -6
View File
@@ -237,12 +237,7 @@ Rails.application.routes.draw do
end
namespace :whatsapp do
resources :callbacks, only: [] do
collection do
post :embedded_signup
get :config
end
end
resource :authorization, only: [:create]
end
resources :webhooks, only: [:index, :create, :update, :destroy]
@@ -0,0 +1,279 @@
require 'rails_helper'
RSpec.describe Api::V1::Accounts::Whatsapp::AuthorizationsController, type: :controller do
let(:account) { create(:account) }
let(:agent) { create(:user, account: account, role: :agent) }
describe 'POST #create' do
context 'when user is not authenticated' do
it 'returns unauthorized' do
post :create, params: { account_id: account.id }
expect(response).to have_http_status(:unauthorized)
end
end
context 'when user is authenticated' do
before { sign_in(agent) }
context 'when feature is not enabled' do
before do
account.disable_features!(:whatsapp_embedded_signup)
end
it 'returns forbidden' do
post :create, params: {
account_id: account.id,
code: 'test_code',
business_id: 'test_business_id',
waba_id: 'test_waba_id'
}
expect(response).to have_http_status(:forbidden)
expect(response.parsed_body['error']).to eq('WhatsApp embedded signup is not enabled for this account')
end
end
context 'when feature is enabled' do
before do
account.enable_features!(:whatsapp_embedded_signup)
end
it 'returns unprocessable entity when code is missing' do
post :create, params: {
account_id: account.id,
business_id: 'test_business_id',
waba_id: 'test_waba_id'
}
expect(response).to have_http_status(:unprocessable_entity)
expect(response.parsed_body['error']).to include('code')
end
it 'returns unprocessable entity when business_id is missing' do
post :create, params: {
account_id: account.id,
code: 'test_code',
waba_id: 'test_waba_id'
}
expect(response).to have_http_status(:unprocessable_entity)
expect(response.parsed_body['error']).to include('business_id')
end
it 'returns unprocessable entity when waba_id is missing' do
post :create, params: {
account_id: account.id,
code: 'test_code',
business_id: 'test_business_id'
}
expect(response).to have_http_status(:unprocessable_entity)
expect(response.parsed_body['error']).to include('waba_id')
end
it 'creates whatsapp channel successfully' do
whatsapp_channel = create(:channel_whatsapp, account: account)
inbox = create(:inbox, account: account, channel: whatsapp_channel)
embedded_signup_service = instance_double(Whatsapp::EmbeddedSignupService)
allow(Whatsapp::EmbeddedSignupService).to receive(:new).and_return(embedded_signup_service)
allow(embedded_signup_service).to receive(:perform).and_return(whatsapp_channel)
allow(whatsapp_channel).to receive(:inbox).and_return(inbox)
# Stub webhook setup service to prevent HTTP calls
webhook_service = instance_double(Whatsapp::WebhookSetupService)
allow(Whatsapp::WebhookSetupService).to receive(:new).and_return(webhook_service)
allow(webhook_service).to receive(:perform)
post :create, params: {
account_id: account.id,
code: 'test_code',
business_id: 'test_business_id',
waba_id: 'test_waba_id',
phone_number_id: 'test_phone_id'
}
expect(response).to have_http_status(:success)
response_data = response.parsed_body
expect(response_data['success']).to be true
expect(response_data['id']).to eq(inbox.id)
expect(response_data['name']).to eq(inbox.name)
expect(response_data['channel_type']).to eq('whatsapp')
end
it 'calls the embedded signup service with correct parameters' do
whatsapp_channel = create(:channel_whatsapp, account: account)
inbox = create(:inbox, account: account, channel: whatsapp_channel)
embedded_signup_service = instance_double(Whatsapp::EmbeddedSignupService)
expect(Whatsapp::EmbeddedSignupService).to receive(:new).with(
account: account,
code: 'test_code',
business_id: 'test_business_id',
waba_id: 'test_waba_id',
phone_number_id: 'test_phone_id'
).and_return(embedded_signup_service)
allow(embedded_signup_service).to receive(:perform).and_return(whatsapp_channel)
allow(whatsapp_channel).to receive(:inbox).and_return(inbox)
# Stub webhook setup service
webhook_service = instance_double(Whatsapp::WebhookSetupService)
allow(Whatsapp::WebhookSetupService).to receive(:new).and_return(webhook_service)
allow(webhook_service).to receive(:perform)
post :create, params: {
account_id: account.id,
code: 'test_code',
business_id: 'test_business_id',
waba_id: 'test_waba_id',
phone_number_id: 'test_phone_id'
}
end
it 'accepts phone_number_id as optional parameter' do
whatsapp_channel = create(:channel_whatsapp, account: account)
inbox = create(:inbox, account: account, channel: whatsapp_channel)
embedded_signup_service = instance_double(Whatsapp::EmbeddedSignupService)
expect(Whatsapp::EmbeddedSignupService).to receive(:new).with(
account: account,
code: 'test_code',
business_id: 'test_business_id',
waba_id: 'test_waba_id',
phone_number_id: nil
).and_return(embedded_signup_service)
allow(embedded_signup_service).to receive(:perform).and_return(whatsapp_channel)
allow(whatsapp_channel).to receive(:inbox).and_return(inbox)
# Stub webhook setup service
webhook_service = instance_double(Whatsapp::WebhookSetupService)
allow(Whatsapp::WebhookSetupService).to receive(:new).and_return(webhook_service)
allow(webhook_service).to receive(:perform)
post :create, params: {
account_id: account.id,
code: 'test_code',
business_id: 'test_business_id',
waba_id: 'test_waba_id'
}
expect(response).to have_http_status(:success)
end
it 'returns unprocessable entity when service fails' do
allow(Whatsapp::EmbeddedSignupService).to receive(:new).and_raise(StandardError, 'Service error')
post :create, params: {
account_id: account.id,
code: 'test_code',
business_id: 'test_business_id',
waba_id: 'test_waba_id'
}
expect(response).to have_http_status(:unprocessable_entity)
response_data = response.parsed_body
expect(response_data['success']).to be false
expect(response_data['error']).to eq('Service error')
end
it 'logs error when service fails' do
allow(Whatsapp::EmbeddedSignupService).to receive(:new).and_raise(StandardError, 'Service error')
expect(Rails.logger).to receive(:error).with(/\[WHATSAPP AUTHORIZATION\] Embedded signup error: Service error/)
expect(Rails.logger).to receive(:error).with(/authorizations_controller/)
post :create, params: {
account_id: account.id,
code: 'test_code',
business_id: 'test_business_id',
waba_id: 'test_waba_id'
}
end
it 'handles token exchange errors' do
allow(Whatsapp::EmbeddedSignupService).to receive(:new)
.and_raise(StandardError, 'Invalid authorization code')
post :create, params: {
account_id: account.id,
code: 'invalid_code',
business_id: 'test_business_id',
waba_id: 'test_waba_id'
}
expect(response).to have_http_status(:unprocessable_entity)
expect(response.parsed_body['error']).to eq('Invalid authorization code')
end
it 'handles channel already exists error' do
allow(Whatsapp::EmbeddedSignupService).to receive(:new)
.and_raise(StandardError, 'Channel already exists')
post :create, params: {
account_id: account.id,
code: 'test_code',
business_id: 'test_business_id',
waba_id: 'test_waba_id'
}
expect(response).to have_http_status(:unprocessable_entity)
expect(response.parsed_body['error']).to eq('Channel already exists')
end
end
context 'when user is not authorized for the account' do
let(:other_account) { create(:account) }
before do
account.enable_features!(:whatsapp_embedded_signup)
end
it 'returns unauthorized' do
post :create, params: {
account_id: other_account.id,
code: 'test_code',
business_id: 'test_business_id',
waba_id: 'test_waba_id'
}
expect(response).to have_http_status(:unauthorized)
end
end
context 'when user is an administrator' do
let(:administrator) { create(:user, account: account, role: :administrator) }
before do
sign_in(administrator)
account.enable_features!(:whatsapp_embedded_signup)
end
it 'allows channel creation' do
embedded_signup_service = instance_double(Whatsapp::EmbeddedSignupService)
whatsapp_channel = create(:channel_whatsapp, account: account)
inbox = create(:inbox, account: account, channel: whatsapp_channel)
allow(Whatsapp::EmbeddedSignupService).to receive(:new).and_return(embedded_signup_service)
allow(embedded_signup_service).to receive(:perform).and_return(whatsapp_channel)
allow(whatsapp_channel).to receive(:inbox).and_return(inbox)
# Stub webhook setup service
webhook_service = instance_double(Whatsapp::WebhookSetupService)
allow(Whatsapp::WebhookSetupService).to receive(:new).and_return(webhook_service)
allow(webhook_service).to receive(:perform)
post :create, params: {
account_id: account.id,
code: 'test_code',
business_id: 'test_business_id',
waba_id: 'test_waba_id'
}
expect(response).to have_http_status(:success)
end
end
end
end
end
+81
View File
@@ -61,4 +61,85 @@ RSpec.describe Channel::Whatsapp do
expect(channel.provider_config['webhook_verify_token']).to eq '123'
end
end
describe 'webhook setup after creation' do
let(:account) { create(:account) }
let(:webhook_service) { instance_double(Whatsapp::WebhookSetupService) }
before do
allow(Whatsapp::WebhookSetupService).to receive(:new).and_return(webhook_service)
allow(webhook_service).to receive(:perform)
end
context 'when channel is created through embedded signup' do
it 'sets up webhooks automatically' do
expect(Whatsapp::WebhookSetupService).to receive(:new).with(
anything,
'test_waba_id',
'test_access_token'
)
expect(webhook_service).to receive(:perform)
create(:channel_whatsapp,
account: account,
provider: 'whatsapp_cloud',
provider_config: {
'source' => 'embedded_signup',
'business_account_id' => 'test_waba_id',
'api_key' => 'test_access_token'
},
validate_provider_config: false,
sync_templates: false)
end
it 'does not raise error if webhook setup fails' do
allow(webhook_service).to receive(:perform).and_raise(StandardError, 'Webhook error')
expect do
create(:channel_whatsapp,
account: account,
provider: 'whatsapp_cloud',
provider_config: {
'source' => 'embedded_signup',
'business_account_id' => 'test_waba_id',
'api_key' => 'test_access_token'
},
validate_provider_config: false,
sync_templates: false)
end.not_to raise_error
end
end
context 'when channel is created through manual setup' do
it 'does not setup webhooks' do
expect(Whatsapp::WebhookSetupService).not_to receive(:new)
create(:channel_whatsapp,
account: account,
provider: 'whatsapp_cloud',
provider_config: {
'business_account_id' => 'test_waba_id',
'api_key' => 'test_access_token'
},
validate_provider_config: false,
sync_templates: false)
end
end
context 'when channel is created with different provider' do
it 'does not setup webhooks for 360dialog provider' do
expect(Whatsapp::WebhookSetupService).not_to receive(:new)
create(:channel_whatsapp,
account: account,
provider: 'default',
provider_config: {
'source' => 'embedded_signup',
'api_key' => 'test_360dialog_key'
},
validate_provider_config: false,
sync_templates: false)
end
end
end
end
+57
View File
@@ -0,0 +1,57 @@
require 'rails_helper'
RSpec.describe Featurable do
let(:account) { create(:account) }
describe 'WhatsApp embedded signup feature' do
it 'is disabled by default' do
expect(account.feature_whatsapp_embedded_signup?).to be false
expect(account.feature_enabled?('whatsapp_embedded_signup')).to be false
end
describe '#enable_features!' do
it 'enables the whatsapp embedded signup feature' do
account.enable_features!(:whatsapp_embedded_signup)
expect(account.feature_whatsapp_embedded_signup?).to be true
expect(account.feature_enabled?('whatsapp_embedded_signup')).to be true
end
it 'enables multiple features at once' do
account.enable_features!(:whatsapp_embedded_signup, :help_center)
expect(account.feature_whatsapp_embedded_signup?).to be true
expect(account.feature_help_center?).to be true
end
end
describe '#disable_features!' do
before do
account.enable_features!(:whatsapp_embedded_signup)
end
it 'disables the whatsapp embedded signup feature' do
expect(account.feature_whatsapp_embedded_signup?).to be true
account.disable_features!(:whatsapp_embedded_signup)
expect(account.feature_whatsapp_embedded_signup?).to be false
end
end
describe '#enabled_features' do
it 'includes whatsapp_embedded_signup when enabled' do
account.enable_features!(:whatsapp_embedded_signup)
expect(account.enabled_features).to include('whatsapp_embedded_signup' => true)
end
it 'does not include whatsapp_embedded_signup when disabled' do
account.disable_features!(:whatsapp_embedded_signup)
expect(account.enabled_features).not_to include('whatsapp_embedded_signup' => true)
end
end
describe '#all_features' do
it 'includes whatsapp_embedded_signup in all features list' do
expect(account.all_features).to have_key('whatsapp_embedded_signup')
end
end
end
end
@@ -19,6 +19,11 @@ describe Whatsapp::ChannelCreationService do
# Clean up any existing channels to avoid phone number conflicts
Channel::Whatsapp.destroy_all
# Stub the webhook setup service to prevent HTTP calls during tests
webhook_service = instance_double(Whatsapp::WebhookSetupService)
allow(Whatsapp::WebhookSetupService).to receive(:new).and_return(webhook_service)
allow(webhook_service).to receive(:perform)
# Stub the provider validation and sync_templates
allow(Channel::Whatsapp).to receive(:new).and_wrap_original do |method, *args|
channel = method.call(*args)
@@ -111,4 +116,4 @@ describe Whatsapp::ChannelCreationService do
end
end
end
end
end
@@ -32,7 +32,6 @@ describe Whatsapp::EmbeddedSignupService do
let(:phone_info_service) { instance_double(Whatsapp::PhoneInfoService) }
let(:token_validation_service) { instance_double(Whatsapp::TokenValidationService) }
let(:channel_creation_service) { instance_double(Whatsapp::ChannelCreationService) }
let(:webhook_setup_service) { instance_double(Whatsapp::WebhookSetupService) }
before do
allow(GlobalConfig).to receive(:clear_cache)
@@ -53,9 +52,9 @@ describe Whatsapp::EmbeddedSignupService do
.and_return(channel_creation_service)
allow(channel_creation_service).to receive(:perform).and_return(channel)
allow(Whatsapp::WebhookSetupService).to receive(:new)
.with(channel, waba_id, access_token).and_return(webhook_setup_service)
allow(webhook_setup_service).to receive(:perform)
# Webhook setup is now handled in the channel after_create callback
# So we stub it at the channel level
allow(channel).to receive(:setup_webhooks)
end
it 'orchestrates all services in the correct order' do
@@ -64,7 +63,6 @@ describe Whatsapp::EmbeddedSignupService do
expect(phone_info_service).to receive(:perform).ordered
expect(token_validation_service).to receive(:perform).ordered
expect(channel_creation_service).to receive(:perform).ordered
expect(webhook_setup_service).to receive(:perform).ordered
result = service.perform
expect(result).to eq(channel)
@@ -125,4 +123,4 @@ describe Whatsapp::EmbeddedSignupService do
end
end
end
end
end