mirror of https://github.com/zulip/zulip.git
migrate_customers: Migrate customer from server to realms during login.
Earlier, the 'handle_customer_migration_from_server_to_realms' function was called during the send analytics step. It resulted in an error for customers having multiple Zulip servers, one for testing and the others for not-testing, sharing a push bouncer registration. The migration step when run in a test instance caused customers to have their legacy plan migrated to a test realm, resulting in them losing their legacy plan. This commit moves the migration step to run during plan management login step. This reduces the chances of losing legacy plan as we expect them to only verify that 8.0 upgrade works and not bother trying to login to plan management from their test instance.
This commit is contained in:
parent
4ab9cd7cf2
commit
4715a058b0
|
@ -51,6 +51,7 @@ class RemoteRealmBillingTestCase(BouncerTestCase):
|
||||||
# This only matters if first_time_login is True, since otherwise
|
# This only matters if first_time_login is True, since otherwise
|
||||||
# there's no confirmation link to be clicked:
|
# there's no confirmation link to be clicked:
|
||||||
return_without_clicking_confirmation_link: bool = False,
|
return_without_clicking_confirmation_link: bool = False,
|
||||||
|
server_on_active_plan_error: bool = False,
|
||||||
) -> "TestHttpResponse":
|
) -> "TestHttpResponse":
|
||||||
now = timezone_now()
|
now = timezone_now()
|
||||||
|
|
||||||
|
@ -68,6 +69,10 @@ class RemoteRealmBillingTestCase(BouncerTestCase):
|
||||||
with time_machine.travel(now, tick=False):
|
with time_machine.travel(now, tick=False):
|
||||||
result = self.client_get(signed_auth_url, subdomain="selfhosting")
|
result = self.client_get(signed_auth_url, subdomain="selfhosting")
|
||||||
|
|
||||||
|
if server_on_active_plan_error:
|
||||||
|
self.assert_in_response("Plan management not available", result)
|
||||||
|
return result
|
||||||
|
|
||||||
if first_time_login:
|
if first_time_login:
|
||||||
self.assertFalse(RemoteRealmBillingUser.objects.filter(user_uuid=user.uuid).exists())
|
self.assertFalse(RemoteRealmBillingUser.objects.filter(user_uuid=user.uuid).exists())
|
||||||
# When logging in for the first time some extra steps are needed
|
# When logging in for the first time some extra steps are needed
|
||||||
|
@ -463,6 +468,9 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_transfer_legacy_plan_from_server_to_all_realms(self) -> None:
|
def test_transfer_legacy_plan_from_server_to_all_realms(self) -> None:
|
||||||
|
self.login("desdemona")
|
||||||
|
desdemona = self.example_user("desdemona")
|
||||||
|
|
||||||
# Assert current server is not on any plan.
|
# Assert current server is not on any plan.
|
||||||
self.assertIsNone(get_customer_by_remote_server(self.server))
|
self.assertIsNone(get_customer_by_remote_server(self.server))
|
||||||
|
|
||||||
|
@ -497,6 +505,12 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
self.add_mock_response()
|
self.add_mock_response()
|
||||||
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
||||||
|
|
||||||
|
# Login to plan management.
|
||||||
|
result = self.execute_remote_billing_authentication_flow(
|
||||||
|
desdemona, server_on_active_plan_error=True
|
||||||
|
)
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
# RemoteRealm objects should be created for all realms on the server.
|
# RemoteRealm objects should be created for all realms on the server.
|
||||||
self.assert_length(RemoteRealm.objects.all(), 4)
|
self.assert_length(RemoteRealm.objects.all(), 4)
|
||||||
|
|
||||||
|
@ -509,8 +523,11 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
server_customer.sponsorship_pending = False
|
server_customer.sponsorship_pending = False
|
||||||
server_customer.save()
|
server_customer.save()
|
||||||
|
|
||||||
# Send server data to push bouncer again.
|
# Login to plan management. Performs customer migration from server to realms.
|
||||||
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
result = self.execute_remote_billing_authentication_flow(
|
||||||
|
desdemona, server_on_active_plan_error=False
|
||||||
|
)
|
||||||
|
self.assertEqual(result.status_code, 302)
|
||||||
|
|
||||||
# Server plan status was reset
|
# Server plan status was reset
|
||||||
self.server.refresh_from_db()
|
self.server.refresh_from_db()
|
||||||
|
@ -538,6 +555,9 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
def test_transfer_legacy_plan_scheduled_for_upgrade_from_server_to_realm(
|
def test_transfer_legacy_plan_scheduled_for_upgrade_from_server_to_realm(
|
||||||
self,
|
self,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
self.login("desdemona")
|
||||||
|
desdemona = self.example_user("desdemona")
|
||||||
|
|
||||||
# Assert current server is not on any plan.
|
# Assert current server is not on any plan.
|
||||||
self.assertIsNone(get_customer_by_remote_server(self.server))
|
self.assertIsNone(get_customer_by_remote_server(self.server))
|
||||||
|
|
||||||
|
@ -580,6 +600,12 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
self.add_mock_response()
|
self.add_mock_response()
|
||||||
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
||||||
|
|
||||||
|
# Login to plan management.
|
||||||
|
result = self.execute_remote_billing_authentication_flow(
|
||||||
|
desdemona, server_on_active_plan_error=True
|
||||||
|
)
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
# Server plan status stayed the same.
|
# Server plan status stayed the same.
|
||||||
self.server.refresh_from_db()
|
self.server.refresh_from_db()
|
||||||
self.assertEqual(self.server.plan_type, RemoteZulipServer.PLAN_TYPE_SELF_MANAGED_LEGACY)
|
self.assertEqual(self.server.plan_type, RemoteZulipServer.PLAN_TYPE_SELF_MANAGED_LEGACY)
|
||||||
|
@ -600,6 +626,12 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
# Send server data to push bouncer.
|
# Send server data to push bouncer.
|
||||||
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
||||||
|
|
||||||
|
# Login to plan management. Performs customer migration from server to realms.
|
||||||
|
result = self.execute_remote_billing_authentication_flow(
|
||||||
|
desdemona, server_on_active_plan_error=False
|
||||||
|
)
|
||||||
|
self.assertEqual(result.status_code, 302)
|
||||||
|
|
||||||
# Server plan status was reset
|
# Server plan status was reset
|
||||||
self.server.refresh_from_db()
|
self.server.refresh_from_db()
|
||||||
self.assertEqual(self.server.plan_type, RemoteZulipServer.PLAN_TYPE_SELF_MANAGED)
|
self.assertEqual(self.server.plan_type, RemoteZulipServer.PLAN_TYPE_SELF_MANAGED)
|
||||||
|
@ -635,6 +667,9 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
def test_transfer_business_plan_from_server_to_realm(
|
def test_transfer_business_plan_from_server_to_realm(
|
||||||
self,
|
self,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
self.login("desdemona")
|
||||||
|
desdemona = self.example_user("desdemona")
|
||||||
|
|
||||||
# Assert current server is not on any plan.
|
# Assert current server is not on any plan.
|
||||||
self.assertIsNone(get_customer_by_remote_server(self.server))
|
self.assertIsNone(get_customer_by_remote_server(self.server))
|
||||||
self.assertEqual(self.server.plan_type, RemoteZulipServer.PLAN_TYPE_SELF_MANAGED)
|
self.assertEqual(self.server.plan_type, RemoteZulipServer.PLAN_TYPE_SELF_MANAGED)
|
||||||
|
@ -666,6 +701,12 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
self.add_mock_response()
|
self.add_mock_response()
|
||||||
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
||||||
|
|
||||||
|
# Login to plan management.
|
||||||
|
result = self.execute_remote_billing_authentication_flow(
|
||||||
|
desdemona, server_on_active_plan_error=True
|
||||||
|
)
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
# Server plan status stayed the same.
|
# Server plan status stayed the same.
|
||||||
self.server.refresh_from_db()
|
self.server.refresh_from_db()
|
||||||
self.assertEqual(self.server.plan_type, RemoteZulipServer.PLAN_TYPE_BUSINESS)
|
self.assertEqual(self.server.plan_type, RemoteZulipServer.PLAN_TYPE_BUSINESS)
|
||||||
|
@ -686,6 +727,12 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
# Send server data to push bouncer.
|
# Send server data to push bouncer.
|
||||||
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
||||||
|
|
||||||
|
# Login to plan management. Performs customer migration from server to realms.
|
||||||
|
result = self.execute_remote_billing_authentication_flow(
|
||||||
|
desdemona, server_on_active_plan_error=False
|
||||||
|
)
|
||||||
|
self.assertEqual(result.status_code, 302)
|
||||||
|
|
||||||
# Server plan status was reset
|
# Server plan status was reset
|
||||||
self.server.refresh_from_db()
|
self.server.refresh_from_db()
|
||||||
self.assertEqual(self.server.plan_type, RemoteZulipServer.PLAN_TYPE_SELF_MANAGED)
|
self.assertEqual(self.server.plan_type, RemoteZulipServer.PLAN_TYPE_SELF_MANAGED)
|
||||||
|
@ -709,6 +756,9 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
|
|
||||||
@responses.activate
|
@responses.activate
|
||||||
def test_transfer_plan_from_server_to_realm_edge_cases(self) -> None:
|
def test_transfer_plan_from_server_to_realm_edge_cases(self) -> None:
|
||||||
|
self.login("desdemona")
|
||||||
|
desdemona = self.example_user("desdemona")
|
||||||
|
|
||||||
# CASE: Server has no customer
|
# CASE: Server has no customer
|
||||||
self.assertIsNone(get_customer_by_remote_server(self.server))
|
self.assertIsNone(get_customer_by_remote_server(self.server))
|
||||||
|
|
||||||
|
@ -716,6 +766,10 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
self.add_mock_response()
|
self.add_mock_response()
|
||||||
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
||||||
|
|
||||||
|
# Login to plan management.
|
||||||
|
result = self.execute_remote_billing_authentication_flow(desdemona)
|
||||||
|
self.assertEqual(result.status_code, 302)
|
||||||
|
|
||||||
# Still no customer.
|
# Still no customer.
|
||||||
self.assertIsNone(get_customer_by_remote_server(self.server))
|
self.assertIsNone(get_customer_by_remote_server(self.server))
|
||||||
|
|
||||||
|
@ -726,6 +780,12 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
# Send server data to push bouncer.
|
# Send server data to push bouncer.
|
||||||
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
||||||
|
|
||||||
|
# Login to plan management.
|
||||||
|
result = self.execute_remote_billing_authentication_flow(
|
||||||
|
desdemona, first_time_login=False, expect_tos=False
|
||||||
|
)
|
||||||
|
self.assertEqual(result.status_code, 302)
|
||||||
|
|
||||||
# Server still has no plan.
|
# Server still has no plan.
|
||||||
self.assertIsNone(get_current_plan_by_customer(server_customer))
|
self.assertIsNone(get_current_plan_by_customer(server_customer))
|
||||||
|
|
||||||
|
@ -740,6 +800,12 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
# Send server data to push bouncer.
|
# Send server data to push bouncer.
|
||||||
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
||||||
|
|
||||||
|
# Login to plan management.
|
||||||
|
result = self.execute_remote_billing_authentication_flow(
|
||||||
|
desdemona, server_on_active_plan_error=True
|
||||||
|
)
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
# Server stays on the same plan.
|
# Server stays on the same plan.
|
||||||
server_plan = get_current_plan_by_customer(server_customer)
|
server_plan = get_current_plan_by_customer(server_customer)
|
||||||
assert server_plan is not None
|
assert server_plan is not None
|
||||||
|
@ -753,8 +819,11 @@ class RemoteBillingAuthenticationTest(RemoteRealmBillingTestCase):
|
||||||
self.server.plan_type = RemoteZulipServer.PLAN_TYPE_BUSINESS
|
self.server.plan_type = RemoteZulipServer.PLAN_TYPE_BUSINESS
|
||||||
self.server.save(update_fields=["plan_type"])
|
self.server.save(update_fields=["plan_type"])
|
||||||
|
|
||||||
# Send server data to push bouncer.
|
# Login to plan management.
|
||||||
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
result = self.execute_remote_billing_authentication_flow(
|
||||||
|
desdemona, server_on_active_plan_error=True
|
||||||
|
)
|
||||||
|
self.assertEqual(result.status_code, 200)
|
||||||
|
|
||||||
# Server stays on the same plan.
|
# Server stays on the same plan.
|
||||||
server_customer.refresh_from_db()
|
server_customer.refresh_from_db()
|
||||||
|
|
|
@ -6070,11 +6070,20 @@ class TestRemoteRealmBillingFlow(StripeTestCase, RemoteRealmBillingTestCase):
|
||||||
self.assertEqual(server_customer_plan.status, CustomerPlan.ACTIVE)
|
self.assertEqual(server_customer_plan.status, CustomerPlan.ACTIVE)
|
||||||
self.assertEqual(remote_server.plan_type, RemoteZulipServer.PLAN_TYPE_SELF_MANAGED_LEGACY)
|
self.assertEqual(remote_server.plan_type, RemoteZulipServer.PLAN_TYPE_SELF_MANAGED_LEGACY)
|
||||||
|
|
||||||
# Upload data. Performs customer migration from server to realms.
|
# Upload data.
|
||||||
with time_machine.travel(self.now, tick=False):
|
with time_machine.travel(self.now, tick=False):
|
||||||
self.add_mock_response()
|
self.add_mock_response()
|
||||||
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
||||||
|
|
||||||
|
self.login("hamlet")
|
||||||
|
hamlet = self.example_user("hamlet")
|
||||||
|
billing_base_url = self.billing_session.billing_base_url
|
||||||
|
|
||||||
|
# Login. Performs customer migration from server to realms.
|
||||||
|
result = self.execute_remote_billing_authentication_flow(hamlet)
|
||||||
|
self.assertEqual(result.status_code, 302)
|
||||||
|
self.assertEqual(result["Location"], f"{billing_base_url}/plans/")
|
||||||
|
|
||||||
remote_server.refresh_from_db()
|
remote_server.refresh_from_db()
|
||||||
server_customer_plan.refresh_from_db()
|
server_customer_plan.refresh_from_db()
|
||||||
self.assertEqual(server_customer_plan.status, CustomerPlan.ENDED)
|
self.assertEqual(server_customer_plan.status, CustomerPlan.ENDED)
|
||||||
|
@ -6092,15 +6101,6 @@ class TestRemoteRealmBillingFlow(StripeTestCase, RemoteRealmBillingTestCase):
|
||||||
self.assertEqual(customer_plan.tier, CustomerPlan.TIER_SELF_HOSTED_LEGACY)
|
self.assertEqual(customer_plan.tier, CustomerPlan.TIER_SELF_HOSTED_LEGACY)
|
||||||
self.assertEqual(customer_plan.status, CustomerPlan.ACTIVE)
|
self.assertEqual(customer_plan.status, CustomerPlan.ACTIVE)
|
||||||
|
|
||||||
self.login("hamlet")
|
|
||||||
hamlet = self.example_user("hamlet")
|
|
||||||
billing_base_url = self.billing_session.billing_base_url
|
|
||||||
|
|
||||||
# Login
|
|
||||||
result = self.execute_remote_billing_authentication_flow(hamlet)
|
|
||||||
self.assertEqual(result.status_code, 302)
|
|
||||||
self.assertEqual(result["Location"], f"{billing_base_url}/plans/")
|
|
||||||
|
|
||||||
# upgrade to business plan
|
# upgrade to business plan
|
||||||
with time_machine.travel(self.now, tick=False):
|
with time_machine.travel(self.now, tick=False):
|
||||||
result = self.client_get(f"{billing_base_url}/upgrade/", subdomain="selfhosting")
|
result = self.client_get(f"{billing_base_url}/upgrade/", subdomain="selfhosting")
|
||||||
|
|
|
@ -47,7 +47,11 @@ from zerver.lib.exceptions import (
|
||||||
RemoteBillingAuthenticationError,
|
RemoteBillingAuthenticationError,
|
||||||
RemoteRealmServerMismatchError,
|
RemoteRealmServerMismatchError,
|
||||||
)
|
)
|
||||||
from zerver.lib.remote_server import RealmDataForAnalytics, UserDataForRemoteBilling
|
from zerver.lib.remote_server import (
|
||||||
|
RealmDataForAnalytics,
|
||||||
|
UserDataForRemoteBilling,
|
||||||
|
get_realms_info_for_push_bouncer,
|
||||||
|
)
|
||||||
from zerver.lib.response import json_success
|
from zerver.lib.response import json_success
|
||||||
from zerver.lib.send_email import FromAddress, send_email
|
from zerver.lib.send_email import FromAddress, send_email
|
||||||
from zerver.lib.timestamp import datetime_to_timestamp
|
from zerver.lib.timestamp import datetime_to_timestamp
|
||||||
|
@ -61,6 +65,7 @@ from zilencer.models import (
|
||||||
RemoteZulipServer,
|
RemoteZulipServer,
|
||||||
get_remote_server_by_uuid,
|
get_remote_server_by_uuid,
|
||||||
)
|
)
|
||||||
|
from zilencer.views import handle_customer_migration_from_server_to_realms
|
||||||
|
|
||||||
billing_logger = logging.getLogger("corporate.stripe")
|
billing_logger = logging.getLogger("corporate.stripe")
|
||||||
|
|
||||||
|
@ -194,6 +199,23 @@ def remote_realm_billing_finalize_login(
|
||||||
# pretty recently. (And we generally don't delete these at all.)
|
# pretty recently. (And we generally don't delete these at all.)
|
||||||
raise AssertionError
|
raise AssertionError
|
||||||
|
|
||||||
|
try:
|
||||||
|
handle_customer_migration_from_server_to_realms(
|
||||||
|
server=remote_server, realms=get_realms_info_for_push_bouncer()
|
||||||
|
)
|
||||||
|
except Exception: # nocoverage
|
||||||
|
billing_logger.exception(
|
||||||
|
"%s: Failed to migrate customer from server (id: %s) to realms",
|
||||||
|
request.path,
|
||||||
|
remote_server.id,
|
||||||
|
stack_info=True,
|
||||||
|
)
|
||||||
|
raise JsonableError(
|
||||||
|
_(
|
||||||
|
"Failed to migrate customer from server to realms. Please contact support for assistance."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Redirect to error page if server is on an active plan
|
# Redirect to error page if server is on an active plan
|
||||||
server_customer = get_customer_by_remote_server(remote_server)
|
server_customer = get_customer_by_remote_server(remote_server)
|
||||||
if server_customer is not None:
|
if server_customer is not None:
|
||||||
|
|
|
@ -993,21 +993,6 @@ def remote_server_post_analytics(
|
||||||
if remote_server_version_updated:
|
if remote_server_version_updated:
|
||||||
fix_remote_realm_foreign_keys(server, realms)
|
fix_remote_realm_foreign_keys(server, realms)
|
||||||
|
|
||||||
try:
|
|
||||||
handle_customer_migration_from_server_to_realms(server, realms)
|
|
||||||
except Exception: # nocoverage
|
|
||||||
logger.exception(
|
|
||||||
"%s: Failed to migrate customer from server (id: %s) to realms",
|
|
||||||
request.path,
|
|
||||||
server.id,
|
|
||||||
stack_info=True,
|
|
||||||
)
|
|
||||||
raise JsonableError(
|
|
||||||
_(
|
|
||||||
"Failed to migrate customer from server to realms. Please contact support for assistance."
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
realm_id_to_remote_realm = build_realm_id_to_remote_realm_dict(server, realms)
|
realm_id_to_remote_realm = build_realm_id_to_remote_realm_dict(server, realms)
|
||||||
|
|
||||||
remote_realm_counts = [
|
remote_realm_counts = [
|
||||||
|
|
Loading…
Reference in New Issue