mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	zilencer: Make BaseRemoteCount.remote_id field nullable.
This commit is contained in:
		
				
					committed by
					
						
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							c4fbb6319b
						
					
				
				
					commit
					2ecd7abc0d
				
			@@ -74,6 +74,8 @@ from zerver.models import (
 | 
			
		||||
    get_user,
 | 
			
		||||
    is_cross_realm_bot_email,
 | 
			
		||||
)
 | 
			
		||||
from zilencer.models import RemoteInstallationCount, RemoteZulipServer
 | 
			
		||||
from zilencer.views import get_last_id_from_server
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AnalyticsTestCase(ZulipTestCase):
 | 
			
		||||
@@ -1852,3 +1854,26 @@ class TestRealmActiveHumans(AnalyticsTestCase):
 | 
			
		||||
            1,
 | 
			
		||||
        )
 | 
			
		||||
        self.assertEqual(RealmCount.objects.filter(property="realm_active_humans::day").count(), 1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GetLastIdFromServerTest(ZulipTestCase):
 | 
			
		||||
    def test_get_last_id_from_server_ignores_null(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        Verifies that get_last_id_from_server ignores null remote_ids, since this goes
 | 
			
		||||
        against the default Postgres ordering behavior, which treats nulls as the largest value.
 | 
			
		||||
        """
 | 
			
		||||
        self.server_uuid = "6cde5f7a-1f7e-4978-9716-49f69ebfc9fe"
 | 
			
		||||
        self.server = RemoteZulipServer.objects.create(
 | 
			
		||||
            uuid=self.server_uuid,
 | 
			
		||||
            api_key="magic_secret_api_key",
 | 
			
		||||
            hostname="demo.example.com",
 | 
			
		||||
            last_updated=timezone_now(),
 | 
			
		||||
        )
 | 
			
		||||
        first = RemoteInstallationCount.objects.create(
 | 
			
		||||
            end_time=timezone_now(), server=self.server, property="test", value=1, remote_id=1
 | 
			
		||||
        )
 | 
			
		||||
        RemoteInstallationCount.objects.create(
 | 
			
		||||
            end_time=timezone_now(), server=self.server, property="test2", value=1, remote_id=None
 | 
			
		||||
        )
 | 
			
		||||
        result = get_last_id_from_server(self.server, RemoteInstallationCount)
 | 
			
		||||
        self.assertEqual(result, first.remote_id)
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,22 @@
 | 
			
		||||
# Generated by Django 4.2.6 on 2023-10-23 20:22
 | 
			
		||||
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        ("zilencer", "0030_alter_remoteinstallationcount_remote_id"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="remoteinstallationcount",
 | 
			
		||||
            name="remote_id",
 | 
			
		||||
            field=models.IntegerField(null=True),
 | 
			
		||||
        ),
 | 
			
		||||
        migrations.AlterField(
 | 
			
		||||
            model_name="remoterealmcount",
 | 
			
		||||
            name="remote_id",
 | 
			
		||||
            field=models.IntegerField(null=True),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
@@ -135,7 +135,7 @@ class BaseRemoteCount(BaseCount):
 | 
			
		||||
    # The remote_id field is the id value of the corresponding *Count object
 | 
			
		||||
    # on the remote server.
 | 
			
		||||
    # It lets us deduplicate data from the remote server.
 | 
			
		||||
    remote_id = models.IntegerField()
 | 
			
		||||
    remote_id = models.IntegerField(null=True)
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 
 | 
			
		||||
@@ -444,6 +444,10 @@ def validate_incoming_table_data(
 | 
			
		||||
    for row in rows:
 | 
			
		||||
        if is_count_stat and row["property"] not in COUNT_STATS:
 | 
			
		||||
            raise JsonableError(_("Invalid property {property}").format(property=row["property"]))
 | 
			
		||||
        if row.get("id") is None:
 | 
			
		||||
            # This shouldn't be possible, as submitting data like this should be
 | 
			
		||||
            # prevented by our param validators.
 | 
			
		||||
            raise AssertionError(f"Missing id field in row {row}")
 | 
			
		||||
        if row["id"] <= last_id:
 | 
			
		||||
            raise JsonableError(_("Data is out of order."))
 | 
			
		||||
        last_id = row["id"]
 | 
			
		||||
@@ -592,7 +596,15 @@ def remote_server_post_analytics(
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_last_id_from_server(server: RemoteZulipServer, model: Any) -> int:
 | 
			
		||||
    last_count = model.objects.filter(server=server).order_by("remote_id").only("remote_id").last()
 | 
			
		||||
    last_count = (
 | 
			
		||||
        model.objects.filter(server=server)
 | 
			
		||||
        # Rows with remote_id=None are managed by the bouncer service itself,
 | 
			
		||||
        # and thus aren't meant for syncing and should be ignored here.
 | 
			
		||||
        .exclude(remote_id=None)
 | 
			
		||||
        .order_by("remote_id")
 | 
			
		||||
        .only("remote_id")
 | 
			
		||||
        .last()
 | 
			
		||||
    )
 | 
			
		||||
    if last_count is not None:
 | 
			
		||||
        return last_count.remote_id
 | 
			
		||||
    return 0
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user