33import typing
44
55from firebase_admin .db import Reference as FbReference
6+ from pydantic import BaseModel , ConfigDict
67
78from apps .common .models import FirebasePushResource , FirebasePushStatusEnum
89from main .celery import app
1415class InvalidObjectPushException (Exception ): ...
1516
1617
17- class FirebasePush [T : FirebasePushResource ](abc .ABC ):
18- model : type [T ]
18+ # FIXME(tnagorra): Add inheritance_checks for model and firebase_model
19+ class FirebasePush [T : FirebasePushResource , K : BaseModel ](abc .ABC ):
20+ model_class : type [T ]
21+ firebase_model_class : type [K ]
1922
2023 def __init__ (
2124 self ,
2225 obj_id : int ,
2326 ):
2427 self .obj_id = obj_id
2528
29+ @classmethod
30+ def _inheritance_checks (cls ):
31+ # FIXME(tnagorra): Find a better way to skip for base classes
32+ if cls .__name__ == "FirebasePush" :
33+ # Skip check for the abstract class
34+ return
35+
36+ missing_fields = []
37+ for attr_name in [
38+ "model_class" ,
39+ "firebase_model_class" ,
40+ ]:
41+ if getattr (cls , attr_name , None ) is None :
42+ missing_fields .append (attr_name )
43+
44+ if missing_fields :
45+ raise NotImplementedError (f"Please define { ',' .join (missing_fields )} for { cls } " )
46+
47+ def __init_subclass__ (cls , ** kwargs ):
48+ super ().__init_subclass__ (** kwargs )
49+ cls ._inheritance_checks ()
50+
2651 @abc .abstractmethod
2752 def handle_new_object_on_firebase (self , model_obj : T , fb_reference : FbReference ): ...
2853
2954 @abc .abstractmethod
30- def handle_object_update_on_firebase (self , model_obj : T , fb_reference : FbReference ): ...
55+ def handle_object_update_on_firebase (self , model_obj : T , fb_obj : K , fb_reference : FbReference ): ...
3156
3257 @abc .abstractmethod
3358 def get_firebase_path (self , firebase_id : str , model : type [T ]) -> str : ...
3459
3560 def push (self ) -> None :
36- model_obj = self .model .objects .get (id = self .obj_id )
61+ model_obj = self .model_class .objects .get (id = self .obj_id )
62+
63+ # FIXME(tnagorra): The following fails because set firebase_push_status is not set on create/update
64+ # if model_obj.firebase_push_status_enum != FirebasePushStatusEnum.PENDING:
65+ # logger.warning(
66+ # "Firebase push error: push is not required for %s",
67+ # model_obj._meta.label,
68+ # extra={"id": model_obj.pk},
69+ # )
70+ # return
3771
3872 model_obj .update_firebase_push_status (FirebasePushStatusEnum .PROCESSING )
3973
4074 try :
4175 model_ref = Config .FIREBASE_HELPER .ref (
42- self .get_firebase_path (model_obj .firebase_id , self .model ),
76+ self .get_firebase_path (model_obj .firebase_id , self .model_class ),
4377 )
4478 fb_model : typing .Any = model_ref .get ()
4579
4680 if not model_obj .firebase_last_pushed :
4781 if fb_model is not None :
4882 logger .error (
49- "Firebase creation error: existing %s found " ,
83+ "Firebase create error: %s already exists in Firebase " ,
5084 model_obj ._meta .label ,
51- extra = {"model_obj_id " : model_obj .pk },
85+ extra = {"id " : model_obj .pk },
5286 )
5387 raise InvalidObjectPushException
5488 self .handle_new_object_on_firebase (model_obj , model_ref )
@@ -57,17 +91,24 @@ def push(self) -> None:
5791 logger .error (
5892 "Firebase update error: missing %s in Firebase" ,
5993 model_obj ._meta .label ,
60- extra = {"model_obj_id " : model_obj .pk },
94+ extra = {"id " : model_obj .pk },
6195 )
6296 raise InvalidObjectPushException
63- self .handle_object_update_on_firebase (model_obj , model_ref )
6497
98+ class RelaxedModel (self .firebase_model_class ):
99+ model_config = ConfigDict (extra = "ignore" )
100+
101+ # NOTE: we want to ignore extra fields from firebase
102+ valid_fb_model = RelaxedModel .model_validate (obj = fb_model )
103+ valid_fb_model = self .firebase_model_class .model_validate (valid_fb_model )
104+
105+ self .handle_object_update_on_firebase (model_obj , valid_fb_model , model_ref )
65106 except InvalidObjectPushException :
66107 model_obj .update_firebase_push_status (FirebasePushStatusEnum .FAILED )
67108 except Exception :
68109 logger .error (
69- "Unexpected error while pushing to Firebase " ,
70- extra = {"model_obj_id " : model_obj .pk },
110+ "Firebase push error: Unexpected error occurred " ,
111+ extra = {"id " : model_obj .pk },
71112 exc_info = True ,
72113 )
73114 model_obj .update_firebase_push_status (FirebasePushStatusEnum .FAILED )
0 commit comments