remote_server: Send API feature level along with Zulip version.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2023-12-08 12:38:01 -08:00 committed by Tim Abbott
parent 0400614a48
commit f86becfc94
5 changed files with 35 additions and 7 deletions

View File

@ -10,7 +10,7 @@ from django.utils.translation import gettext as _
from pydantic import UUID4, BaseModel, ConfigDict, Field, Json, field_validator
from analytics.models import InstallationCount, RealmCount
from version import ZULIP_VERSION
from version import API_FEATURE_LEVEL, ZULIP_VERSION
from zerver.lib.exceptions import JsonableError, MissingRemoteRealmError
from zerver.lib.outgoing_http import OutgoingSession
from zerver.lib.queue import queue_json_publish
@ -92,6 +92,7 @@ class AnalyticsRequest(BaseModel):
realmauditlog_rows: Optional[Json[List[RealmAuditLogDataForAnalytics]]] = None
realms: Json[List[RealmDataForAnalytics]]
version: Optional[Json[str]]
api_feature_level: Optional[Json[int]]
class UserDataForRemoteBilling(BaseModel):
@ -335,6 +336,7 @@ def send_analytics_to_push_bouncer() -> None:
realmauditlog_rows=realmauditlog_data,
realms=get_realms_info_for_push_bouncer(),
version=ZULIP_VERSION,
api_feature_level=API_FEATURE_LEVEL,
)
try:
@ -350,6 +352,7 @@ def send_realms_only_to_push_bouncer() -> Dict[str, RemoteRealmDictValue]:
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

View File

@ -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 ZULIP_VERSION
from version import API_FEATURE_LEVEL, 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 (
@ -1358,11 +1358,12 @@ class AnalyticsBouncerTest(BouncerTestCase):
realmauditlog_rows=realmauditlog_data,
realms=[],
version=None,
api_feature_level=None,
)
result = self.uuid_post(
self.server_uuid,
"/api/v1/remotes/server/analytics",
request.model_dump(round_trip=True, exclude={"realms", "version"}),
request.model_dump(round_trip=True, exclude={"realms", "version", "api_feature_level"}),
subdomain="",
)
self.assert_json_error(result, "Data is out of order.")
@ -1396,6 +1397,7 @@ class AnalyticsBouncerTest(BouncerTestCase):
realmauditlog_rows=[],
realms=realms_data,
version=None,
api_feature_level=None,
)
result = self.uuid_post(
self.server_uuid,
@ -1447,11 +1449,12 @@ class AnalyticsBouncerTest(BouncerTestCase):
realmauditlog_rows=realmauditlog_data,
realms=[],
version=None,
api_feature_level=None,
)
result = self.uuid_post(
self.server_uuid,
"/api/v1/remotes/server/analytics",
request.model_dump(round_trip=True, exclude={"version"}),
request.model_dump(round_trip=True, exclude={"version", "api_feature_level"}),
subdomain="",
)
self.assert_json_error(result, "Invalid event type.")
@ -1472,11 +1475,12 @@ class AnalyticsBouncerTest(BouncerTestCase):
realmauditlog_rows=realmauditlog_data,
realms=[],
version=None,
api_feature_level=None,
)
result = self.uuid_post(
self.server_uuid,
"/api/v1/remotes/server/analytics",
request.model_dump(round_trip=True, exclude={"version"}),
request.model_dump(round_trip=True, exclude={"version", "api_feature_level"}),
subdomain="",
)
self.assert_json_success(result)
@ -1876,6 +1880,7 @@ class AnalyticsBouncerTest(BouncerTestCase):
[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",

View File

@ -0,0 +1,17 @@
# Generated by Django 4.2.8 on 2023-12-09 18:08
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("zilencer", "0047_preregistrationremoteserverbillinguser_and_more"),
]
operations = [
migrations.AddField(
model_name="remotezulipserver",
name="last_api_feature_level",
field=models.PositiveIntegerField(null=True),
),
]

View File

@ -48,6 +48,7 @@ class RemoteZulipServer(models.Model):
contact_email = models.EmailField(blank=True, null=False)
last_updated = models.DateTimeField("last updated", auto_now=True)
last_version = models.CharField(max_length=VERSION_MAX_LENGTH, null=True)
last_api_feature_level = models.PositiveIntegerField(null=True)
# Whether the server registration has been deactivated.
deactivated = models.BooleanField(default=False)

View File

@ -675,6 +675,7 @@ def remote_server_post_analytics(
realmauditlog_rows: Optional[Json[List[RealmAuditLogDataForAnalytics]]] = None,
realms: Optional[Json[List[RealmDataForAnalytics]]] = None,
version: Optional[Json[str]] = None,
api_feature_level: Optional[Json[int]] = None,
) -> HttpResponse:
# Lock the server, preventing this from racing with other
# duplicate submissions of the data
@ -683,9 +684,10 @@ def remote_server_post_analytics(
remote_server_version_updated = False
if version is not None:
version = version[0 : RemoteZulipServer.VERSION_MAX_LENGTH]
if version != server.last_version:
if version != server.last_version or api_feature_level != server.last_api_feature_level:
server.last_version = version
server.save(update_fields=["last_version"])
server.last_api_feature_level = api_feature_level
server.save(update_fields=["last_version", "last_api_feature_level"])
remote_server_version_updated = True
validate_incoming_table_data(