FIX(account): fix duplicated invite code
fix duplicated invite code
This commit is contained in:
parent
f6099af0e4
commit
0973d08637
4 changed files with 66 additions and 12 deletions
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.1.4 on 2026-07-25
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0009_inviterequest'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='inviterequest',
|
||||
name='mobile_number',
|
||||
field=models.CharField(db_index=True, max_length=30, unique=True, verbose_name='mobile number'),
|
||||
),
|
||||
]
|
||||
|
|
@ -278,7 +278,7 @@ class InviteRequest(BaseModel):
|
|||
related_name='invite_requests',
|
||||
verbose_name=_('inviter'),
|
||||
)
|
||||
mobile_number = models.CharField(_('mobile number'), max_length=30, db_index=True)
|
||||
mobile_number = models.CharField(_('mobile number'), unique=True, max_length=30, db_index=True)
|
||||
application = models.ForeignKey(
|
||||
oauth2_settings.APPLICATION_MODEL,
|
||||
on_delete=models.PROTECT,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from django.utils import timezone
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from unidecode import unidecode
|
||||
|
||||
from utils.exceptions import UnprocessableEntity
|
||||
from utils.exceptions import UnprocessableEntity, Conflict
|
||||
|
||||
phone_number_validator = RegexValidator(regex=r'^\+98[1-9]\d{9}$', message=_(
|
||||
"Phone number must be entered in the format: '+989999999999'."
|
||||
|
|
@ -150,6 +150,19 @@ class InviteRequestSerializer(serializers.ModelSerializer):
|
|||
def validate_mobile_number(self, value):
|
||||
value = unidecode(value)
|
||||
phone_number_validator(value)
|
||||
|
||||
if User.objects.filter(phone_number=value).exists():
|
||||
raise Conflict(
|
||||
detail=_('This mobile number is already registered as a user.'),
|
||||
code='user_already_exists',
|
||||
)
|
||||
|
||||
if InviteRequest.objects.filter(mobile_number=value).exists():
|
||||
raise Conflict(
|
||||
detail=_('An invite request for this mobile number already exists.'),
|
||||
code='invite_request_already_exists',
|
||||
)
|
||||
|
||||
return value
|
||||
|
||||
def create(self, validated_data):
|
||||
|
|
|
|||
|
|
@ -153,9 +153,11 @@ class InviteRequestTests(APITestCase):
|
|||
user_uuid = uuid.UUID('c14e8b86-8f4a-44d9-b29d-badceb47005f')
|
||||
inviter_uuid = uuid.UUID('d14e8b86-8f4a-44d9-b29d-badceb47005f')
|
||||
application_uuid = uuid.UUID('e14e8b86-8f4a-44d9-b29d-badceb47005f')
|
||||
access_token = 'inviteAccessToken123456789012345678901'
|
||||
client_id = 'inviteClientId1234567890123456789012'
|
||||
client_secret = 'inviteClientSecret123456789012345678'
|
||||
mobile_number = '+989120000000'
|
||||
expire_datetime = timezone.now() + timedelta(seconds=3600)
|
||||
|
||||
client = APIClient()
|
||||
|
||||
|
|
@ -177,9 +179,13 @@ class InviteRequestTests(APITestCase):
|
|||
allowed_scope='accounts.invite:submit',
|
||||
)
|
||||
|
||||
def basic_auth_header(self):
|
||||
credentials = f'{self.client_id}:{self.client_secret}'.encode('utf-8')
|
||||
return 'Basic ' + base64.b64encode(credentials).decode('utf-8')
|
||||
AccessToken.objects.create(
|
||||
token=self.access_token,
|
||||
application_id=self.application_uuid,
|
||||
scope='accounts.invite:submit',
|
||||
expires=self.expire_datetime,
|
||||
)
|
||||
self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {self.access_token}')
|
||||
|
||||
def test_create_invite_request_success(self):
|
||||
response = self.client.post(
|
||||
|
|
@ -189,7 +195,6 @@ class InviteRequestTests(APITestCase):
|
|||
'mobile_number': self.mobile_number,
|
||||
},
|
||||
format='json',
|
||||
HTTP_AUTHORIZATION=self.basic_auth_header(),
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 201)
|
||||
|
|
@ -208,13 +213,32 @@ class InviteRequestTests(APITestCase):
|
|||
'mobile_number': self.mobile_number,
|
||||
},
|
||||
format='json',
|
||||
HTTP_AUTHORIZATION=self.basic_auth_header(),
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(InviteRequest.objects.count(), 0)
|
||||
|
||||
def test_create_invite_request_invalid_client_credentials(self):
|
||||
def test_create_invite_request_existing_user(self):
|
||||
response = self.client.post(
|
||||
reverse('users:invite_request_api'),
|
||||
data={
|
||||
'invite_code': str(self.inviter_uuid),
|
||||
'mobile_number': self.inviter.phone_number,
|
||||
},
|
||||
format='json',
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 409)
|
||||
self.assertEqual(response.json()['details']['error'], 'user_already_exists')
|
||||
self.assertEqual(InviteRequest.objects.count(), 0)
|
||||
|
||||
def test_create_invite_request_duplicate_mobile_number(self):
|
||||
InviteRequest.objects.create(
|
||||
inviter=self.inviter,
|
||||
mobile_number=self.mobile_number,
|
||||
application=self.application,
|
||||
)
|
||||
|
||||
response = self.client.post(
|
||||
reverse('users:invite_request_api'),
|
||||
data={
|
||||
|
|
@ -222,11 +246,11 @@ class InviteRequestTests(APITestCase):
|
|||
'mobile_number': self.mobile_number,
|
||||
},
|
||||
format='json',
|
||||
HTTP_AUTHORIZATION='Basic ' + base64.b64encode(b'wrong:credentials').decode('utf-8'),
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 401)
|
||||
self.assertEqual(InviteRequest.objects.count(), 0)
|
||||
self.assertEqual(response.status_code, 409)
|
||||
self.assertEqual(response.json()['details']['error'], 'invite_request_already_exists')
|
||||
self.assertEqual(InviteRequest.objects.count(), 1)
|
||||
|
||||
def test_create_invite_request_missing_scope(self):
|
||||
self.application.allowed_scope = ''
|
||||
|
|
@ -239,7 +263,6 @@ class InviteRequestTests(APITestCase):
|
|||
'mobile_number': self.mobile_number,
|
||||
},
|
||||
format='json',
|
||||
HTTP_AUTHORIZATION=self.basic_auth_header(),
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue