Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions api/auth/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,10 @@ def validate(self, attrs):
data["refresh"] = str(refresh)
data["access"] = str(refresh.access_token)

self.device.is_logged_in = True
_update_fields=["is_logged_in"]
self.device.active_session = True
# NOTE: adding 'active' in the update_fields is important.
# The save() method will trigger active = False depending on the value of active_session.
_update_fields=["active_session", "active"]
# Update last_login device
if api_settings.UPDATE_LAST_LOGIN:
self.device.last_login = timezone.now()
Expand Down
1 change: 1 addition & 0 deletions api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1463,6 +1463,7 @@ def validate(self, data):

return data

@transaction.atomic
def create(self, validated_data):
# Extract the user and model from the data
user = validated_data.get('user')
Expand Down
14 changes: 9 additions & 5 deletions api/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -539,8 +539,9 @@ def test_device_is_set_to_logged_in_on_login_if_not_logged(self, app_user, user_
user=app_user,
device_id='unique_id_for_device',
registration_id='fcm_token',
is_logged_in=False
active_session=False
)
assert not device.active
response = client.post(
self.endpoint,
data={
Expand All @@ -552,16 +553,18 @@ def test_device_is_set_to_logged_in_on_login_if_not_logged(self, app_user, user_
assert response.status_code == status.HTTP_200_OK

device.refresh_from_db()
assert device.is_logged_in
assert device.active_session
assert device.active

def test_device_is_set_to_not_logged_in_if_login_with_duplicated_device_id(self, app_user, user_password, client):
dummy_user = TigaUser.objects.create()
device = Device.objects.create(
user=dummy_user,
device_id='unique_id_for_device',
registration_id='fcm_token',
is_logged_in=True
active_session=True
)
assert device.active
# Login with same device_id but different user.
response = client.post(
self.endpoint,
Expand All @@ -574,7 +577,8 @@ def test_device_is_set_to_not_logged_in_if_login_with_duplicated_device_id(self,
assert response.status_code == status.HTTP_200_OK

device.refresh_from_db()
assert not device.is_logged_in
assert not device.active_session
assert not device.active

@pytest.mark.parametrize(
"token_field",
Expand Down Expand Up @@ -654,7 +658,7 @@ def test_inactive_device_is_set_to_active_on_registration_id_change(self, app_us
os_name='android',
os_version='32',
active=False,
is_logged_in=True
active_session=True
)
response = app_api_client.patch(
self.endpoint + f"{device.device_id}/",
Expand Down
4 changes: 2 additions & 2 deletions tigaserver_app/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ class MobileAppAdmin(admin.ModelAdmin):
class DeviceInline(admin.StackedInline):
model = Device
fields = (
('device_id', 'is_logged_in', 'last_login'),
('device_id', 'active_session', 'last_login'),
('registration_id', 'active'),
'type',
'mobile_app',
Expand All @@ -81,7 +81,7 @@ class DeviceInline(admin.StackedInline):
readonly_fields = (
'date_created',
'updated_at',
'is_logged_in',
'active_session',
'last_login'
)
extra = 0
Expand Down
2 changes: 1 addition & 1 deletion tigaserver_app/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ class DeviceQuerySet(FCMDeviceQuerySet):
def deactivate_devices_with_error_results(self, *args, **kwargs):
deactivated_ids = super().deactivate_devices_with_error_results(*args, **kwargs)

self.filter(registration_id__in=deactivated_ids).update(is_logged_in=False)
self.filter(registration_id__in=deactivated_ids).update(active_session=False)

return deactivated_ids

Expand Down
26 changes: 26 additions & 0 deletions tigaserver_app/migrations/0085_auto_20250822_1214.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 3.2.25 on 2025-08-22 12:14

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('tigaserver_app', '0084_alter_mobileapp_package_version'),
]

operations = [
migrations.RemoveConstraint(
model_name='device',
name='unique_is_logged_in_device_id',
),
migrations.RenameField(
model_name='device',
old_name='is_logged_in',
new_name='active_session',
),
migrations.AddConstraint(
model_name='device',
constraint=models.UniqueConstraint(condition=models.Q(('active_session', True), ('device_id__isnull', False), models.Q(('device_id__exact', ''), _negated=True)), fields=('active_session', 'device_id'), name='unique_active_session_device_id'),
),
]
32 changes: 14 additions & 18 deletions tigaserver_app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,9 +364,9 @@ def __str__(self):

class Device(AbstractFCMDevice):
# NOTE: self.active : If the FCM TOKEN is active
# self.is_logged_in : If the Device is is_logged_in for the user
# self.active_session : If the Device has and active logged session for the user

# NOTE: if ever work on a logout method, set is_logged_in/active to False on logout.
# NOTE: if ever work on a logout method, set active_session/active to False on logout.
# Override user to make FK to TigaUser instead of User
user = models.ForeignKey(
TigaUser,
Expand All @@ -376,7 +376,7 @@ class Device(AbstractFCMDevice):
)

mobile_app = models.ForeignKey(MobileApp, null=True, on_delete=models.PROTECT)
is_logged_in = models.BooleanField(default=False)
active_session = models.BooleanField(default=False)

registration_id = models.TextField(null=True, db_index=True, verbose_name='Registration token')

Expand Down Expand Up @@ -422,7 +422,7 @@ class Device(AbstractFCMDevice):
'name',
'date_created',
'updated_at',
'is_logged_in',
'active_session',
'last_login',
'user'
],
Expand Down Expand Up @@ -462,12 +462,7 @@ def save(self, *args, **kwargs):
if self.os_locale:
self.os_locale = standarize_language_tag(self.os_locale)

_fields_with_changes = self.__get_changed_fields(update_fields=kwargs.get('update_fields'))
if self.registration_id and 'registration_id' in _fields_with_changes:
self.active = True

if not self.registration_id or not self.is_logged_in:
self.active = False
self.active = bool(self.registration_id and self.active_session)

if self.active and self.registration_id:
update_device_qs = Device.objects.filter(active=True, registration_id=self.registration_id)
Expand All @@ -480,19 +475,20 @@ def save(self, *args, **kwargs):
device._change_reason = 'Another user has created/update a device with the same registration_id'
device.save()

if self.is_logged_in and self.device_id:
update_device_qs = Device.objects.filter(is_logged_in=True, device_id=self.device_id)
if self.active_session and self.device_id:
update_device_qs = Device.objects.filter(active_session=True, device_id=self.device_id)
if self.pk:
update_device_qs = update_device_qs.exclude(pk=self.pk)

for device in update_device_qs.iterator():
device.is_logged_in = False
device.active_session = False
# For simple history
device._change_reason = 'Another user has created/update a device with the same device_id'
device.save()

if self.pk:
_tracked_fields = [field.name for field in self.__class__.history.model._meta.get_fields()]
_fields_with_changes = self.__get_changed_fields(update_fields=kwargs.get('update_fields'))
if not any(element in _tracked_fields for element in _fields_with_changes):
# Only will create history if at least one tracked field has changed.
self.skip_history_when_saving = True
Expand Down Expand Up @@ -524,9 +520,9 @@ class Meta(AbstractFCMDevice.Meta):
condition=models.Q(active=True, registration_id__isnull=False) & ~models.Q(registration_id__exact=''),
),
models.UniqueConstraint(
fields=['is_logged_in', 'device_id'],
name='unique_is_logged_in_device_id',
condition=models.Q(is_logged_in=True, device_id__isnull=False) & ~models.Q(device_id__exact=''),
fields=['active_session', 'device_id'],
name='unique_active_session_device_id',
condition=models.Q(active_session=True, device_id__isnull=False) & ~models.Q(device_id__exact=''),
),
models.UniqueConstraint(
fields=['user', 'device_id'],
Expand Down Expand Up @@ -1995,7 +1991,7 @@ def save(self, *args, **kwargs):
).select_for_update().order_by('-last_login').first() or Device.objects.filter(
user=self.user,
model__isnull=True,
is_logged_in=True,
active_session=True,
pk__in=models.Subquery(
Device.objects.filter(
user=models.OuterRef('user'),
Expand Down Expand Up @@ -2035,7 +2031,7 @@ def save(self, *args, **kwargs):
device.os_version = self.os_version
device.os_locale = self.os_language
device.mobile_app = self.mobile_app
device.is_logged_in = True
device.active_session = True
device.active = True
device.last_login = timezone.now()

Expand Down
26 changes: 13 additions & 13 deletions tigaserver_app/tests/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ def setUp(self):
Device.objects.create(
user=t,
active=True,
is_logged_in=True,
active_session=True,
registration_id="caM8sSvLQKmX4Iai1xGb9w:APA91bGhzu3DYeYLTh-M9elzrhK492V0J3wDrsFsUDaw13v3Wxzb_9YbemsnMTb3N7_GilKwtS73NtbywSloNRo2alfpIMu29FKszZYr6WxoNdGao6PGNRf4kS1tKCiEAZgFvMkdLkgT"
)

Expand Down Expand Up @@ -615,7 +615,7 @@ def test_device_is_created_if_not_exist_on_new_report(self):
self.assertEqual(device.os_version, "testVersion")
self.assertEqual(device.os_locale, "es-ES")
self.assertEqual(device.mobile_app, report.mobile_app)
self.assertEqual(device.is_logged_in, True)
self.assertEqual(device.active_session, True)
self.assertEqual(device.last_login, timezone.now())

self.assertIsNone(device.registration_id)
Expand All @@ -631,7 +631,7 @@ def test_device_with_model_null_is_updated_on_new_report(self):
user=user,
model=None,
active=True,
is_logged_in=True,
active_session=True,
last_login=timezone.now()-timedelta(days=1)
)
self.assertIsNone(device.model)
Expand Down Expand Up @@ -663,7 +663,7 @@ def test_device_with_model_null_is_updated_on_new_report(self):
self.assertEqual(device.os_version, "testVersion")
self.assertEqual(device.os_locale, "es-ES")
self.assertEqual(device.mobile_app, mobile_app)
self.assertTrue(device.is_logged_in)
self.assertTrue(device.active_session)
self.assertTrue(device.active)
self.assertEqual(device.last_login, timezone.now())
self.assertEqual(device.registration_id, fcm_token)
Expand All @@ -676,7 +676,7 @@ def test_device_with_model_is_updated_on_new_report(self):
user=user,
model="test_model",
active=True,
is_logged_in=True,
active_session=True,
last_login=timezone.now()-timedelta(days=1)
)
self.assertIsNone(device.type)
Expand Down Expand Up @@ -708,7 +708,7 @@ def test_device_with_model_is_updated_on_new_report(self):
self.assertEqual(device.os_version, "testVersion")
self.assertEqual(device.os_locale, "es-ES")
self.assertEqual(device.mobile_app, mobile_app)
self.assertEqual(device.is_logged_in, True)
self.assertEqual(device.active_session, True)
self.assertEqual(device.last_login, timezone.now())

def test_POST_with_not_valid_version_number_raise_400(self):
Expand Down Expand Up @@ -977,7 +977,7 @@ def setUp(self):
Device.objects.create(
user=t,
active=True,
is_logged_in=True,
active_session=True,
registration_id='caM8sSvLQKmX4Iai1xGb9w:APA91bGhzu3DYeYLTh-M9elzrhK492V0J3wDrsFsUDaw13v3Wxzb_9YbemsnMTb3N7_GilKwtS73NtbywSloNRo2alfpIMu29FKszZYr6WxoNdGao6PGNRf4kS1tKCiEAZgFvMkdLkgT'
)

Expand Down Expand Up @@ -1943,7 +1943,7 @@ def test_device_is_updated_if_previous_model_exist_and_new_model_None_also(self)
model="test_model",
registration_id="fcm_token",
active=True,
is_logged_in=True,
active_session=True,
last_login=timezone.now() - timedelta(days=1)
)
_ = Report.objects.create(
Expand Down Expand Up @@ -1975,7 +1975,7 @@ def test_device_is_updated_if_previous_model_exist_and_new_model_None_also(self)
model=None,
registration_id="new_fcm_token",
active=True,
is_logged_in=True,
active_session=True,
last_login=timezone.now() - timedelta(minutes=1)
)

Expand All @@ -1984,7 +1984,7 @@ def test_device_is_updated_if_previous_model_exist_and_new_model_None_also(self)
model=None,
registration_id="new_fcm_token2",
active=True,
is_logged_in=True,
active_session=True,
last_login=timezone.now()
)

Expand Down Expand Up @@ -2376,7 +2376,7 @@ def test_post_fcm_token_creates_new_device_if_no_device_exist(self):
registration_id=fcm_token
)
self.assertTrue(device.active)
self.assertTrue(device.is_logged_in)
self.assertTrue(device.active_session)
self.assertEqual(device.last_login, timezone.now())
self.assertIsNone(device.model)

Expand All @@ -2390,7 +2390,7 @@ def test_post_fcm_token_updates_device_with_same_registration_id(self):
user=self.tiga_user,
registration_id=fcm_token,
active=True,
is_logged_in=True,
active_session=True,
last_login=timezone.now() - timedelta(days=1)
)

Expand All @@ -2411,7 +2411,7 @@ def test_post_fcm_token_updates_device_with_same_registration_id(self):

self.assertEqual(device.registration_id, fcm_token)
self.assertTrue(device.active)
self.assertTrue(device.is_logged_in)
self.assertTrue(device.active_session)
self.assertEqual(device.last_login, timezone.now())


Expand Down
2 changes: 1 addition & 1 deletion tigaserver_app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -1051,7 +1051,7 @@ def token(request):
registration_id=token,
defaults={
'active': True,
'is_logged_in': True,
'active_session': True,
'last_login': timezone.now()
}
)
Expand Down