Skip to content

Commit e304a84

Browse files
authored
fix: Populate spec.project in registry REST feature view responses (#… (#7037)
1 parent 7903e2e commit e304a84

3 files changed

Lines changed: 35 additions & 12 deletions

File tree

‎sdk/python/feast/api/registry/rest/feature_views.py‎

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,25 @@ class ApplyFeatureViewRequestBody(BaseModel):
5151
owner: Optional[str] = ""
5252

5353

54-
def _extract_feature_view_from_any(any_feature_view: dict) -> dict:
54+
def _extract_feature_view_from_any(
55+
any_feature_view: dict, project: Optional[str] = None
56+
) -> dict:
5557
"""Extract the specific feature view type and data from an AnyFeatureView object.
5658
5759
Args:
5860
any_feature_view: Dictionary containing the AnyFeatureView data
61+
project: Project the feature view was read from, filled into an unset spec.project
5962
6063
Returns:
6164
Dictionary with 'type' and feature view data, or empty dict if no valid type found
6265
"""
6366
for key, value in any_feature_view.items():
6467
if value:
65-
return {"type": key, **value}
68+
result = {"type": key, **value}
69+
spec = result.get("spec")
70+
if project and isinstance(spec, dict) and not spec.get("project"):
71+
result["spec"] = {**spec, "project": project}
72+
return result
6673

6774
return {}
6875

@@ -110,7 +117,9 @@ def list_feature_views_all(
110117
response = grpc_call(grpc_handler.ListAllFeatureViews, req)
111118
any_feature_views = response.get("featureViews", [])
112119
for any_feature_view in any_feature_views:
113-
feature_view = _extract_feature_view_from_any(any_feature_view)
120+
feature_view = _extract_feature_view_from_any(
121+
any_feature_view, project_name
122+
)
114123
if feature_view:
115124
feature_view["project"] = project_name
116125
all_feature_views.append(feature_view)
@@ -157,7 +166,7 @@ def get_any_feature_view(
157166
response = grpc_call(grpc_handler.GetAnyFeatureView, req)
158167
any_feature_view = response.get("anyFeatureView", {})
159168

160-
result = _extract_feature_view_from_any(any_feature_view)
169+
result = _extract_feature_view_from_any(any_feature_view, project)
161170

162171
if include_relationships:
163172
relationships = get_object_relationships(
@@ -315,7 +324,7 @@ def list_all_feature_views(
315324
# Extract the specific type of feature view from each AnyFeatureView
316325
feature_views = []
317326
for any_feature_view in any_feature_views:
318-
feature_view = _extract_feature_view_from_any(any_feature_view)
327+
feature_view = _extract_feature_view_from_any(any_feature_view, project)
319328
if feature_view:
320329
feature_views.append(feature_view)
321330

‎sdk/python/feast/api/registry/rest/rest_registry_server.py‎

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import logging
33
import re
44

5-
from fastapi import Depends, FastAPI, HTTPException, Request, status
5+
from fastapi import Depends, FastAPI, HTTPException, Request
66
from fastapi.exceptions import RequestValidationError
77
from fastapi.responses import JSONResponse
88
from pydantic import ValidationError
@@ -63,12 +63,6 @@ def __init__(self, store: FeatureStore):
6363
root_path="/api/v1",
6464
docs_url="/",
6565
redoc_url="/docs",
66-
default_status_code=status.HTTP_200_OK,
67-
default_headers={
68-
"X-Content-Type-Options": "nosniff",
69-
"X-XSS-Protection": "1; mode=block",
70-
"X-Frame-Options": "DENY",
71-
},
7266
)
7367
self._add_exception_handlers()
7468
self._add_logging_middleware()

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,26 @@ def test_feature_views_type_field_via_rest(fastapi_test_app):
215215
assert data["spec"]["name"] == "user_profile"
216216

217217

218+
def test_feature_views_spec_project_via_rest(fastapi_test_app):
219+
"""Test that spec.project is populated on every feature view endpoint."""
220+
response = fastapi_test_app.get("/feature_views?project=demo_project")
221+
assert response.status_code == 200
222+
for fv in response.json()["featureViews"]:
223+
assert fv["spec"]["project"] == "demo_project"
224+
225+
response = fastapi_test_app.get("/feature_views/user_profile?project=demo_project")
226+
assert response.status_code == 200
227+
assert response.json()["spec"]["project"] == "demo_project"
228+
229+
response = fastapi_test_app.get("/feature_views/all")
230+
assert response.status_code == 200
231+
feature_views = response.json()["featureViews"]
232+
assert feature_views
233+
for fv in feature_views:
234+
assert fv["project"] == "demo_project"
235+
assert fv["spec"]["project"] == "demo_project"
236+
237+
218238
def test_feature_views_entity_filtering_via_rest(fastapi_test_app):
219239
"""Test that feature views can be filtered by entity."""
220240
response = fastapi_test_app.get("/feature_views?project=demo_project")

0 commit comments

Comments
 (0)