mirror of https://github.com/zulip/zulip.git
zilencer: Return can_push info at the push/notify endpoint.
This provides the remote server this information to refresh it on its Realm attributes whenever it sends a push notification. Fixes #27483.
This commit is contained in:
parent
b09f3a2da1
commit
c1988a14a7
|
@ -46,6 +46,7 @@ from zerver.lib.send_email import (
|
||||||
send_email_to_billing_admins_and_realm_owners,
|
send_email_to_billing_admins_and_realm_owners,
|
||||||
)
|
)
|
||||||
from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime
|
from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime
|
||||||
|
from zerver.lib.types import RemoteRealmDictValue
|
||||||
from zerver.lib.url_encoding import append_url_query_string
|
from zerver.lib.url_encoding import append_url_query_string
|
||||||
from zerver.lib.utils import assert_is_not_none
|
from zerver.lib.utils import assert_is_not_none
|
||||||
from zerver.models import (
|
from zerver.models import (
|
||||||
|
@ -3244,6 +3245,28 @@ class RemoteRealmBillingSession(BillingSession): # nocoverage
|
||||||
if plan is None:
|
if plan is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def get_push_service_validity_dict(self) -> RemoteRealmDictValue:
|
||||||
|
customer = self.get_customer()
|
||||||
|
if customer is None:
|
||||||
|
return {"can_push": True, "expected_end_timestamp": None}
|
||||||
|
|
||||||
|
current_plan = get_current_plan_by_customer(customer)
|
||||||
|
if current_plan is None:
|
||||||
|
return {"can_push": True, "expected_end_timestamp": None}
|
||||||
|
|
||||||
|
expected_end_timestamp = None
|
||||||
|
if current_plan.status in [
|
||||||
|
CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE,
|
||||||
|
CustomerPlan.DOWNGRADE_AT_END_OF_FREE_TRIAL,
|
||||||
|
]:
|
||||||
|
expected_end_timestamp = datetime_to_timestamp(
|
||||||
|
self.get_next_billing_cycle(current_plan)
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
"can_push": True,
|
||||||
|
"expected_end_timestamp": expected_end_timestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class RemoteServerBillingSession(BillingSession): # nocoverage
|
class RemoteServerBillingSession(BillingSession): # nocoverage
|
||||||
"""Billing session for pre-8.0 servers that do not yet support
|
"""Billing session for pre-8.0 servers that do not yet support
|
||||||
|
|
|
@ -641,6 +641,21 @@ def send_notifications_to_bouncer(
|
||||||
increment=total_android_devices + total_apple_devices,
|
increment=total_android_devices + total_apple_devices,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
remote_realm_dict = response_data.get("realm")
|
||||||
|
if remote_realm_dict is not None:
|
||||||
|
# The server may have updated our understanding of whether
|
||||||
|
# push notifications will work.
|
||||||
|
assert isinstance(remote_realm_dict, dict)
|
||||||
|
do_set_realm_property(
|
||||||
|
user_profile.realm,
|
||||||
|
"push_notifications_enabled",
|
||||||
|
remote_realm_dict["can_push"],
|
||||||
|
acting_user=None,
|
||||||
|
)
|
||||||
|
do_set_push_notifications_enabled_end_timestamp(
|
||||||
|
user_profile.realm, remote_realm_dict["expected_end_timestamp"], acting_user=None
|
||||||
|
)
|
||||||
|
|
||||||
return total_android_devices, total_apple_devices
|
return total_android_devices, total_apple_devices
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -591,6 +591,7 @@ class PushBouncerNotificationTest(BouncerTestCase):
|
||||||
payload = {
|
payload = {
|
||||||
"user_id": hamlet.id,
|
"user_id": hamlet.id,
|
||||||
"user_uuid": str(hamlet.uuid),
|
"user_uuid": str(hamlet.uuid),
|
||||||
|
"realm_uuid": str(hamlet.realm.uuid),
|
||||||
"gcm_payload": {"event": "remove", "zulip_message_ids": many_ids},
|
"gcm_payload": {"event": "remove", "zulip_message_ids": many_ids},
|
||||||
"apns_payload": {
|
"apns_payload": {
|
||||||
"badge": 0,
|
"badge": 0,
|
||||||
|
@ -619,6 +620,7 @@ class PushBouncerNotificationTest(BouncerTestCase):
|
||||||
"total_android_devices": 2,
|
"total_android_devices": 2,
|
||||||
"total_apple_devices": 1,
|
"total_apple_devices": 1,
|
||||||
"deleted_devices": {"android_devices": [], "apple_devices": []},
|
"deleted_devices": {"android_devices": [], "apple_devices": []},
|
||||||
|
"realm": {"can_push": True, "expected_end_timestamp": None},
|
||||||
},
|
},
|
||||||
data,
|
data,
|
||||||
)
|
)
|
||||||
|
@ -1813,7 +1815,9 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
"zilencer.views.RemoteRealmBillingSession.get_customer", return_value=dummy_customer
|
"zilencer.views.RemoteRealmBillingSession.get_customer", return_value=dummy_customer
|
||||||
):
|
):
|
||||||
with mock.patch("zilencer.views.get_current_plan_by_customer", return_value=None) as m:
|
with mock.patch(
|
||||||
|
"corporate.lib.stripe.get_current_plan_by_customer", return_value=None
|
||||||
|
) as m:
|
||||||
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
send_server_data_to_push_bouncer(consider_usage_statistics=False)
|
||||||
m.assert_called()
|
m.assert_called()
|
||||||
realms = Realm.objects.all()
|
realms = Realm.objects.all()
|
||||||
|
@ -1828,7 +1832,8 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
||||||
"zilencer.views.RemoteRealmBillingSession.get_customer", return_value=dummy_customer
|
"zilencer.views.RemoteRealmBillingSession.get_customer", return_value=dummy_customer
|
||||||
):
|
):
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
"zilencer.views.get_current_plan_by_customer", return_value=dummy_customer_plan
|
"corporate.lib.stripe.get_current_plan_by_customer",
|
||||||
|
return_value=dummy_customer_plan,
|
||||||
):
|
):
|
||||||
with mock.patch(
|
with mock.patch(
|
||||||
"zilencer.views.RemoteRealmBillingSession.get_next_billing_cycle",
|
"zilencer.views.RemoteRealmBillingSession.get_next_billing_cycle",
|
||||||
|
@ -3614,6 +3619,7 @@ class TestSendNotificationsToBouncer(PushNotificationTest):
|
||||||
android_devices=[device.token for device in android_devices],
|
android_devices=[device.token for device in android_devices],
|
||||||
apple_devices=[device.token for device in apple_devices],
|
apple_devices=[device.token for device in apple_devices],
|
||||||
),
|
),
|
||||||
|
"realm": {"can_push": True, "expected_end_timestamp": None},
|
||||||
}
|
}
|
||||||
total_android_devices, total_apple_devices = send_notifications_to_bouncer(
|
total_android_devices, total_apple_devices = send_notifications_to_bouncer(
|
||||||
user, {"apns": True}, {"gcm": True}, {}, list(android_devices), list(apple_devices)
|
user, {"apns": True}, {"gcm": True}, {}, list(android_devices), list(apple_devices)
|
||||||
|
@ -3650,6 +3656,23 @@ class TestSendNotificationsToBouncer(PushNotificationTest):
|
||||||
self.assertEqual(PushDeviceToken.objects.filter(kind=PushDeviceToken.APNS).count(), 0)
|
self.assertEqual(PushDeviceToken.objects.filter(kind=PushDeviceToken.APNS).count(), 0)
|
||||||
self.assertEqual(PushDeviceToken.objects.filter(kind=PushDeviceToken.GCM).count(), 0)
|
self.assertEqual(PushDeviceToken.objects.filter(kind=PushDeviceToken.GCM).count(), 0)
|
||||||
|
|
||||||
|
# Now simulating getting "can_push" as False from the bouncer and verify
|
||||||
|
# that we update the realm value.
|
||||||
|
mock_send.return_value = {
|
||||||
|
"total_android_devices": 1,
|
||||||
|
"total_apple_devices": 3,
|
||||||
|
"realm": {"can_push": False, "expected_end_timestamp": None},
|
||||||
|
"deleted_devices": DevicesToCleanUpDict(
|
||||||
|
android_devices=[],
|
||||||
|
apple_devices=[],
|
||||||
|
),
|
||||||
|
}
|
||||||
|
total_android_devices, total_apple_devices = send_notifications_to_bouncer(
|
||||||
|
user, {"apns": True}, {"gcm": True}, {}, list(android_devices), list(apple_devices)
|
||||||
|
)
|
||||||
|
user.realm.refresh_from_db()
|
||||||
|
self.assertEqual(user.realm.push_notifications_enabled, False)
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PUSH_NOTIFICATION_BOUNCER_URL="https://push.zulip.org.example.com")
|
@override_settings(PUSH_NOTIFICATION_BOUNCER_URL="https://push.zulip.org.example.com")
|
||||||
class TestSendToPushBouncer(ZulipTestCase):
|
class TestSendToPushBouncer(ZulipTestCase):
|
||||||
|
|
|
@ -47,7 +47,7 @@ from zerver.lib.remote_server import (
|
||||||
)
|
)
|
||||||
from zerver.lib.request import REQ, has_request_variables
|
from zerver.lib.request import REQ, has_request_variables
|
||||||
from zerver.lib.response import json_success
|
from zerver.lib.response import json_success
|
||||||
from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime
|
from zerver.lib.timestamp import timestamp_to_datetime
|
||||||
from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint
|
from zerver.lib.typed_endpoint import JsonBodyPayload, typed_endpoint
|
||||||
from zerver.lib.types import RemoteRealmDictValue
|
from zerver.lib.types import RemoteRealmDictValue
|
||||||
from zerver.lib.validator import check_capped_string, check_int, check_string_fixed_length
|
from zerver.lib.validator import check_capped_string, check_int, check_string_fixed_length
|
||||||
|
@ -514,6 +514,8 @@ def remote_server_notify_push(
|
||||||
timezone_now(),
|
timezone_now(),
|
||||||
increment=android_successfully_delivered + apple_successfully_delivered,
|
increment=android_successfully_delivered + apple_successfully_delivered,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
remote_realm_dict: Optional[RemoteRealmDictValue] = None
|
||||||
if remote_realm is not None:
|
if remote_realm is not None:
|
||||||
do_increment_logging_stat(
|
do_increment_logging_stat(
|
||||||
remote_realm,
|
remote_realm,
|
||||||
|
@ -522,6 +524,8 @@ def remote_server_notify_push(
|
||||||
timezone_now(),
|
timezone_now(),
|
||||||
increment=android_successfully_delivered + apple_successfully_delivered,
|
increment=android_successfully_delivered + apple_successfully_delivered,
|
||||||
)
|
)
|
||||||
|
billing_session = RemoteRealmBillingSession(remote_realm)
|
||||||
|
remote_realm_dict = billing_session.get_push_service_validity_dict()
|
||||||
|
|
||||||
deleted_devices = get_deleted_devices(
|
deleted_devices = get_deleted_devices(
|
||||||
user_identity,
|
user_identity,
|
||||||
|
@ -536,6 +540,7 @@ def remote_server_notify_push(
|
||||||
"total_android_devices": len(android_devices),
|
"total_android_devices": len(android_devices),
|
||||||
"total_apple_devices": len(apple_devices),
|
"total_apple_devices": len(apple_devices),
|
||||||
"deleted_devices": deleted_devices,
|
"deleted_devices": deleted_devices,
|
||||||
|
"realm": remote_realm_dict,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -968,29 +973,7 @@ def remote_server_post_analytics(
|
||||||
for remote_realm in remote_realms:
|
for remote_realm in remote_realms:
|
||||||
uuid = str(remote_realm.uuid)
|
uuid = str(remote_realm.uuid)
|
||||||
billing_session = RemoteRealmBillingSession(remote_realm)
|
billing_session = RemoteRealmBillingSession(remote_realm)
|
||||||
|
remote_realm_dict[uuid] = billing_session.get_push_service_validity_dict()
|
||||||
customer = billing_session.get_customer()
|
|
||||||
if customer is None:
|
|
||||||
remote_realm_dict[uuid] = {"can_push": True, "expected_end_timestamp": None}
|
|
||||||
continue
|
|
||||||
|
|
||||||
current_plan = get_current_plan_by_customer(customer)
|
|
||||||
if current_plan is None:
|
|
||||||
remote_realm_dict[uuid] = {"can_push": True, "expected_end_timestamp": None}
|
|
||||||
continue
|
|
||||||
|
|
||||||
expected_end_timestamp = None
|
|
||||||
if current_plan.status in [
|
|
||||||
CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE,
|
|
||||||
CustomerPlan.DOWNGRADE_AT_END_OF_FREE_TRIAL,
|
|
||||||
]:
|
|
||||||
expected_end_timestamp = datetime_to_timestamp(
|
|
||||||
billing_session.get_next_billing_cycle(current_plan)
|
|
||||||
)
|
|
||||||
remote_realm_dict[uuid] = {
|
|
||||||
"can_push": True,
|
|
||||||
"expected_end_timestamp": expected_end_timestamp,
|
|
||||||
}
|
|
||||||
|
|
||||||
return json_success(request, data={"realms": remote_realm_dict})
|
return json_success(request, data={"realms": remote_realm_dict})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue