mirror of https://github.com/zulip/zulip.git
remote_server: Eliminate separate realms-only code path.
Given that most of the use cases for realms-only code path would really like to upload audit logs too, and the others would likely produce a better user experience if they upoaded audit logs, we should just have a single main code path here i.e. 'send_analytics_to_push_bouncer'. We still only upload usage statistics according to documented option, and only from the analytics cron job. The error handling takes place in 'send_analytics_to_push_bouncer' itself.
This commit is contained in:
parent
6f93ab72c0
commit
d763fae9d0
|
@ -106,4 +106,4 @@ class Command(BaseCommand):
|
|||
logger.info("Sleeping %d seconds before reporting...", delay)
|
||||
time.sleep(delay)
|
||||
|
||||
send_analytics_to_push_bouncer()
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=True)
|
||||
|
|
|
@ -15,7 +15,7 @@ from corporate.lib.remote_billing_util import (
|
|||
RemoteBillingIdentityDict,
|
||||
RemoteBillingUserDict,
|
||||
)
|
||||
from zerver.lib.remote_server import send_realms_only_to_push_bouncer
|
||||
from zerver.lib.remote_server import send_analytics_to_push_bouncer
|
||||
from zerver.lib.test_classes import BouncerTestCase
|
||||
from zerver.lib.timestamp import datetime_to_timestamp
|
||||
from zerver.models import UserProfile
|
||||
|
@ -163,7 +163,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase):
|
|||
realm = desdemona.realm
|
||||
|
||||
self.add_mock_response()
|
||||
send_realms_only_to_push_bouncer()
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
|
||||
result = self.execute_remote_billing_authentication_flow(desdemona)
|
||||
|
||||
|
@ -193,14 +193,14 @@ class RemoteBillingAuthenticationTest(BouncerTestCase):
|
|||
# and successfully completing the flow - transparently to the user.
|
||||
self.assertFalse(RemoteRealm.objects.filter(uuid=realm.uuid).exists())
|
||||
|
||||
# send_realms_only_to_push_bouncer will be called within the endpoint's
|
||||
# send_analytics_to_push_bouncer will be called within the endpoint's
|
||||
# error handling to register realms with the bouncer. We mock.patch it
|
||||
# to be able to assert that it was called - but also use side_effect
|
||||
# to maintain the original behavior of the function, instead of
|
||||
# replacing it with a Mock.
|
||||
with mock.patch(
|
||||
"zerver.views.push_notifications.send_realms_only_to_push_bouncer",
|
||||
side_effect=send_realms_only_to_push_bouncer,
|
||||
"zerver.views.push_notifications.send_analytics_to_push_bouncer",
|
||||
side_effect=send_analytics_to_push_bouncer,
|
||||
) as m:
|
||||
result = self.execute_remote_billing_authentication_flow(desdemona)
|
||||
|
||||
|
@ -219,7 +219,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase):
|
|||
desdemona = self.example_user("desdemona")
|
||||
|
||||
self.add_mock_response()
|
||||
send_realms_only_to_push_bouncer()
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
|
||||
result = self.execute_remote_billing_authentication_flow(
|
||||
desdemona,
|
||||
|
@ -235,7 +235,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase):
|
|||
desdemona = self.example_user("desdemona")
|
||||
|
||||
self.add_mock_response()
|
||||
send_realms_only_to_push_bouncer()
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
|
||||
with self.settings(TERMS_OF_SERVICE_VERSION="1.0"):
|
||||
result = self.execute_remote_billing_authentication_flow(
|
||||
|
@ -280,7 +280,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase):
|
|||
realm = desdemona.realm
|
||||
|
||||
self.add_mock_response()
|
||||
send_realms_only_to_push_bouncer()
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
|
||||
with time_machine.travel(now, tick=False):
|
||||
result = self.execute_remote_billing_authentication_flow(desdemona)
|
||||
|
@ -336,7 +336,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase):
|
|||
realm = desdemona.realm
|
||||
|
||||
self.add_mock_response()
|
||||
send_realms_only_to_push_bouncer()
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
|
||||
# Straight-up access without authing at all:
|
||||
result = self.client_get(f"/realm/{realm.uuid!s}/plans/", subdomain="selfhosting")
|
||||
|
@ -388,7 +388,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase):
|
|||
realm = desdemona.realm
|
||||
|
||||
self.add_mock_response()
|
||||
send_realms_only_to_push_bouncer()
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
|
||||
result = self.execute_remote_billing_authentication_flow(desdemona, "sponsorship")
|
||||
|
||||
|
@ -408,7 +408,7 @@ class RemoteBillingAuthenticationTest(BouncerTestCase):
|
|||
realm = desdemona.realm
|
||||
|
||||
self.add_mock_response()
|
||||
send_realms_only_to_push_bouncer()
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
|
||||
result = self.execute_remote_billing_authentication_flow(desdemona, "upgrade")
|
||||
|
||||
|
|
|
@ -44,9 +44,8 @@ from zerver.lib.exceptions import ErrorCode, JsonableError
|
|||
from zerver.lib.message import access_message, huddle_users
|
||||
from zerver.lib.outgoing_http import OutgoingSession
|
||||
from zerver.lib.remote_server import (
|
||||
PushNotificationBouncerServerError,
|
||||
send_analytics_to_push_bouncer,
|
||||
send_json_to_push_bouncer,
|
||||
send_realms_only_to_push_bouncer,
|
||||
send_to_push_bouncer,
|
||||
)
|
||||
from zerver.lib.soft_deactivation import soft_reactivate_if_personal_notification
|
||||
|
@ -783,37 +782,9 @@ def initialize_push_notifications() -> None:
|
|||
|
||||
if uses_notification_bouncer():
|
||||
# If we're using the notification bouncer, check if we can
|
||||
# actually send push notifications.
|
||||
|
||||
try:
|
||||
realms = send_realms_only_to_push_bouncer()
|
||||
except PushNotificationBouncerServerError: # nocoverage
|
||||
# 50x errors from the bouncer cannot be addressed by the
|
||||
# administrator of this server, and may be localized to
|
||||
# this endpoint; don't rashly mark push notifications as
|
||||
# disabled when they are likely working perfectly fine.
|
||||
return
|
||||
except Exception:
|
||||
# An exception was thrown trying to ask the bouncer service whether we can send
|
||||
# push notifications or not. There may be certain transient failures that we could
|
||||
# ignore here, but the default explanation is that there is something wrong either
|
||||
# with our credentials being corrupted or our ability to reach the bouncer service
|
||||
# over the network, so we immediately move to reporting push notifications as likely not working,
|
||||
# as whatever failed here is likely to also fail when trying to send a push notification.
|
||||
for realm in Realm.objects.filter(push_notifications_enabled=True):
|
||||
do_set_realm_property(realm, "push_notifications_enabled", False, acting_user=None)
|
||||
do_set_push_notifications_enabled_end_timestamp(realm, None, acting_user=None)
|
||||
logger.exception("Exception while sending realms only data to push bouncer")
|
||||
return
|
||||
|
||||
for realm_uuid, data in realms.items():
|
||||
realm = Realm.objects.get(uuid=realm_uuid)
|
||||
do_set_realm_property(
|
||||
realm, "push_notifications_enabled", data["can_push"], acting_user=None
|
||||
)
|
||||
do_set_push_notifications_enabled_end_timestamp(
|
||||
realm, data["expected_end_timestamp"], acting_user=None
|
||||
)
|
||||
# actually send push notifications, and update our
|
||||
# understanding of that state for each realm accordingly.
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
return
|
||||
|
||||
logger.warning( # nocoverage
|
||||
|
|
|
@ -11,6 +11,10 @@ from pydantic import UUID4, BaseModel, ConfigDict, Field, Json, field_validator
|
|||
|
||||
from analytics.models import InstallationCount, RealmCount
|
||||
from version import API_FEATURE_LEVEL, ZULIP_VERSION
|
||||
from zerver.actions.realm_settings import (
|
||||
do_set_push_notifications_enabled_end_timestamp,
|
||||
do_set_realm_property,
|
||||
)
|
||||
from zerver.lib.exceptions import (
|
||||
JsonableError,
|
||||
MissingRemoteRealmError,
|
||||
|
@ -18,7 +22,6 @@ from zerver.lib.exceptions import (
|
|||
)
|
||||
from zerver.lib.outgoing_http import OutgoingSession
|
||||
from zerver.lib.queue import queue_json_publish
|
||||
from zerver.lib.types import RemoteRealmDictValue
|
||||
from zerver.models import OrgTypeEnum, Realm, RealmAuditLog
|
||||
|
||||
|
||||
|
@ -311,7 +314,7 @@ def get_realms_info_for_push_bouncer(realm_id: Optional[int] = None) -> List[Rea
|
|||
return realm_info_list
|
||||
|
||||
|
||||
def send_analytics_to_push_bouncer() -> None:
|
||||
def send_analytics_to_push_bouncer(consider_usage_statistics: bool = True) -> None:
|
||||
logger = logging.getLogger("zulip.analytics")
|
||||
# first, check what's latest
|
||||
try:
|
||||
|
@ -326,7 +329,10 @@ def send_analytics_to_push_bouncer() -> None:
|
|||
last_acked_installation_count_id = result["last_installation_count_id"]
|
||||
last_acked_realmauditlog_id = result["last_realmauditlog_id"]
|
||||
|
||||
if settings.SUBMIT_USAGE_STATISTICS:
|
||||
if settings.SUBMIT_USAGE_STATISTICS and consider_usage_statistics:
|
||||
# Only upload usage statistics, which is relatively expensive,
|
||||
# if called from the analytics cron job and the server has
|
||||
# uploading such statistics enabled.
|
||||
installation_count_query = InstallationCount.objects.filter(
|
||||
id__gt=last_acked_installation_count_id
|
||||
)
|
||||
|
@ -353,32 +359,48 @@ def send_analytics_to_push_bouncer() -> None:
|
|||
api_feature_level=API_FEATURE_LEVEL,
|
||||
)
|
||||
|
||||
# Send the actual request, and process the response.
|
||||
try:
|
||||
send_to_push_bouncer("POST", "server/analytics", request.model_dump(round_trip=True))
|
||||
except JsonableError as e:
|
||||
logger.warning(e.msg)
|
||||
logger.info("Reported %d records", record_count)
|
||||
response = send_to_push_bouncer(
|
||||
"POST", "server/analytics", request.model_dump(round_trip=True)
|
||||
)
|
||||
except PushNotificationBouncerServerError: # nocoverage
|
||||
# 50x errors from the bouncer cannot be addressed by the
|
||||
# administrator of this server, and may be localized to
|
||||
# this endpoint; don't rashly mark push notifications as
|
||||
# disabled when they are likely working perfectly fine.
|
||||
return
|
||||
except Exception as e:
|
||||
if isinstance(e, JsonableError):
|
||||
# Log exceptions with server error messages to the analytics log.
|
||||
logger.warning(e.msg)
|
||||
|
||||
# An exception was thrown trying to ask the bouncer service whether we can send
|
||||
# push notifications or not. There may be certain transient failures that we could
|
||||
# ignore here, but the default explanation is that there is something wrong either
|
||||
# with our credentials being corrupted or our ability to reach the bouncer service
|
||||
# over the network, so we immediately move to reporting push notifications as likely not working,
|
||||
# as whatever failed here is likely to also fail when trying to send a push notification.
|
||||
for realm in Realm.objects.filter(push_notifications_enabled=True):
|
||||
do_set_realm_property(realm, "push_notifications_enabled", False, acting_user=None)
|
||||
do_set_push_notifications_enabled_end_timestamp(realm, None, acting_user=None)
|
||||
if not isinstance(e, JsonableError):
|
||||
# Log this generic error only if we haven't already logged specific error above.
|
||||
logger.exception("Exception asking push bouncer if notifications will work.")
|
||||
return
|
||||
|
||||
def send_realms_only_to_push_bouncer() -> Dict[str, RemoteRealmDictValue]:
|
||||
request = AnalyticsRequest.model_construct(
|
||||
realm_counts=[],
|
||||
installation_counts=[],
|
||||
realms=get_realms_info_for_push_bouncer(),
|
||||
version=ZULIP_VERSION,
|
||||
api_feature_level=API_FEATURE_LEVEL,
|
||||
)
|
||||
|
||||
# We don't catch JsonableError here, because we want it to propagate further
|
||||
# to either explicitly, loudly fail or be error-handled by the caller.
|
||||
response = send_to_push_bouncer(
|
||||
"POST",
|
||||
"server/analytics",
|
||||
request.model_dump(round_trip=True, exclude={"realmauditlog_rows"}),
|
||||
)
|
||||
assert isinstance(response["realms"], dict) # for mypy
|
||||
realms = response["realms"]
|
||||
for realm_uuid, data in realms.items():
|
||||
realm = Realm.objects.get(uuid=realm_uuid)
|
||||
do_set_realm_property(
|
||||
realm, "push_notifications_enabled", data["can_push"], acting_user=None
|
||||
)
|
||||
do_set_push_notifications_enabled_end_timestamp(
|
||||
realm, data["expected_end_timestamp"], acting_user=None
|
||||
)
|
||||
|
||||
return response["realms"]
|
||||
logger.info("Reported %d records", record_count)
|
||||
|
||||
|
||||
def enqueue_register_realm_with_push_bouncer_if_needed(realm: Realm) -> None:
|
||||
|
|
|
@ -1399,14 +1399,28 @@ class RealmImportExportTest(ExportFile):
|
|||
with self.settings(BILLING_ENABLED=False), self.assertLogs(level="INFO"), patch(
|
||||
"zerver.lib.remote_server.send_to_push_bouncer"
|
||||
) as m:
|
||||
get_response = {
|
||||
"last_realm_count_id": 0,
|
||||
"last_installation_count_id": 0,
|
||||
"last_realmauditlog_id": 0,
|
||||
}
|
||||
|
||||
def mock_send_to_push_bouncer_response( # type: ignore[return]
|
||||
method: str, *args: Any
|
||||
) -> Optional[Dict[str, int]]:
|
||||
if method == "GET":
|
||||
return get_response
|
||||
|
||||
m.side_effect = mock_send_to_push_bouncer_response
|
||||
|
||||
new_realm = do_import_realm(get_output_dir(), "test-zulip")
|
||||
|
||||
self.assertTrue(Realm.objects.filter(string_id="test-zulip").exists())
|
||||
calls_args_for_assert = m.call_args_list[0][0]
|
||||
calls_args_for_assert = m.call_args_list[1][0]
|
||||
self.assertEqual(calls_args_for_assert[0], "POST")
|
||||
self.assertEqual(calls_args_for_assert[1], "server/analytics")
|
||||
self.assertIn(
|
||||
new_realm.id, [realm["id"] for realm in json.loads(m.call_args_list[0][0][2]["realms"])]
|
||||
new_realm.id, [realm["id"] for realm in json.loads(m.call_args_list[1][0][2]["realms"])]
|
||||
)
|
||||
|
||||
def test_import_files_from_local(self) -> None:
|
||||
|
|
|
@ -24,7 +24,7 @@ from typing_extensions import override
|
|||
from analytics.lib.counts import CountStat, LoggingCountStat
|
||||
from analytics.models import InstallationCount, RealmCount
|
||||
from corporate.models import CustomerPlan
|
||||
from version import API_FEATURE_LEVEL, ZULIP_VERSION
|
||||
from version import ZULIP_VERSION
|
||||
from zerver.actions.message_delete import do_delete_messages
|
||||
from zerver.actions.message_flags import do_mark_stream_messages_as_read, do_update_message_flags
|
||||
from zerver.actions.realm_settings import (
|
||||
|
@ -67,7 +67,6 @@ from zerver.lib.remote_server import (
|
|||
build_analytics_data,
|
||||
get_realms_info_for_push_bouncer,
|
||||
send_analytics_to_push_bouncer,
|
||||
send_realms_only_to_push_bouncer,
|
||||
send_to_push_bouncer,
|
||||
)
|
||||
from zerver.lib.response import json_response_from_error
|
||||
|
@ -780,30 +779,29 @@ class PushBouncerNotificationTest(BouncerTestCase):
|
|||
with mock.patch(
|
||||
"zerver.lib.push_notifications.uses_notification_bouncer", return_value=True
|
||||
):
|
||||
realms_response = {realm.uuid: {"can_push": True, "expected_end_timestamp": None}}
|
||||
with mock.patch(
|
||||
"zerver.lib.push_notifications.send_realms_only_to_push_bouncer",
|
||||
return_value=realms_response,
|
||||
):
|
||||
with mock.patch("zerver.lib.remote_server.send_to_push_bouncer") as m:
|
||||
post_response = {
|
||||
"realms": {realm.uuid: {"can_push": True, "expected_end_timestamp": None}}
|
||||
}
|
||||
get_response = {
|
||||
"last_realm_count_id": 0,
|
||||
"last_installation_count_id": 0,
|
||||
"last_realmauditlog_id": 0,
|
||||
}
|
||||
|
||||
def mock_send_to_push_bouncer_response(method: str, *args: Any) -> Dict[str, Any]:
|
||||
if method == "POST":
|
||||
return post_response
|
||||
return get_response
|
||||
|
||||
m.side_effect = mock_send_to_push_bouncer_response
|
||||
|
||||
initialize_push_notifications()
|
||||
|
||||
realm = get_realm("zulip")
|
||||
self.assertTrue(realm.push_notifications_enabled)
|
||||
self.assertEqual(realm.push_notifications_enabled_end_timestamp, None)
|
||||
|
||||
with mock.patch(
|
||||
"zerver.lib.push_notifications.send_realms_only_to_push_bouncer",
|
||||
side_effect=Exception,
|
||||
), self.assertLogs("zerver.lib.push_notifications", level="ERROR") as exception_log:
|
||||
initialize_push_notifications()
|
||||
|
||||
realm = get_realm("zulip")
|
||||
self.assertFalse(realm.push_notifications_enabled)
|
||||
self.assertIn(
|
||||
"ERROR:zerver.lib.push_notifications:Exception while sending realms only data to push bouncer",
|
||||
exception_log.output[0],
|
||||
)
|
||||
|
||||
@override_settings(PUSH_NOTIFICATION_BOUNCER_URL="https://push.zulip.org.example.com")
|
||||
@responses.activate
|
||||
def test_register_token_realm_uuid_belongs_to_different_server(self) -> None:
|
||||
|
@ -1044,10 +1042,6 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
|||
|
||||
self.add_mock_response()
|
||||
# Send any existing data over, so that we can start the test with a "clean" slate
|
||||
audit_log = RealmAuditLog.objects.all().order_by("id").last()
|
||||
assert audit_log is not None
|
||||
audit_log_max_id = audit_log.id
|
||||
|
||||
remote_server = RemoteZulipServer.objects.get(uuid=self.server_uuid)
|
||||
assert remote_server is not None
|
||||
assert remote_server.last_version is None
|
||||
|
@ -1055,6 +1049,10 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
|||
send_analytics_to_push_bouncer()
|
||||
self.assertTrue(responses.assert_call_count(ANALYTICS_STATUS_URL, 1))
|
||||
|
||||
audit_log = RealmAuditLog.objects.all().order_by("id").last()
|
||||
assert audit_log is not None
|
||||
audit_log_max_id = audit_log.id
|
||||
|
||||
remote_server = RemoteZulipServer.objects.get(uuid=self.server_uuid)
|
||||
assert remote_server.last_version == ZULIP_VERSION
|
||||
|
||||
|
@ -1126,8 +1124,14 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
|||
send_analytics_to_push_bouncer()
|
||||
check_counts(2, 2, 0, 0, 1)
|
||||
|
||||
with self.settings(SUBMIT_USAGE_STATISTICS=True):
|
||||
# With 'SUBMIT_USAGE_STATISTICS=True' but 'consider_usage_statistics=False',
|
||||
# we don't send RealmCount and InstallationCounts.
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
check_counts(3, 3, 0, 0, 1)
|
||||
|
||||
send_analytics_to_push_bouncer()
|
||||
check_counts(3, 3, 1, 1, 1)
|
||||
check_counts(4, 4, 1, 1, 1)
|
||||
|
||||
self.assertEqual(
|
||||
list(
|
||||
|
@ -1204,7 +1208,7 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
|||
do_deactivate_realm(zephyr_realm, acting_user=None)
|
||||
|
||||
send_analytics_to_push_bouncer()
|
||||
check_counts(4, 4, 1, 1, 7)
|
||||
check_counts(5, 5, 1, 1, 7)
|
||||
|
||||
zephyr_remote_realm = RemoteRealm.objects.get(uuid=zephyr_realm.uuid)
|
||||
self.assertEqual(zephyr_remote_realm.host, zephyr_realm.host)
|
||||
|
@ -1282,7 +1286,7 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
|||
|
||||
# Test having no new rows
|
||||
send_analytics_to_push_bouncer()
|
||||
check_counts(5, 5, 1, 1, 7)
|
||||
check_counts(6, 6, 1, 1, 7)
|
||||
|
||||
# Test only having new RealmCount rows
|
||||
RealmCount.objects.create(
|
||||
|
@ -1298,14 +1302,14 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
|||
value=9,
|
||||
)
|
||||
send_analytics_to_push_bouncer()
|
||||
check_counts(6, 6, 3, 1, 7)
|
||||
check_counts(7, 7, 3, 1, 7)
|
||||
|
||||
# Test only having new InstallationCount rows
|
||||
InstallationCount.objects.create(
|
||||
property=realm_stat.property, end_time=end_time + timedelta(days=1), value=6
|
||||
)
|
||||
send_analytics_to_push_bouncer()
|
||||
check_counts(7, 7, 3, 2, 7)
|
||||
check_counts(8, 8, 3, 2, 7)
|
||||
|
||||
# Test only having new RealmAuditLog rows
|
||||
# Non-synced event
|
||||
|
@ -1317,7 +1321,7 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
|||
extra_data={"data": "foo"},
|
||||
)
|
||||
send_analytics_to_push_bouncer()
|
||||
check_counts(8, 8, 3, 2, 7)
|
||||
check_counts(9, 9, 3, 2, 7)
|
||||
# Synced event
|
||||
RealmAuditLog.objects.create(
|
||||
realm=user.realm,
|
||||
|
@ -1329,7 +1333,7 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
|||
},
|
||||
)
|
||||
send_analytics_to_push_bouncer()
|
||||
check_counts(9, 9, 3, 2, 8)
|
||||
check_counts(10, 10, 3, 2, 8)
|
||||
|
||||
# Now create an InstallationCount with a property that's not supposed
|
||||
# to be tracked by the remote server - since the bouncer itself tracks
|
||||
|
@ -1349,7 +1353,7 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
|||
)
|
||||
# The analytics endpoint call counts increase by 1, but the actual RemoteCounts remain unchanged,
|
||||
# since syncing the data failed.
|
||||
check_counts(10, 10, 3, 2, 8)
|
||||
check_counts(11, 11, 3, 2, 8)
|
||||
forbidden_installation_count.delete()
|
||||
|
||||
(realm_count_data, installation_count_data, realmauditlog_data) = build_analytics_data(
|
||||
|
@ -1387,7 +1391,7 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
|||
],
|
||||
)
|
||||
# Only the request counts go up -- all of the other rows' duplicates are dropped
|
||||
check_counts(11, 11, 3, 2, 8)
|
||||
check_counts(12, 12, 3, 2, 8)
|
||||
|
||||
# Test that only valid org_type values are accepted - integers defined in OrgTypeEnum.
|
||||
realms_data = get_realms_info_for_push_bouncer()
|
||||
|
@ -1785,28 +1789,30 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
|||
|
||||
@override_settings(PUSH_NOTIFICATION_BOUNCER_URL="https://push.zulip.org.example.com")
|
||||
@responses.activate
|
||||
def test_send_realms_only_to_push_bouncer(self) -> None:
|
||||
def test_realm_properties_after_send_analytics(self) -> None:
|
||||
self.add_mock_response()
|
||||
|
||||
with mock.patch(
|
||||
"zilencer.views.RemoteRealmBillingSession.get_customer", return_value=None
|
||||
) as m:
|
||||
realms = send_realms_only_to_push_bouncer()
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
m.assert_called()
|
||||
for data in realms.values():
|
||||
self.assertEqual(data["can_push"], True)
|
||||
self.assertEqual(data["expected_end_timestamp"], None)
|
||||
realms = Realm.objects.all()
|
||||
for realm in realms:
|
||||
self.assertEqual(realm.push_notifications_enabled, True)
|
||||
self.assertEqual(realm.push_notifications_enabled_end_timestamp, None)
|
||||
|
||||
dummy_customer = mock.MagicMock()
|
||||
with mock.patch(
|
||||
"zilencer.views.RemoteRealmBillingSession.get_customer", return_value=dummy_customer
|
||||
):
|
||||
with mock.patch("zilencer.views.get_current_plan_by_customer", return_value=None) as m:
|
||||
realms = send_realms_only_to_push_bouncer()
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
m.assert_called()
|
||||
for data in realms.values():
|
||||
self.assertEqual(data["can_push"], True)
|
||||
self.assertEqual(data["expected_end_timestamp"], None)
|
||||
realms = Realm.objects.all()
|
||||
for realm in realms:
|
||||
self.assertEqual(realm.push_notifications_enabled, True)
|
||||
self.assertEqual(realm.push_notifications_enabled_end_timestamp, None)
|
||||
|
||||
dummy_customer_plan = mock.MagicMock()
|
||||
dummy_customer_plan.status = CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE
|
||||
|
@ -1820,16 +1826,48 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
|||
with mock.patch(
|
||||
"zilencer.views.RemoteRealmBillingSession.get_next_billing_cycle",
|
||||
return_value=dummy_date,
|
||||
) as m:
|
||||
realms = send_realms_only_to_push_bouncer()
|
||||
) as m, self.assertLogs("zulip.analytics", level="INFO") as info_log:
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
m.assert_called()
|
||||
for data in realms.values():
|
||||
self.assertEqual(data["can_push"], True)
|
||||
realms = Realm.objects.all()
|
||||
for realm in realms:
|
||||
self.assertEqual(realm.push_notifications_enabled, True)
|
||||
self.assertEqual(
|
||||
data["expected_end_timestamp"], datetime_to_timestamp(dummy_date)
|
||||
realm.push_notifications_enabled_end_timestamp,
|
||||
dummy_date,
|
||||
)
|
||||
self.assertIn(
|
||||
"INFO:zulip.analytics:Reported 0 records",
|
||||
info_log.output[0],
|
||||
)
|
||||
|
||||
send_realms_only_to_push_bouncer()
|
||||
with mock.patch("zerver.lib.remote_server.send_to_push_bouncer") as m, self.assertLogs(
|
||||
"zulip.analytics", level="ERROR"
|
||||
) as exception_log:
|
||||
get_response = {
|
||||
"last_realm_count_id": 0,
|
||||
"last_installation_count_id": 0,
|
||||
"last_realmauditlog_id": 0,
|
||||
}
|
||||
|
||||
def mock_send_to_push_bouncer_response(method: str, *args: Any) -> Dict[str, int]:
|
||||
if method == "POST":
|
||||
raise Exception
|
||||
return get_response
|
||||
|
||||
m.side_effect = mock_send_to_push_bouncer_response
|
||||
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
|
||||
realms = Realm.objects.all()
|
||||
for realm in realms:
|
||||
self.assertFalse(realm.push_notifications_enabled)
|
||||
self.assertIn(
|
||||
"ERROR:zulip.analytics:Exception asking push bouncer if notifications will work.",
|
||||
exception_log.output[0],
|
||||
)
|
||||
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
|
||||
self.assertEqual(
|
||||
list(
|
||||
|
@ -1859,38 +1897,6 @@ class AnalyticsBouncerTest(BouncerTestCase):
|
|||
],
|
||||
)
|
||||
|
||||
# Use a mock to assert exactly the data that gets sent.
|
||||
dummy_send_realms_only_response = {
|
||||
"result": "success",
|
||||
"msg": "",
|
||||
"realms": {
|
||||
"f9535515-84d0-489e-80d5-9ae97c3c7ec1": {
|
||||
"can_push": True,
|
||||
"expected_end_timestamp": None,
|
||||
},
|
||||
},
|
||||
}
|
||||
with mock.patch(
|
||||
"zerver.lib.remote_server.send_to_push_bouncer",
|
||||
return_value=dummy_send_realms_only_response,
|
||||
) as mock_send_to_push_bouncer:
|
||||
send_realms_only_to_push_bouncer()
|
||||
|
||||
post_data = {
|
||||
"realm_counts": "[]",
|
||||
"installation_counts": "[]",
|
||||
"realms": orjson.dumps(
|
||||
[dict(realm_data) for realm_data in get_realms_info_for_push_bouncer()]
|
||||
).decode(),
|
||||
"version": orjson.dumps(ZULIP_VERSION).decode(),
|
||||
"api_feature_level": orjson.dumps(API_FEATURE_LEVEL).decode(),
|
||||
}
|
||||
mock_send_to_push_bouncer.assert_called_with(
|
||||
"POST",
|
||||
"server/analytics",
|
||||
post_data,
|
||||
)
|
||||
|
||||
|
||||
class PushNotificationTest(BouncerTestCase):
|
||||
@override
|
||||
|
|
|
@ -1089,18 +1089,30 @@ class RealmTest(ZulipTestCase):
|
|||
}
|
||||
with mock.patch(
|
||||
"zerver.lib.remote_server.send_to_push_bouncer",
|
||||
return_value=dummy_send_realms_only_response,
|
||||
) as m:
|
||||
get_response = {
|
||||
"last_realm_count_id": 0,
|
||||
"last_installation_count_id": 0,
|
||||
"last_realmauditlog_id": 0,
|
||||
}
|
||||
|
||||
def mock_send_to_push_bouncer_response(method: str, *args: Any) -> Dict[str, Any]:
|
||||
if method == "GET":
|
||||
return get_response
|
||||
return dummy_send_realms_only_response
|
||||
|
||||
m.side_effect = mock_send_to_push_bouncer_response
|
||||
|
||||
realm = do_create_realm("realm_string_id", "realm name")
|
||||
|
||||
self.assertEqual(realm.string_id, "realm_string_id")
|
||||
self.assertEqual(m.call_count, 1)
|
||||
self.assertEqual(m.call_count, 2)
|
||||
|
||||
calls_args_for_assert = m.call_args_list[0][0]
|
||||
calls_args_for_assert = m.call_args_list[1][0]
|
||||
self.assertEqual(calls_args_for_assert[0], "POST")
|
||||
self.assertEqual(calls_args_for_assert[1], "server/analytics")
|
||||
self.assertIn(
|
||||
realm.id, [realm["id"] for realm in json.loads(m.call_args_list[0][0][2]["realms"])]
|
||||
realm.id, [realm["id"] for realm in json.loads(m.call_args_list[1][0][2]["realms"])]
|
||||
)
|
||||
|
||||
def test_changing_waiting_period_updates_system_groups(self) -> None:
|
||||
|
|
|
@ -24,7 +24,7 @@ from zerver.lib.push_notifications import (
|
|||
from zerver.lib.remote_server import (
|
||||
UserDataForRemoteBilling,
|
||||
get_realms_info_for_push_bouncer,
|
||||
send_realms_only_to_push_bouncer,
|
||||
send_analytics_to_push_bouncer,
|
||||
send_to_push_bouncer,
|
||||
)
|
||||
from zerver.lib.request import REQ, has_request_variables
|
||||
|
@ -162,7 +162,7 @@ def self_hosting_auth_redirect(
|
|||
result = send_to_push_bouncer("POST", "server/billing", post_data)
|
||||
except MissingRemoteRealmError:
|
||||
# Upload realm info and re-try. It should work now.
|
||||
send_realms_only_to_push_bouncer()
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
result = send_to_push_bouncer("POST", "server/billing", post_data)
|
||||
except RemoteRealmServerMismatchError:
|
||||
return render(request, "zilencer/remote_realm_server_mismatch_error.html", status=403)
|
||||
|
|
|
@ -80,7 +80,7 @@ from zerver.lib.pysa import mark_sanitized
|
|||
from zerver.lib.queue import SimpleQueueClient, retry_event
|
||||
from zerver.lib.remote_server import (
|
||||
PushNotificationBouncerRetryLaterError,
|
||||
send_realms_only_to_push_bouncer,
|
||||
send_analytics_to_push_bouncer,
|
||||
)
|
||||
from zerver.lib.send_email import (
|
||||
EmailNotDeliveredError,
|
||||
|
@ -1174,9 +1174,9 @@ class DeferredWorker(QueueProcessingWorker):
|
|||
# In the future we may use the realm_id to send only that single realm's info.
|
||||
realm_id = event["realm_id"]
|
||||
logger.info(
|
||||
"Running send_realms_only_to_push_bouncer, requested due to realm %s", realm_id
|
||||
"Running send_analytics_to_push_bouncer, requested due to realm %s", realm_id
|
||||
)
|
||||
send_realms_only_to_push_bouncer()
|
||||
send_analytics_to_push_bouncer(consider_usage_statistics=False)
|
||||
|
||||
end = time.time()
|
||||
logger.info(
|
||||
|
|
Loading…
Reference in New Issue