11from dataclasses import dataclass
2- from typing import Generic , Iterable , List , Set , Tuple , TypeVar
2+ from typing import Any , Dict , Generic , Iterable , List , Set , Tuple , TypeVar
33
44from feast .base_feature_view import BaseFeatureView
55from feast .diff .property_diff import PropertyDiff , TransitionType
1616from feast .protos .feast .core .RequestFeatureView_pb2 import (
1717 RequestFeatureView as RequestFeatureViewProto ,
1818)
19+ from feast .registry import FeastObjectType , Registry
20+ from feast .repo_contents import RepoContents
1921
20- FcoProto = TypeVar (
21- "FcoProto" ,
22- EntityProto ,
23- FeatureViewProto ,
24- FeatureServiceProto ,
25- OnDemandFeatureViewProto ,
26- RequestFeatureViewProto ,
27- )
22+ FEAST_OBJECT_TYPE_TO_STR = {
23+ FeastObjectType .ENTITY : "entity" ,
24+ FeastObjectType .FEATURE_VIEW : "feature view" ,
25+ FeastObjectType .ON_DEMAND_FEATURE_VIEW : "on demand feature view" ,
26+ FeastObjectType .REQUEST_FEATURE_VIEW : "request feature view" ,
27+ FeastObjectType .FEATURE_SERVICE : "feature service" ,
28+ }
29+
30+ FEAST_OBJECT_TYPES = FEAST_OBJECT_TYPE_TO_STR .keys ()
31+
32+ Fco = TypeVar ("Fco" , Entity , BaseFeatureView , FeatureService )
2833
2934
3035@dataclass
31- class FcoDiff (Generic [FcoProto ]):
36+ class FcoDiff (Generic [Fco ]):
3237 name : str
3338 fco_type : str
34- current_fco : FcoProto
35- new_fco : FcoProto
39+ current_fco : Fco
40+ new_fco : Fco
3641 fco_property_diffs : List [PropertyDiff ]
3742 transition_type : TransitionType
3843
@@ -48,20 +53,28 @@ def add_fco_diff(self, fco_diff: FcoDiff):
4853 self .fco_diffs .append (fco_diff )
4954
5055
51- Fco = TypeVar ("Fco" , Entity , BaseFeatureView , FeatureService )
52-
53-
54- def tag_objects_for_keep_delete_add (
56+ def tag_objects_for_keep_delete_update_add (
5557 existing_objs : Iterable [Fco ], desired_objs : Iterable [Fco ]
56- ) -> Tuple [Set [Fco ], Set [Fco ], Set [Fco ]]:
58+ ) -> Tuple [Set [Fco ], Set [Fco ], Set [Fco ], Set [ Fco ] ]:
5759 existing_obj_names = {e .name for e in existing_objs }
5860 desired_obj_names = {e .name for e in desired_objs }
5961
6062 objs_to_add = {e for e in desired_objs if e .name not in existing_obj_names }
61- objs_to_keep = {e for e in desired_objs if e .name in existing_obj_names }
63+ objs_to_update = {e for e in desired_objs if e .name in existing_obj_names }
64+ objs_to_keep = {e for e in existing_objs if e .name in desired_obj_names }
6265 objs_to_delete = {e for e in existing_objs if e .name not in desired_obj_names }
6366
64- return objs_to_keep , objs_to_delete , objs_to_add
67+ return objs_to_keep , objs_to_delete , objs_to_update , objs_to_add
68+
69+
70+ FcoProto = TypeVar (
71+ "FcoProto" ,
72+ EntityProto ,
73+ FeatureViewProto ,
74+ FeatureServiceProto ,
75+ OnDemandFeatureViewProto ,
76+ RequestFeatureViewProto ,
77+ )
6578
6679
6780def tag_proto_objects_for_keep_delete_add (
@@ -80,23 +93,158 @@ def tag_proto_objects_for_keep_delete_add(
8093FIELDS_TO_IGNORE = {"project" }
8194
8295
83- def diff_between (current : FcoProto , new : FcoProto , object_type : str ) -> FcoDiff :
84- assert current .DESCRIPTOR .full_name == new .DESCRIPTOR .full_name
96+ def diff_registry_objects (current : Fco , new : Fco , object_type : str ) -> FcoDiff :
97+ current_proto = current .to_proto ()
98+ new_proto = new .to_proto ()
99+ assert current_proto .DESCRIPTOR .full_name == new_proto .DESCRIPTOR .full_name
85100 property_diffs = []
86101 transition : TransitionType = TransitionType .UNCHANGED
87- if current .spec != new .spec :
88- for _field in current .spec .DESCRIPTOR .fields :
102+ if current_proto .spec != new_proto .spec :
103+ for _field in current_proto .spec .DESCRIPTOR .fields :
89104 if _field .name in FIELDS_TO_IGNORE :
90105 continue
91- if getattr (current .spec , _field .name ) != getattr (new .spec , _field .name ):
106+ if getattr (current_proto .spec , _field .name ) != getattr (
107+ new_proto .spec , _field .name
108+ ):
92109 transition = TransitionType .UPDATE
93110 property_diffs .append (
94111 PropertyDiff (
95112 _field .name ,
96- getattr (current .spec , _field .name ),
97- getattr (new .spec , _field .name ),
113+ getattr (current_proto .spec , _field .name ),
114+ getattr (new_proto .spec , _field .name ),
98115 )
99116 )
100117 return FcoDiff (
101- new .spec .name , object_type , current , new , property_diffs , transition ,
118+ name = new_proto .spec .name ,
119+ fco_type = object_type ,
120+ current_fco = current ,
121+ new_fco = new ,
122+ fco_property_diffs = property_diffs ,
123+ transition_type = transition ,
102124 )
125+
126+
127+ def extract_objects_for_keep_delete_update_add (
128+ registry : Registry , current_project : str , desired_repo_contents : RepoContents ,
129+ ) -> Tuple [
130+ Dict [FeastObjectType , Set [Fco ]],
131+ Dict [FeastObjectType , Set [Fco ]],
132+ Dict [FeastObjectType , Set [Fco ]],
133+ Dict [FeastObjectType , Set [Fco ]],
134+ ]:
135+ """
136+ Returns the objects in the registry that must be modified to achieve the desired repo state.
137+
138+ Args:
139+ registry: The registry storing the current repo state.
140+ current_project: The Feast project whose objects should be compared.
141+ desired_repo_contents: The desired repo state.
142+ """
143+ objs_to_keep = {}
144+ objs_to_delete = {}
145+ objs_to_update = {}
146+ objs_to_add = {}
147+
148+ registry_object_type_to_objects : Dict [FeastObjectType , List [Any ]]
149+ registry_object_type_to_objects = {
150+ FeastObjectType .ENTITY : registry .list_entities (project = current_project ),
151+ FeastObjectType .FEATURE_VIEW : registry .list_feature_views (
152+ project = current_project
153+ ),
154+ FeastObjectType .ON_DEMAND_FEATURE_VIEW : registry .list_on_demand_feature_views (
155+ project = current_project
156+ ),
157+ FeastObjectType .REQUEST_FEATURE_VIEW : registry .list_request_feature_views (
158+ project = current_project
159+ ),
160+ FeastObjectType .FEATURE_SERVICE : registry .list_feature_services (
161+ project = current_project
162+ ),
163+ }
164+ registry_object_type_to_repo_contents : Dict [FeastObjectType , Set [Any ]]
165+ registry_object_type_to_repo_contents = {
166+ FeastObjectType .ENTITY : desired_repo_contents .entities ,
167+ FeastObjectType .FEATURE_VIEW : desired_repo_contents .feature_views ,
168+ FeastObjectType .ON_DEMAND_FEATURE_VIEW : desired_repo_contents .on_demand_feature_views ,
169+ FeastObjectType .REQUEST_FEATURE_VIEW : desired_repo_contents .request_feature_views ,
170+ FeastObjectType .FEATURE_SERVICE : desired_repo_contents .feature_services ,
171+ }
172+
173+ for object_type in FEAST_OBJECT_TYPES :
174+ (
175+ to_keep ,
176+ to_delete ,
177+ to_update ,
178+ to_add ,
179+ ) = tag_objects_for_keep_delete_update_add (
180+ registry_object_type_to_objects [object_type ],
181+ registry_object_type_to_repo_contents [object_type ],
182+ )
183+
184+ objs_to_keep [object_type ] = to_keep
185+ objs_to_delete [object_type ] = to_delete
186+ objs_to_update [object_type ] = to_update
187+ objs_to_add [object_type ] = to_add
188+
189+ return objs_to_keep , objs_to_delete , objs_to_update , objs_to_add
190+
191+
192+ def diff_between (
193+ registry : Registry , current_project : str , desired_repo_contents : RepoContents ,
194+ ) -> RegistryDiff :
195+ """
196+ Returns the difference between the current and desired repo states.
197+
198+ Args:
199+ registry: The registry storing the current repo state.
200+ current_project: The Feast project for which the diff is being computed.
201+ desired_repo_contents: The desired repo state.
202+ """
203+ diff = RegistryDiff ()
204+
205+ (
206+ objs_to_keep ,
207+ objs_to_delete ,
208+ objs_to_update ,
209+ objs_to_add ,
210+ ) = extract_objects_for_keep_delete_update_add (
211+ registry , current_project , desired_repo_contents
212+ )
213+
214+ for object_type in FEAST_OBJECT_TYPES :
215+ objects_to_keep = objs_to_keep [object_type ]
216+ objects_to_delete = objs_to_delete [object_type ]
217+ objects_to_update = objs_to_update [object_type ]
218+ objects_to_add = objs_to_add [object_type ]
219+
220+ for e in objects_to_add :
221+ diff .add_fco_diff (
222+ FcoDiff (
223+ name = e .name ,
224+ fco_type = FEAST_OBJECT_TYPE_TO_STR [object_type ],
225+ current_fco = None ,
226+ new_fco = e ,
227+ fco_property_diffs = [],
228+ transition_type = TransitionType .CREATE ,
229+ )
230+ )
231+ for e in objects_to_delete :
232+ diff .add_fco_diff (
233+ FcoDiff (
234+ name = e .name ,
235+ fco_type = FEAST_OBJECT_TYPE_TO_STR [object_type ],
236+ current_fco = e ,
237+ new_fco = None ,
238+ fco_property_diffs = [],
239+ transition_type = TransitionType .DELETE ,
240+ )
241+ )
242+ for e in objects_to_update :
243+ current_obj = [_e for _e in objects_to_keep if _e .name == e .name ][0 ]
244+ diff .add_fco_diff (
245+ diff_registry_objects (
246+ current_obj , e , FEAST_OBJECT_TYPE_TO_STR [object_type ]
247+ )
248+ )
249+
250+ return diff
0 commit comments