feat(whatsapp): enable reconfigure for embedded signup inboxes (#15038)
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
class Api::V1::Accounts::Whatsapp::AuthorizationsController < Api::V1::Accounts::BaseController
|
||||
# Reconfiguring/reauthorizing a live inbox swaps its credentials, so restrict it to admins.
|
||||
before_action :check_admin_authorization?, if: -> { params[:inbox_id].present? }
|
||||
before_action :fetch_and_validate_inbox, if: -> { params[:inbox_id].present? }
|
||||
|
||||
# POST /api/v1/accounts/:account_id/whatsapp/authorization
|
||||
@@ -31,7 +33,7 @@ class Api::V1::Accounts::Whatsapp::AuthorizationsController < Api::V1::Accounts:
|
||||
end
|
||||
|
||||
def validate_reauthorization_required
|
||||
return if @inbox.channel.reauthorization_required? || can_upgrade_to_embedded_signup?
|
||||
return if @inbox.channel.reauthorization_required? || can_reconfigure_channel?
|
||||
|
||||
render json: {
|
||||
success: false,
|
||||
@@ -39,10 +41,13 @@ class Api::V1::Accounts::Whatsapp::AuthorizationsController < Api::V1::Accounts:
|
||||
}, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def can_upgrade_to_embedded_signup?
|
||||
def can_reconfigure_channel?
|
||||
channel = @inbox.channel
|
||||
return false unless channel.provider == 'whatsapp_cloud'
|
||||
|
||||
# Reconfiguring a live embedded-signup channel requires the feature flag.
|
||||
return Current.account.feature_enabled?('whatsapp_reconfigure') if channel.provider_config['source'] == 'embedded_signup'
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ export const FEATURE_FLAGS = {
|
||||
CAMPAIGNS: 'campaigns',
|
||||
WHATSAPP_CAMPAIGNS: 'whatsapp_campaign',
|
||||
WHATSAPP_MANUAL_TRANSFER: 'whatsapp_manual_transfer',
|
||||
WHATSAPP_RECONFIGURE: 'whatsapp_reconfigure',
|
||||
CANNED_RESPONSES: 'canned_responses',
|
||||
CRM: 'crm',
|
||||
CUSTOM_ATTRIBUTES: 'custom_attributes',
|
||||
|
||||
+59
-1
@@ -1,5 +1,9 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useWhatsappEmbeddedSignup } from 'dashboard/composables/useWhatsappEmbeddedSignup';
|
||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
import whatsappChannel from 'dashboard/api/channel/whatsappChannel';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
import SettingsFieldSection from 'dashboard/components-next/Settings/SettingsFieldSection.vue';
|
||||
import SettingsToggleSection from 'dashboard/components-next/Settings/SettingsToggleSection.vue';
|
||||
@@ -30,7 +34,8 @@ export default {
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
return { v$: useVuelidate() };
|
||||
const { runEmbeddedSignup } = useWhatsappEmbeddedSignup();
|
||||
return { v$: useVuelidate(), runEmbeddedSignup };
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -41,15 +46,29 @@ export default {
|
||||
allowedDomains: '',
|
||||
isUpdatingAllowedDomains: false,
|
||||
isSettingDefaults: false,
|
||||
isReconfiguring: false,
|
||||
};
|
||||
},
|
||||
validations: {
|
||||
whatsAppInboxAPIKey: { required },
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
accountId: 'getCurrentAccountId',
|
||||
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
|
||||
}),
|
||||
isEmbeddedSignupWhatsApp() {
|
||||
return this.inbox.provider_config?.source === 'embedded_signup';
|
||||
},
|
||||
showWhatsAppReconfigure() {
|
||||
return (
|
||||
this.isEmbeddedSignupWhatsApp &&
|
||||
this.isFeatureEnabledonAccount(
|
||||
this.accountId,
|
||||
FEATURE_FLAGS.WHATSAPP_RECONFIGURE
|
||||
)
|
||||
);
|
||||
},
|
||||
isForwardingEnabled() {
|
||||
return !!this.inbox.forwarding_enabled;
|
||||
},
|
||||
@@ -160,6 +179,28 @@ export default {
|
||||
useAlert(this.$t('INBOX_MGMT.EDIT.API.ERROR_MESSAGE'));
|
||||
}
|
||||
},
|
||||
async reconfigureWhatsApp() {
|
||||
this.isReconfiguring = true;
|
||||
try {
|
||||
const credentials = await this.runEmbeddedSignup();
|
||||
// User dismissed the Meta popup without completing signup.
|
||||
if (!credentials) return;
|
||||
|
||||
await whatsappChannel.reauthorizeWhatsApp({
|
||||
inboxId: this.inbox.id,
|
||||
...credentials,
|
||||
});
|
||||
useAlert(
|
||||
this.$t('INBOX_MGMT.SETTINGS_POPUP.WHATSAPP_RECONFIGURE_SUCCESS')
|
||||
);
|
||||
} catch (error) {
|
||||
useAlert(
|
||||
this.$t('INBOX_MGMT.SETTINGS_POPUP.WHATSAPP_RECONFIGURE_ERROR')
|
||||
);
|
||||
} finally {
|
||||
this.isReconfiguring = false;
|
||||
}
|
||||
},
|
||||
async syncTemplates() {
|
||||
this.isSyncingTemplates = true;
|
||||
try {
|
||||
@@ -358,6 +399,23 @@ export default {
|
||||
>
|
||||
<woot-code :script="inbox.provider_config.webhook_verify_token" />
|
||||
</SettingsFieldSection>
|
||||
<SettingsFieldSection
|
||||
v-if="showWhatsAppReconfigure"
|
||||
:label="
|
||||
$t('INBOX_MGMT.SETTINGS_POPUP.WHATSAPP_EMBEDDED_SIGNUP_TITLE')
|
||||
"
|
||||
:help-text="
|
||||
$t('INBOX_MGMT.SETTINGS_POPUP.WHATSAPP_EMBEDDED_SIGNUP_DESCRIPTION')
|
||||
"
|
||||
>
|
||||
<NextButton
|
||||
:is-loading="isReconfiguring"
|
||||
:disabled="isReconfiguring"
|
||||
@click="reconfigureWhatsApp"
|
||||
>
|
||||
{{ $t('INBOX_MGMT.SETTINGS_POPUP.WHATSAPP_RECONFIGURE_BUTTON') }}
|
||||
</NextButton>
|
||||
</SettingsFieldSection>
|
||||
</template>
|
||||
|
||||
<!-- Manual Setup Section -->
|
||||
|
||||
@@ -47,7 +47,7 @@ class Whatsapp::EmbeddedSignupService
|
||||
account: @account,
|
||||
inbox_id: @inbox_id,
|
||||
phone_number_id: @phone_number_id,
|
||||
business_id: @business_id
|
||||
waba_id: @waba_id
|
||||
).perform(access_token, phone_info)
|
||||
else
|
||||
waba_info = { waba_id: @waba_id, business_name: phone_info[:business_name] }
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
class Whatsapp::ReauthorizationService
|
||||
def initialize(account:, inbox_id:, phone_number_id:, business_id:)
|
||||
def initialize(account:, inbox_id:, phone_number_id:, waba_id:)
|
||||
@account = account
|
||||
@inbox_id = inbox_id
|
||||
@phone_number_id = phone_number_id
|
||||
@business_id = business_id
|
||||
@waba_id = waba_id
|
||||
end
|
||||
|
||||
def perform(access_token, phone_info)
|
||||
@@ -33,7 +33,7 @@ class Whatsapp::ReauthorizationService
|
||||
channel.provider_config = current_config.merge(
|
||||
'api_key' => access_token,
|
||||
'phone_number_id' => resolved_phone_number_id,
|
||||
'business_account_id' => @business_id,
|
||||
'business_account_id' => @waba_id,
|
||||
'source' => 'embedded_signup'
|
||||
)
|
||||
channel.save!
|
||||
|
||||
@@ -261,3 +261,7 @@
|
||||
display_name: API and Webhooks
|
||||
enabled: true
|
||||
column: feature_flags_ext_1
|
||||
- name: whatsapp_reconfigure
|
||||
display_name: WhatsApp Reconfigure
|
||||
enabled: false
|
||||
column: feature_flags_ext_1
|
||||
|
||||
@@ -456,29 +456,18 @@ RSpec.describe 'WhatsApp Authorization API', type: :request do
|
||||
create(:inbox_member, inbox: whatsapp_inbox, user: agent)
|
||||
end
|
||||
|
||||
it 'returns unprocessable_entity error' do
|
||||
it 'returns unauthorized error' do
|
||||
allow(whatsapp_channel).to receive(:reauthorization_required?).and_return(true)
|
||||
|
||||
# Stub the embedded signup service to prevent HTTP calls
|
||||
embedded_signup_service = instance_double(Whatsapp::EmbeddedSignupService)
|
||||
allow(Whatsapp::EmbeddedSignupService).to receive(:new).with(
|
||||
account: account,
|
||||
params: {
|
||||
code: 'test',
|
||||
business_id: 'test',
|
||||
waba_id: 'test'
|
||||
},
|
||||
inbox_id: whatsapp_inbox.id
|
||||
).and_return(embedded_signup_service)
|
||||
allow(embedded_signup_service).to receive(:perform).and_return(whatsapp_channel)
|
||||
expect(Whatsapp::EmbeddedSignupService).not_to receive(:new)
|
||||
|
||||
post "/api/v1/accounts/#{account.id}/whatsapp/authorization",
|
||||
params: { inbox_id: whatsapp_inbox.id, code: 'test', business_id: 'test', waba_id: 'test' },
|
||||
headers: agent.create_new_auth_token,
|
||||
as: :json
|
||||
|
||||
# Agents should get unprocessable_entity since they can find the inbox but channel doesn't need reauth
|
||||
expect(response).to have_http_status(:unprocessable_entity)
|
||||
# Reauthorizing an existing inbox swaps live credentials, so it is restricted to admins.
|
||||
expect(response).to have_http_status(:unauthorized)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ RSpec.describe Account do
|
||||
it 'configures the account feature flag extension column' do
|
||||
expect(described_class.flag_columns).to include('feature_flags', 'feature_flags_ext_1')
|
||||
expect(described_class.flag_mapping['feature_flags_ext_1']).to eq(feature_whatsapp_manual_transfer: 1, feature_data_import: 1 << 1,
|
||||
feature_api_and_webhooks: 1 << 2)
|
||||
feature_api_and_webhooks: 1 << 2, feature_whatsapp_reconfigure: 1 << 3)
|
||||
expect(described_class.flag_mapping['feature_flags_ext_1'][:feature_whatsapp_manual_transfer]).to eq(1)
|
||||
expect(described_class.flag_mapping['feature_flags_ext_1'][:feature_data_import]).to eq(2)
|
||||
end
|
||||
|
||||
@@ -158,7 +158,7 @@ describe Whatsapp::EmbeddedSignupService do
|
||||
account: account,
|
||||
inbox_id: inbox_id,
|
||||
phone_number_id: params[:phone_number_id],
|
||||
business_id: params[:business_id]
|
||||
waba_id: params[:waba_id]
|
||||
).and_return(reauth_service)
|
||||
allow(reauth_service).to receive(:perform).with(access_token, phone_info).and_return(channel)
|
||||
|
||||
@@ -212,7 +212,7 @@ describe Whatsapp::EmbeddedSignupService do
|
||||
account: account,
|
||||
inbox_id: inbox.id,
|
||||
phone_number_id: params[:phone_number_id],
|
||||
business_id: params[:business_id]
|
||||
waba_id: params[:waba_id]
|
||||
).and_return(reauth_service)
|
||||
|
||||
allow(reauth_service).to receive(:perform) do
|
||||
|
||||
Reference in New Issue
Block a user