chore: Use per-account feature flag for App Store channel
This commit is contained in:
@@ -31,7 +31,7 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
||||
end
|
||||
|
||||
def create
|
||||
return render_app_store_feature_disabled if app_store_channel_requested? && !app_store_reviews_enabled?
|
||||
return render_app_store_feature_disabled if app_store_channel_requested? && !Current.account.feature_enabled?(:channel_app_store)
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
channel = create_channel
|
||||
@@ -110,10 +110,6 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
||||
render json: { message: 'App Store Reviews channel is not enabled for this account' }, status: :forbidden
|
||||
end
|
||||
|
||||
def app_store_reviews_enabled?
|
||||
GlobalConfigService.load('ENABLE_APP_STORE_REVIEWS_CHANNEL', 'false').to_s == 'true'
|
||||
end
|
||||
|
||||
def update_inbox_working_hours
|
||||
@inbox.update_working_hours(params.permit(working_hours: Inbox::OFFISABLE_ATTRS)[:working_hours]) if params[:working_hours]
|
||||
end
|
||||
|
||||
@@ -74,7 +74,6 @@ class DashboardController < ActionController::Base
|
||||
FB_APP_ID: GlobalConfigService.load('FB_APP_ID', ''),
|
||||
INSTAGRAM_APP_ID: GlobalConfigService.load('INSTAGRAM_APP_ID', ''),
|
||||
TIKTOK_APP_ID: GlobalConfigService.load('TIKTOK_APP_ID', ''),
|
||||
ENABLE_APP_STORE_REVIEWS_CHANNEL: GlobalConfigService.load('ENABLE_APP_STORE_REVIEWS_CHANNEL', 'false'),
|
||||
FACEBOOK_API_VERSION: GlobalConfigService.load('FACEBOOK_API_VERSION', 'v18.0'),
|
||||
WHATSAPP_APP_ID: GlobalConfigService.load('WHATSAPP_APP_ID', ''),
|
||||
WHATSAPP_CONFIGURATION_ID: GlobalConfigService.load('WHATSAPP_CONFIGURATION_ID', ''),
|
||||
|
||||
@@ -27,10 +27,6 @@ const hasTiktokConfigured = computed(() => {
|
||||
return window.chatwootConfig?.tiktokAppId;
|
||||
});
|
||||
|
||||
const isAppStoreReviewsEnabled = computed(() => {
|
||||
return window.chatwootConfig?.enableAppStoreReviewsChannel === 'true';
|
||||
});
|
||||
|
||||
const isActive = computed(() => {
|
||||
const { key } = props.channel;
|
||||
if (Object.keys(props.enabledFeatures).length === 0) {
|
||||
@@ -57,7 +53,7 @@ const isActive = computed(() => {
|
||||
}
|
||||
|
||||
if (key === 'app_store') {
|
||||
return isAppStoreReviewsEnabled.value;
|
||||
return props.enabledFeatures.channel_app_store;
|
||||
}
|
||||
|
||||
if (key === 'voice') {
|
||||
|
||||
@@ -2,19 +2,12 @@ class Inboxes::FetchAppStoreReviewInboxesJob < ApplicationJob
|
||||
queue_as :scheduled_jobs
|
||||
|
||||
def perform
|
||||
return unless app_store_reviews_enabled?
|
||||
|
||||
Inbox.where(channel_type: 'Channel::AppStore').find_each(batch_size: 100) do |inbox|
|
||||
next if inbox.account.suspended?
|
||||
next unless inbox.account.feature_enabled?(:channel_app_store)
|
||||
next unless inbox.channel.sync_due?
|
||||
|
||||
::Inboxes::FetchAppStoreReviewsJob.perform_later(inbox.channel)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def app_store_reviews_enabled?
|
||||
GlobalConfigService.load('ENABLE_APP_STORE_REVIEWS_CHANNEL', 'false').to_s == 'true'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@ class Inboxes::FetchAppStoreReviewsJob < ApplicationJob
|
||||
queue_as :scheduled_jobs
|
||||
|
||||
def perform(channel)
|
||||
return unless app_store_reviews_enabled?
|
||||
return unless channel.account.feature_enabled?(:channel_app_store)
|
||||
|
||||
channel.fetch_reviews.each do |review_payload|
|
||||
::AppStore::ReviewBuilder.new(review_payload: review_payload, channel: channel).perform
|
||||
@@ -14,10 +14,4 @@ class Inboxes::FetchAppStoreReviewsJob < ApplicationJob
|
||||
rescue StandardError => e
|
||||
ChatwootExceptionTracker.new(e, account: channel.account).capture_exception
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def app_store_reviews_enabled?
|
||||
GlobalConfigService.load('ENABLE_APP_STORE_REVIEWS_CHANNEL', 'false').to_s == 'true'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,7 +21,7 @@ class AppStore::SendOnAppStoreService < Base::SendOnChannelService
|
||||
end
|
||||
|
||||
def validate_feature_enabled!
|
||||
return if GlobalConfigService.load('ENABLE_APP_STORE_REVIEWS_CHANNEL', 'false').to_s == 'true'
|
||||
return if message.account.feature_enabled?(:channel_app_store)
|
||||
|
||||
raise 'App Store Reviews channel is not enabled for this account.'
|
||||
end
|
||||
|
||||
+3
-3
@@ -108,10 +108,10 @@
|
||||
display_name: Custom Tools
|
||||
enabled: false
|
||||
premium: true
|
||||
- name: message_reply_to
|
||||
display_name: Message Reply To
|
||||
- name: channel_app_store
|
||||
display_name: App Store Reviews Channel
|
||||
enabled: false
|
||||
deprecated: true
|
||||
chatwoot_internal: true
|
||||
- name: insert_article_in_reply
|
||||
display_name: Insert Article in Reply
|
||||
enabled: false
|
||||
|
||||
@@ -277,11 +277,6 @@
|
||||
display_title: 'Inactive WhatsApp Numbers'
|
||||
description: 'Comma-separated list of WhatsApp numbers that should be rejected with a 422 error'
|
||||
type: code
|
||||
- name: ENABLE_APP_STORE_REVIEWS_CHANNEL
|
||||
value: false
|
||||
display_title: 'Enable App Store Reviews Channel'
|
||||
description: 'Enable the App Store Reviews inbox channel'
|
||||
type: boolean
|
||||
# ------- End of Chatwoot Internal Config for Cloud ----#
|
||||
|
||||
# ------- Chatwoot Internal Config for Self Hosted ----#
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
class RepurposeMessageReplyToFlagForChannelAppStore < ActiveRecord::Migration[7.1]
|
||||
def up
|
||||
# The message_reply_to flag (deprecated) has been renamed to channel_app_store.
|
||||
# Disable it on any accounts that had message_reply_to enabled so the repurposed
|
||||
# flag starts in its intended default-off state.
|
||||
Account.feature_channel_app_store.find_each(batch_size: 100) do |account|
|
||||
account.disable_features(:channel_app_store)
|
||||
account.save!(validate: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
+1
-1
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.1].define(version: 2026_05_20_090000) do
|
||||
ActiveRecord::Schema[7.1].define(version: 2026_05_22_080000) do
|
||||
# These extensions should be enabled to support this database
|
||||
enable_extension "pg_stat_statements"
|
||||
enable_extension "pg_trgm"
|
||||
|
||||
@@ -435,9 +435,6 @@ RSpec.describe 'Inboxes API', type: :request do
|
||||
end
|
||||
|
||||
it 'does not create an app store inbox when the feature is disabled' do
|
||||
allow(GlobalConfigService).to receive(:load).and_call_original
|
||||
allow(GlobalConfigService).to receive(:load).with('ENABLE_APP_STORE_REVIEWS_CHANNEL', 'false').and_return('false')
|
||||
|
||||
post "/api/v1/accounts/#{account.id}/inboxes",
|
||||
headers: admin.create_new_auth_token,
|
||||
params: { name: 'App Store Reviews',
|
||||
@@ -452,8 +449,7 @@ RSpec.describe 'Inboxes API', type: :request do
|
||||
it 'creates an app store inbox when the feature is enabled' do
|
||||
app_store_client = instance_double(AppStoreConnect::Client)
|
||||
|
||||
allow(GlobalConfigService).to receive(:load).and_call_original
|
||||
allow(GlobalConfigService).to receive(:load).with('ENABLE_APP_STORE_REVIEWS_CHANNEL', 'false').and_return('true')
|
||||
account.enable_features!(:channel_app_store)
|
||||
allow(AppStoreConnect::Client).to receive(:new).and_return(app_store_client)
|
||||
allow(app_store_client).to receive(:fetch_app).and_return(
|
||||
{
|
||||
|
||||
@@ -10,8 +10,8 @@ RSpec.describe Inboxes::FetchAppStoreReviewInboxesJob do
|
||||
let(:suspended_channel) { create(:channel_app_store, account: suspended_account, last_synced_at: 2.hours.ago) }
|
||||
|
||||
before do
|
||||
allow(GlobalConfigService).to receive(:load).and_call_original
|
||||
allow(GlobalConfigService).to receive(:load).with('ENABLE_APP_STORE_REVIEWS_CHANNEL', 'false').and_return('true')
|
||||
account.enable_features!(:channel_app_store)
|
||||
suspended_account.enable_features!(:channel_app_store)
|
||||
end
|
||||
|
||||
it 'enqueues the job' do
|
||||
@@ -32,7 +32,7 @@ RSpec.describe Inboxes::FetchAppStoreReviewInboxesJob do
|
||||
end
|
||||
|
||||
it 'skips channels when the feature is disabled for the account' do
|
||||
allow(GlobalConfigService).to receive(:load).with('ENABLE_APP_STORE_REVIEWS_CHANNEL', 'false').and_return('false')
|
||||
account.disable_features!(:channel_app_store)
|
||||
due_channel
|
||||
|
||||
expect(Inboxes::FetchAppStoreReviewsJob).not_to receive(:perform_later)
|
||||
|
||||
@@ -8,8 +8,7 @@ RSpec.describe Inboxes::FetchAppStoreReviewsJob do
|
||||
let(:review_builder) { instance_double(AppStore::ReviewBuilder, perform: true) }
|
||||
|
||||
before do
|
||||
allow(GlobalConfigService).to receive(:load).and_call_original
|
||||
allow(GlobalConfigService).to receive(:load).with('ENABLE_APP_STORE_REVIEWS_CHANNEL', 'false').and_return('true')
|
||||
channel.account.enable_features!(:channel_app_store)
|
||||
end
|
||||
|
||||
it 'enqueues the job' do
|
||||
@@ -43,7 +42,7 @@ RSpec.describe Inboxes::FetchAppStoreReviewsJob do
|
||||
end
|
||||
|
||||
it 'does not fetch reviews when the feature is disabled for the account' do
|
||||
allow(GlobalConfigService).to receive(:load).with('ENABLE_APP_STORE_REVIEWS_CHANNEL', 'false').and_return('false')
|
||||
channel.account.disable_features!(:channel_app_store)
|
||||
|
||||
expect(channel).not_to receive(:fetch_reviews)
|
||||
|
||||
|
||||
@@ -50,6 +50,23 @@ RSpec.describe Account do
|
||||
end
|
||||
end
|
||||
|
||||
describe 'app store channel feature flag' do
|
||||
let(:account) { create(:account) }
|
||||
|
||||
it 'stores app store channel feature state in feature flags' do
|
||||
expect(account.feature_enabled?(:channel_app_store)).to be false
|
||||
|
||||
account.enable_features!(:channel_app_store)
|
||||
|
||||
expect(account.reload.feature_enabled?(:channel_app_store)).to be true
|
||||
expect(account.enabled_features).to include('channel_app_store' => true)
|
||||
|
||||
account.disable_features!(:channel_app_store)
|
||||
|
||||
expect(account.reload.feature_enabled?(:channel_app_store)).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe 'conversation unread counts feature flag' do
|
||||
let(:account) { create(:account) }
|
||||
let(:inbox) { create(:inbox, account: account) }
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AppStore::SendOnAppStoreService do
|
||||
let(:channel) { create(:channel_app_store) }
|
||||
let(:account) { create(:account) }
|
||||
let(:channel) { create(:channel_app_store, account: account) }
|
||||
let(:inbox) { channel.inbox }
|
||||
let(:contact) { create(:contact, account: inbox.account) }
|
||||
let(:contact_inbox) { create(:contact_inbox, inbox: inbox, contact: contact, source_id: 'review-1') }
|
||||
@@ -12,8 +13,7 @@ RSpec.describe AppStore::SendOnAppStoreService do
|
||||
let(:exception_tracker) { instance_double(ChatwootExceptionTracker, capture_exception: true) }
|
||||
|
||||
before do
|
||||
allow(GlobalConfigService).to receive(:load).and_call_original
|
||||
allow(GlobalConfigService).to receive(:load).with('ENABLE_APP_STORE_REVIEWS_CHANNEL', 'false').and_return('true')
|
||||
account.enable_features!(:channel_app_store)
|
||||
allow(Messages::StatusUpdateService).to receive(:new).and_return(status_update_service)
|
||||
allow(ChatwootExceptionTracker).to receive(:new).and_return(exception_tracker)
|
||||
end
|
||||
@@ -58,7 +58,7 @@ RSpec.describe AppStore::SendOnAppStoreService do
|
||||
end
|
||||
|
||||
it 'marks the message as failed when the feature is disabled' do
|
||||
allow(GlobalConfigService).to receive(:load).with('ENABLE_APP_STORE_REVIEWS_CHANNEL', 'false').and_return('false')
|
||||
account.disable_features!(:channel_app_store)
|
||||
message = create(:message, message_type: :outgoing, inbox: inbox, conversation: conversation, account: inbox.account, content: 'Thanks')
|
||||
|
||||
described_class.new(message: message).perform
|
||||
|
||||
Reference in New Issue
Block a user