Skip to content

Commit 4a9a5d0

Browse files
authored
fix: Fixed error thrown for invalid project name on features api (feast-dev#5525)
1 parent ea53b2b commit 4a9a5d0

File tree

4 files changed

+100
-10
lines changed

4 files changed

+100
-10
lines changed

sdk/python/feast/api/registry/rest/data_sources.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def list_data_sources(
4444
data_sources = response.get("dataSources", [])
4545

4646
result = {
47-
"data_sources": data_sources,
47+
"dataSources": data_sources,
4848
"pagination": response.get("pagination", {}),
4949
}
5050

sdk/python/feast/api/registry/rest/entities.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,28 @@ def get_entity(
9595

9696
result = entity
9797

98+
relationships = get_object_relationships(
99+
grpc_handler, "entity", name, project, allow_cache
100+
)
101+
ds_list_req = RegistryServer_pb2.ListDataSourcesRequest(
102+
project=project,
103+
allow_cache=allow_cache,
104+
)
105+
ds_list_resp = grpc_call(grpc_handler.ListDataSources, ds_list_req)
106+
ds_map = {ds["name"]: ds for ds in ds_list_resp.get("dataSources", [])}
107+
data_source_objs = []
108+
seen_ds_names = set()
109+
for rel in relationships:
110+
if rel.get("target", {}).get("type") == "dataSource":
111+
ds_name = rel["target"]["name"]
112+
if ds_name not in seen_ds_names:
113+
ds_obj = ds_map.get(ds_name)
114+
if ds_obj:
115+
data_source_objs.append(ds_obj)
116+
seen_ds_names.add(ds_name)
117+
result["dataSources"] = data_source_objs
118+
98119
if include_relationships:
99-
relationships = get_object_relationships(
100-
grpc_handler, "entity", name, project, allow_cache
101-
)
102120
result["relationships"] = relationships
103121

104122
return result

sdk/python/feast/api/registry/rest/features.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,15 @@ def list_features(
3939
sorting=create_grpc_sorting_params(sorting_params),
4040
)
4141
response = grpc_call(grpc_handler.ListFeatures, req)
42+
if "features" not in response:
43+
response["features"] = []
44+
if "pagination" not in response:
45+
response["pagination"] = {}
46+
4247
if include_relationships:
48+
features = response.get("features", [])
4349
relationships = get_relationships_for_objects(
44-
grpc_handler, response["features"], "feature", project, allow_cache
50+
grpc_handler, features, "feature", project, allow_cache
4551
)
4652
response["relationships"] = relationships
4753
return response

sdk/python/tests/unit/api/test_api_rest_registry.py

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,7 @@ def test_feature_services_via_rest(fastapi_test_app):
145145

146146
def test_data_sources_via_rest(fastapi_test_app):
147147
response = fastapi_test_app.get("/data_sources?project=demo_project")
148-
assert response.status_code == 200
149-
assert "data_sources" in response.json()
148+
assert "dataSources" in response.json()
150149
response = fastapi_test_app.get(
151150
"/data_sources/user_profile_source?project=demo_project"
152151
)
@@ -650,9 +649,9 @@ def test_data_sources_pagination_via_rest(fastapi_test_app_with_multiple_objects
650649
response = client.get("/data_sources?project=demo_project&page=1&limit=2")
651650
assert response.status_code == 200
652651
data = response.json()
653-
assert "data_sources" in data
652+
assert "dataSources" in data
654653
assert "pagination" in data
655-
assert len(data["data_sources"]) == 2
654+
assert len(data["dataSources"]) == 2
656655
assert data["pagination"]["page"] == 1
657656
assert data["pagination"]["limit"] == 2
658657
assert data["pagination"]["totalCount"] == 3
@@ -669,7 +668,7 @@ def test_data_sources_sorting_via_rest(fastapi_test_app_with_multiple_objects):
669668
)
670669
assert response.status_code == 200
671670
data = response.json()
672-
ds_names = [ds["name"] for ds in data["data_sources"]]
671+
ds_names = [ds["name"] for ds in data["dataSources"]]
673672
assert ds_names == sorted(ds_names)
674673

675674

@@ -1064,3 +1063,70 @@ def test_lineage_complete_all_via_rest(fastapi_test_app):
10641063
assert "dataSources" in project_data["objects"]
10651064
assert "featureViews" in project_data["objects"]
10661065
assert "featureServices" in project_data["objects"]
1066+
1067+
1068+
def test_invalid_project_name_with_relationships_via_rest(fastapi_test_app):
1069+
"""Test REST API response with invalid project name using include_relationships=true.
1070+
The API should not throw 500 or any other error when an invalid project name is provided
1071+
with include_relationships=true parameter.
1072+
"""
1073+
response = fastapi_test_app.get(
1074+
"/entities?project=invalid_project_name&include_relationships=true"
1075+
)
1076+
assert response.status_code == 200
1077+
data = response.json()
1078+
assert "entities" in data
1079+
assert isinstance(data["entities"], list)
1080+
assert len(data["entities"]) == 0
1081+
assert "relationships" in data
1082+
assert isinstance(data["relationships"], dict)
1083+
assert len(data["relationships"]) == 0
1084+
1085+
response = fastapi_test_app.get(
1086+
"/feature_views?project=invalid_project_name&include_relationships=true"
1087+
)
1088+
assert response.status_code == 200
1089+
data = response.json()
1090+
assert "featureViews" in data
1091+
assert isinstance(data["featureViews"], list)
1092+
assert len(data["featureViews"]) == 0
1093+
assert "relationships" in data
1094+
assert isinstance(data["relationships"], dict)
1095+
assert len(data["relationships"]) == 0
1096+
1097+
response = fastapi_test_app.get(
1098+
"/data_sources?project=invalid_project_name&include_relationships=true"
1099+
)
1100+
# Should return 200 with empty results, not 500 or other errors
1101+
assert response.status_code == 200
1102+
data = response.json()
1103+
assert "dataSources" in data
1104+
assert isinstance(data["dataSources"], list)
1105+
assert len(data["dataSources"]) == 0
1106+
assert "relationships" in data
1107+
assert isinstance(data["relationships"], dict)
1108+
assert len(data["relationships"]) == 0
1109+
1110+
response = fastapi_test_app.get(
1111+
"/feature_services?project=invalid_project_name&include_relationships=true"
1112+
)
1113+
assert response.status_code == 200
1114+
data = response.json()
1115+
assert "featureServices" in data
1116+
assert isinstance(data["featureServices"], list)
1117+
assert len(data["featureServices"]) == 0
1118+
assert "relationships" in data
1119+
assert isinstance(data["relationships"], dict)
1120+
assert len(data["relationships"]) == 0
1121+
1122+
response = fastapi_test_app.get(
1123+
"/features?project=invalid_project_name&include_relationships=true"
1124+
)
1125+
assert response.status_code == 200
1126+
data = response.json()
1127+
assert "features" in data
1128+
assert isinstance(data["features"], list)
1129+
assert len(data["features"]) == 0
1130+
assert "relationships" in data
1131+
assert isinstance(data["relationships"], dict)
1132+
assert len(data["relationships"]) == 0

0 commit comments

Comments
 (0)