44from rest_framework .serializers import ModelSerializer , Serializer , ValidationError
55from rest_framework .viewsets import ModelViewSet
66
7- from ..fields import UNSIGNED_64BIT_INT_MAX_VALUE , hex_re
8- from ..models import APNSDevice , GCMDevice , WebPushDevice , WNSDevice
9- from ..settings import PUSH_NOTIFICATIONS_SETTINGS as SETTINGS
7+ from push_notifications .fields import UNSIGNED_64BIT_INT_MAX_VALUE , hex_re
8+ from push_notifications .models import APNSDevice , GCMDevice , WebPushDevice , WNSDevice
9+ from push_notifications .settings import PUSH_NOTIFICATIONS_SETTINGS as SETTINGS
10+ from typing import Any , Union , Dict , Optional
1011
1112
1213# Fields
@@ -15,25 +16,30 @@ class HexIntegerField(IntegerField):
1516 Store an integer represented as a hex string of form "0x01".
1617 """
1718
18- def to_internal_value (self , data ) :
19+ def to_internal_value (self , data : Union [ str , int ]) -> int :
1920 # validate hex string and convert it to the unsigned
2021 # integer representation for internal use
2122 try :
22- data = int (data , 16 ) if type (data ) != int else data
23+ data = int (data , 16 ) if not isinstance (data , int ) else data
2324 except ValueError :
2425 raise ValidationError ("Device ID is not a valid hex number" )
2526 return super ().to_internal_value (data )
2627
27- def to_representation (self , value ) :
28+ def to_representation (self , value : int ) -> int :
2829 return value
2930
3031
3132# Serializers
3233class DeviceSerializerMixin (ModelSerializer ):
3334 class Meta :
3435 fields = (
35- "id" , "name" , "application_id" , "registration_id" , "device_id" ,
36- "active" , "date_created"
36+ "id" ,
37+ "name" ,
38+ "application_id" ,
39+ "registration_id" ,
40+ "device_id" ,
41+ "active" ,
42+ "date_created" ,
3743 )
3844 read_only_fields = ("date_created" ,)
3945
@@ -45,8 +51,7 @@ class APNSDeviceSerializer(ModelSerializer):
4551 class Meta (DeviceSerializerMixin .Meta ):
4652 model = APNSDevice
4753
48- def validate_registration_id (self , value ):
49-
54+ def validate_registration_id (self , value : str ) -> str :
5055 # https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622958-application
5156 # As of 02/2023 APNS tokens (registration_id) "are of variable length. Do not hard-code their size."
5257 if hex_re .match (value ) is None :
@@ -56,10 +61,10 @@ def validate_registration_id(self, value):
5661
5762
5863class UniqueRegistrationSerializerMixin (Serializer ):
59- def validate (self , attrs ) :
60- devices = None
61- primary_key = None
62- request_method = None
64+ def validate (self , attrs : Dict [ str , Any ]) -> Dict [ str , Any ] :
65+ devices : Optional [ Any ] = None
66+ primary_key : Optional [ Any ] = None
67+ request_method : Optional [ str ] = None
6368
6469 if self .initial_data .get ("registration_id" , None ):
6570 if self .instance :
@@ -76,9 +81,10 @@ def validate(self, attrs):
7681
7782 Device = self .Meta .model
7883 if request_method == "update" :
79- reg_id = attrs .get ("registration_id" , self .instance .registration_id )
80- devices = Device .objects .filter (registration_id = reg_id ) \
81- .exclude (id = primary_key )
84+ reg_id : str = attrs .get ("registration_id" , self .instance .registration_id )
85+ devices = Device .objects .filter (registration_id = reg_id ).exclude (
86+ id = primary_key
87+ )
8288 elif request_method == "create" :
8389 devices = Device .objects .filter (registration_id = attrs ["registration_id" ])
8490
@@ -92,20 +98,26 @@ class GCMDeviceSerializer(UniqueRegistrationSerializerMixin, ModelSerializer):
9298 help_text = "ANDROID_ID / TelephonyManager.getDeviceId() (e.g: 0x01)" ,
9399 style = {"input_type" : "text" },
94100 required = False ,
95- allow_null = True
101+ allow_null = True ,
96102 )
97103
98104 class Meta (DeviceSerializerMixin .Meta ):
99105 model = GCMDevice
100106 fields = (
101- "id" , "name" , "registration_id" , "device_id" , "active" , "date_created" ,
102- "cloud_message_type" , "application_id" ,
107+ "id" ,
108+ "name" ,
109+ "registration_id" ,
110+ "device_id" ,
111+ "active" ,
112+ "date_created" ,
113+ "cloud_message_type" ,
114+ "application_id" ,
103115 )
104116 extra_kwargs = {"id" : {"read_only" : False , "required" : False }}
105117
106- def validate_device_id (self , value ) :
118+ def validate_device_id (self , value : Optional [ int ] = None ) -> Optional [ int ] :
107119 # device ids are 64 bit unsigned values
108- if value > UNSIGNED_64BIT_INT_MAX_VALUE :
120+ if value is not None and value > UNSIGNED_64BIT_INT_MAX_VALUE :
109121 raise ValidationError ("Device ID is out of range" )
110122 return value
111123
@@ -119,26 +131,36 @@ class WebPushDeviceSerializer(UniqueRegistrationSerializerMixin, ModelSerializer
119131 class Meta (DeviceSerializerMixin .Meta ):
120132 model = WebPushDevice
121133 fields = (
122- "id" , "name" , "registration_id" , "active" , "date_created" ,
123- "p256dh" , "auth" , "browser" , "application_id" ,
134+ "id" ,
135+ "name" ,
136+ "registration_id" ,
137+ "active" ,
138+ "date_created" ,
139+ "p256dh" ,
140+ "auth" ,
141+ "browser" ,
142+ "application_id" ,
124143 )
125144
126145
127146# Permissions
128147class IsOwner (permissions .BasePermission ):
129- def has_object_permission (self , request , view , obj ) :
148+ def has_object_permission (self , request : Any , view : Any , obj : Any ) -> bool :
130149 # must be the owner to view the object
131150 return obj .user == request .user
132151
133152
134153# Mixins
135154class DeviceViewSetMixin :
136- lookup_field = "registration_id"
137-
138- def create (self , request , * args , ** kwargs ):
139- serializer = None
140- is_update = False
141- if SETTINGS .get ("UPDATE_ON_DUPLICATE_REG_ID" ) and self .lookup_field in request .data :
155+ lookup_field : str = "registration_id"
156+
157+ def create (self , request : Any , * args : Any , ** kwargs : Any ) -> Response :
158+ serializer : Optional [Any ] = None
159+ is_update : bool = False
160+ if (
161+ SETTINGS .get ("UPDATE_ON_DUPLICATE_REG_ID" )
162+ and self .lookup_field in request .data
163+ ):
142164 instance = self .queryset .model .objects .filter (
143165 registration_id = request .data [self .lookup_field ]
144166 ).first ()
@@ -155,23 +177,25 @@ def create(self, request, *args, **kwargs):
155177 else :
156178 self .perform_create (serializer )
157179 headers = self .get_success_headers (serializer .data )
158- return Response (serializer .data , status = status .HTTP_201_CREATED , headers = headers )
180+ return Response (
181+ serializer .data , status = status .HTTP_201_CREATED , headers = headers
182+ )
159183
160- def perform_create (self , serializer ) :
184+ def perform_create (self , serializer : Serializer ) -> Any :
161185 if self .request .user .is_authenticated :
162186 serializer .save (user = self .request .user )
163187 return super ().perform_create (serializer )
164188
165- def perform_update (self , serializer ) :
189+ def perform_update (self , serializer : Serializer ) -> Any :
166190 if self .request .user .is_authenticated :
167191 serializer .save (user = self .request .user )
168192 return super ().perform_update (serializer )
169193
170194
171195class AuthorizedMixin :
172- permission_classes = (permissions .IsAuthenticated , IsOwner )
196+ permission_classes : tuple = (permissions .IsAuthenticated , IsOwner )
173197
174- def get_queryset (self ):
198+ def get_queryset (self ) -> Any :
175199 # filter all devices to only those belonging to the current user
176200 return self .queryset .filter (user = self .request .user )
177201
@@ -207,7 +231,7 @@ class WNSDeviceAuthorizedViewSet(AuthorizedMixin, WNSDeviceViewSet):
207231class WebPushDeviceViewSet (DeviceViewSetMixin , ModelViewSet ):
208232 queryset = WebPushDevice .objects .all ()
209233 serializer_class = WebPushDeviceSerializer
210- lookup_value_regex = '.+'
234+ lookup_value_regex : str = ".+"
211235
212236
213237class WebPushDeviceAuthorizedViewSet (AuthorizedMixin , WebPushDeviceViewSet ):
0 commit comments