From e9da20894cbc30e7df5d8cd145a28ea598e1f0c9 Mon Sep 17 00:00:00 2001 From: ntkathole Date: Wed, 16 Jul 2025 19:17:55 +0530 Subject: [PATCH] feat: Added grpc and rest endpoint for features Signed-off-by: ntkathole --- .../feature-servers/registry-server.md | 179 +++++++++++++++++- protos/feast/registry/RegistryServer.proto | 32 ++++ .../feast/api/registry/rest/__init__.py | 2 + .../feast/api/registry/rest/features.py | 97 ++++++++++ sdk/python/feast/api/registry/rest/lineage.py | 31 ++- .../feast/api/registry/rest/rest_utils.py | 11 +- .../feast/infra/registry/base_registry.py | 6 +- sdk/python/feast/lineage/registry_lineage.py | 63 ++++-- .../feast/registry/RegistryServer_pb2.py | 18 +- .../feast/registry/RegistryServer_pb2.pyi | 116 ++++++++++++ .../feast/registry/RegistryServer_pb2_grpc.py | 67 +++++++ sdk/python/feast/registry_server.py | 72 +++++++ .../tests/unit/api/test_api_rest_registry.py | 125 ++++++++++++ .../unit/api/test_api_rest_registry_server.py | 3 + 14 files changed, 786 insertions(+), 36 deletions(-) create mode 100644 sdk/python/feast/api/registry/rest/features.py diff --git a/docs/reference/feature-servers/registry-server.md b/docs/reference/feature-servers/registry-server.md index ead393721fc..53ac7405ff7 100644 --- a/docs/reference/feature-servers/registry-server.md +++ b/docs/reference/feature-servers/registry-server.md @@ -274,6 +274,137 @@ Most endpoints support these common query parameters: } ``` +### Features + +#### List Features +- **Endpoint**: `GET /api/v1/features` +- **Description**: Retrieve all features in a project +- **Parameters**: + - `project` (required): Project name + - `feature_view` (optional): Filter by feature view name + - `name` (optional): Filter by feature name + - `include_relationships` (optional): Include relationships for each feature (relationships are keyed by feature name) + - `allow_cache` (optional): Whether to allow cached data + - `page` (optional): Page number for pagination + - `limit` (optional): Number of items per page + - `sort_by` (optional): Field to sort by + - `sort_order` (optional): Sort order ("asc" or "desc") +- **Examples**: + ```bash + # Basic list + curl -H "Authorization: Bearer " \ + "http://localhost:6572/api/v1/features?project=my_project" + + # With pagination and relationships + curl -H "Authorization: Bearer " \ + "http://localhost:6572/api/v1/features?project=my_project&include_relationships=true&page=1&limit=5" + ``` +- **Response Example**: + ```json + { + "features": [ + { "name": "conv_rate", "featureView": "driver_hourly_stats_fresh", "type": "Float32" }, + { "name": "acc_rate", "featureView": "driver_hourly_stats_fresh", "type": "Float32" }, + { "name": "avg_daily_trips", "featureView": "driver_hourly_stats_fresh", "type": "Int64" }, + { "name": "conv_rate", "featureView": "driver_hourly_stats", "type": "Float32" }, + { "name": "acc_rate", "featureView": "driver_hourly_stats", "type": "Float32" } + ], + "pagination": { + "page": 1, + "limit": 5, + "totalCount": 10, + "totalPages": 2, + "hasNext": true + }, + "relationships": { + "conv_rate": [ + { "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats_fresh" } }, + { "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats" } }, + { "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureService", "name": "driver_activity_v1" } } + ] + } + } + ``` + +#### Get Feature +- **Endpoint**: `GET /api/v1/features/{feature_view}/{name}` +- **Description**: Retrieve a specific feature by feature view and name +- **Parameters**: + - `feature_view` (path): Feature view name + - `name` (path): Feature name + - `project` (required): Project name + - `include_relationships` (optional): Include relationships for this feature + - `allow_cache` (optional): Whether to allow cached data +- **Examples**: + ```bash + # Basic get + curl -H "Authorization: Bearer " \ + "http://localhost:6572/api/v1/features/driver_hourly_stats/conv_rate?project=my_project" + + # With relationships + curl -H "Authorization: Bearer " \ + "http://localhost:6572/api/v1/features/driver_hourly_stats/conv_rate?project=my_project&include_relationships=true" + ``` +- **Response Example**: + ```json + { + "name": "conv_rate", + "featureView": "driver_hourly_stats", + "type": "Float32", + "relationships": [ + { "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats_fresh" } }, + { "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats" } }, + { "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureService", "name": "driver_activity_v1" } } + ] + } + ``` + +#### List All Features (All Projects) +- **Endpoint**: `GET /api/v1/features/all` +- **Description**: Retrieve all features across all projects. Each feature includes a `project` field. +- **Parameters**: + - `page` (optional): Page number for pagination + - `limit` (optional): Number of items per page + - `sort_by` (optional): Field to sort by + - `sort_order` (optional): Sort order ("asc" or "desc") + - `include_relationships` (optional): Include relationships for each feature + - `allow_cache` (optional): Whether to allow cached data +- **Examples**: + ```bash + curl -H "Authorization: Bearer " \ + "http://localhost:6572/api/v1/features/all?page=1&limit=5&sort_by=name" + # With relationships + curl -H "Authorization: Bearer " \ + "http://localhost:6572/api/v1/features/all?include_relationships=true" + ``` +- **Response Example**: + ```json + { + "features": [ + { "name": "conv_rate", "featureView": "driver_hourly_stats_fresh", "type": "Float32", "project": "multiproject" }, + { "name": "acc_rate", "featureView": "driver_hourly_stats_fresh", "type": "Float32", "project": "multiproject" }, + { "name": "avg_daily_trips", "featureView": "driver_hourly_stats_fresh", "type": "Int64", "project": "multiproject" }, + { "name": "conv_rate", "featureView": "driver_hourly_stats", "type": "Float32", "project": "multiproject" }, + { "name": "acc_rate", "featureView": "driver_hourly_stats", "type": "Float32", "project": "multiproject" } + ], + "pagination": { + "page": 1, + "limit": 5, + "total_count": 20, + "total_pages": 4, + "has_next": true, + "has_previous": false + }, + "relationships": { + "conv_rate": [ + { "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats" } }, + { "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats_fresh" } }, + { "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureService", "name": "driver_activity_v3" } } + ] + } + } + ``` + ### Feature Services #### List Feature Services @@ -356,7 +487,7 @@ Most endpoints support these common query parameters: - **Parameters**: - `project` (required): Project name - `allow_cache` (optional): Whether to allow cached data - - `filter_object_type` (optional): Filter by object type (`dataSource`, `entity`, `featureView`, `featureService`) + - `filter_object_type` (optional): Filter by object type (`dataSource`, `entity`, `featureView`, `featureService`, `feature`) - `filter_object_name` (optional): Filter by object name - **Example**: ```bash @@ -368,7 +499,7 @@ Most endpoints support these common query parameters: - **Endpoint**: `GET /api/v1/lineage/objects/{object_type}/{object_name}` - **Description**: Retrieve relationships for a specific object - **Parameters**: - - `object_type` (path): Type of object (`dataSource`, `entity`, `featureView`, `featureService`) + - `object_type` (path): Type of object (`dataSource`, `entity`, `featureView`, `featureService`, `feature`) - `object_name` (path): Name of the object - `project` (required): Project name - `include_indirect` (optional): Whether to include indirect relationships @@ -376,7 +507,17 @@ Most endpoints support these common query parameters: - **Example**: ```bash curl -H "Authorization: Bearer " \ - "http://localhost:6572/api/v1/lineage/objects/featureView/user_features?project=my_project&include_indirect=true" + "http://localhost:6572/api/v1/lineage/objects/feature/conv_rate?project=my_project&include_indirect=true" + ``` +- **Response Example**: + ```json + { + "relationships": [ + { "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats_fresh" } }, + { "source": { "type": "feature", "name": "conv_rate" }, "target": { "type": "featureView", "name": "driver_hourly_stats" } } + ], + "pagination": { "totalCount": 2, "totalPages": 1 } + } ``` #### Get Complete Registry Data @@ -390,6 +531,35 @@ Most endpoints support these common query parameters: curl -H "Authorization: Bearer " \ "http://localhost:6572/api/v1/lineage/complete?project=my_project" ``` +- **Response Example**: + ```json + { + "project": "multiproject", + "objects": { + "entities": [ ... ], + "dataSources": [ ... ], + "featureViews": [ ... ], + "featureServices": [ ... ], + "features": [ + { "name": "conv_rate", "featureView": "driver_hourly_stats_fresh", "type": "Float32" }, + { "name": "acc_rate", "featureView": "driver_hourly_stats_fresh", "type": "Float32" }, + { "name": "avg_daily_trips", "featureView": "driver_hourly_stats_fresh", "type": "Int64" }, + { "name": "conv_rate", "featureView": "driver_hourly_stats", "type": "Float32" }, + { "name": "acc_rate", "featureView": "driver_hourly_stats", "type": "Float32" }, + { "name": "conv_rate_plus_val1", "featureView": "transformed_conv_rate_fresh", "type": "Float64" }, + { "name": "conv_rate_plus_val2", "featureView": "transformed_conv_rate_fresh", "type": "Float64" }, + { "name": "conv_rate_plus_val1", "featureView": "transformed_conv_rate", "type": "Float64" }, + { "name": "conv_rate_plus_val2", "featureView": "transformed_conv_rate", "type": "Float64" } + ] + }, + "relationships": [ ... ], + "indirectRelationships": [ ... ], + "pagination": { + "features": { "totalCount": 10, "totalPages": 1 }, + ... + } + } + ``` #### Get Registry Lineage (All Projects) - **Endpoint**: `GET /api/v1/lineage/registry/all` @@ -716,6 +886,7 @@ Relationships show how different Feast objects connect to each other, providing - `entity` - Feast entities - `dataSource` - Data sources - `featureView` - Feature views (including regular, on-demand, and stream) +- `feature` - Features (including features from on-demand feature views) - `featureService` - Feature services - `permission` - Permissions - `savedDataset` - Saved datasets @@ -724,6 +895,8 @@ Relationships show how different Feast objects connect to each other, providing - Feature Views → Data Sources (feature views depend on data sources) - Feature Views → Entities (feature views use entities as join keys) - Feature Services → Feature Views (feature services consume feature views) +- Features → Feature Views (features belong to feature views, including on-demand feature views) +- Features → Feature Services (features are consumed by feature services) - Entities → Data Sources (entities connect to data sources through feature views) - Entities → Feature Services (entities connect to feature services through feature views) diff --git a/protos/feast/registry/RegistryServer.proto b/protos/feast/registry/RegistryServer.proto index b16bebbb198..1979c6a08aa 100644 --- a/protos/feast/registry/RegistryServer.proto +++ b/protos/feast/registry/RegistryServer.proto @@ -92,6 +92,9 @@ service RegistryServer{ rpc GetRegistryLineage (GetRegistryLineageRequest) returns (GetRegistryLineageResponse) {} rpc GetObjectRelationships (GetObjectRelationshipsRequest) returns (GetObjectRelationshipsResponse) {} + // Feature RPCs + rpc ListFeatures (ListFeaturesRequest) returns (ListFeaturesResponse) {} + rpc GetFeature (GetFeatureRequest) returns (Feature) {} } // Common pagination and sorting messages @@ -524,3 +527,32 @@ message GetObjectRelationshipsResponse { repeated EntityRelation relationships = 1; PaginationMetadata pagination = 2; } + +// Feature messages +message Feature { + string name = 1; + string feature_view = 2; + string type = 3; + string description = 4; + map tags = 7; +} + +message ListFeaturesRequest { + string project = 1; + string feature_view = 2; + string name = 3; + bool allow_cache = 6; + PaginationParams pagination = 4; + SortingParams sorting = 5; +} + +message ListFeaturesResponse { + repeated Feature features = 1; + PaginationMetadata pagination = 2; +} + +message GetFeatureRequest { + string project = 1; + string feature_view = 2; + string name = 3; +} diff --git a/sdk/python/feast/api/registry/rest/__init__.py b/sdk/python/feast/api/registry/rest/__init__.py index 911ae4f8fb3..3d6c4c8ebb3 100644 --- a/sdk/python/feast/api/registry/rest/__init__.py +++ b/sdk/python/feast/api/registry/rest/__init__.py @@ -4,6 +4,7 @@ from feast.api.registry.rest.entities import get_entity_router from feast.api.registry.rest.feature_services import get_feature_service_router from feast.api.registry.rest.feature_views import get_feature_view_router +from feast.api.registry.rest.features import get_feature_router from feast.api.registry.rest.lineage import get_lineage_router from feast.api.registry.rest.permissions import get_permission_router from feast.api.registry.rest.projects import get_project_router @@ -15,6 +16,7 @@ def register_all_routes(app: FastAPI, grpc_handler): app.include_router(get_data_source_router(grpc_handler)) app.include_router(get_feature_service_router(grpc_handler)) app.include_router(get_feature_view_router(grpc_handler)) + app.include_router(get_feature_router(grpc_handler)) app.include_router(get_lineage_router(grpc_handler)) app.include_router(get_permission_router(grpc_handler)) app.include_router(get_project_router(grpc_handler)) diff --git a/sdk/python/feast/api/registry/rest/features.py b/sdk/python/feast/api/registry/rest/features.py new file mode 100644 index 00000000000..74a9c4bd5f3 --- /dev/null +++ b/sdk/python/feast/api/registry/rest/features.py @@ -0,0 +1,97 @@ +from fastapi import APIRouter, Depends, Query + +from feast.api.registry.rest.rest_utils import ( + aggregate_across_projects, + create_grpc_pagination_params, + create_grpc_sorting_params, + get_object_relationships, + get_pagination_params, + get_relationships_for_objects, + get_sorting_params, + grpc_call, +) +from feast.registry_server import RegistryServer_pb2 + + +def get_feature_router(grpc_handler) -> APIRouter: + router = APIRouter() + + @router.get("/features") + def list_features( + project: str = Query(...), + feature_view: str = Query(None), + name: str = Query(None), + include_relationships: bool = Query( + False, description="Include relationships for each feature" + ), + allow_cache: bool = Query( + True, description="Allow using cached registry data (default: true)" + ), + pagination_params: dict = Depends(get_pagination_params), + sorting_params: dict = Depends(get_sorting_params), + ): + req = RegistryServer_pb2.ListFeaturesRequest( + project=project, + feature_view=feature_view or "", + name=name or "", + allow_cache=allow_cache, + pagination=create_grpc_pagination_params(pagination_params), + sorting=create_grpc_sorting_params(sorting_params), + ) + response = grpc_call(grpc_handler.ListFeatures, req) + if include_relationships: + relationships = get_relationships_for_objects( + grpc_handler, response["features"], "feature", project, allow_cache + ) + response["relationships"] = relationships + return response + + @router.get("/features/{feature_view}/{name}") + def get_feature( + feature_view: str, + name: str, + project: str = Query(...), + include_relationships: bool = Query( + False, description="Include relationships for this feature" + ), + ): + req = RegistryServer_pb2.GetFeatureRequest( + project=project, + feature_view=feature_view, + name=name, + ) + response = grpc_call(grpc_handler.GetFeature, req) + if include_relationships: + response["relationships"] = get_object_relationships( + grpc_handler, "feature", name, project + ) + return response + + @router.get("/features/all") + def list_features_all( + page: int = Query(1, ge=1), + limit: int = Query(50, ge=1, le=100), + sort_by: str = Query(None), + sort_order: str = Query("asc"), + include_relationships: bool = Query( + False, description="Include relationships for each feature" + ), + allow_cache: bool = Query( + True, description="Allow using cached registry data (default: true)" + ), + ): + return aggregate_across_projects( + grpc_handler=grpc_handler, + list_method=grpc_handler.ListFeatures, + request_cls=RegistryServer_pb2.ListFeaturesRequest, + response_key="features", + object_type="feature", + include_relationships=include_relationships, + allow_cache=allow_cache, + page=page, + limit=limit, + sort_by=sort_by, + sort_order=sort_order, + ) + + return router diff --git a/sdk/python/feast/api/registry/rest/lineage.py b/sdk/python/feast/api/registry/rest/lineage.py index d8f075ab070..3c22aadc125 100644 --- a/sdk/python/feast/api/registry/rest/lineage.py +++ b/sdk/python/feast/api/registry/rest/lineage.py @@ -31,7 +31,7 @@ def get_registry_lineage( Args: project: Project name allow_cache: Whether to allow cached data - filter_object_type: Optional filter by object type (dataSource, entity, featureView, featureService) + filter_object_type: Optional filter by object type (dataSource, entity, featureView, featureService, feature) filter_object_name: Optional filter by object name Returns: Dictionary containing relationships and indirect_relationships arrays @@ -68,7 +68,7 @@ def get_object_relationships_path( """ Get relationships for a specific object. Args: - object_type: Type of object (dataSource, entity, featureView, featureService) + object_type: Type of object (dataSource, entity, featureView, featureService, feature) object_name: Name of the object project: Project name include_indirect: Whether to include indirect relationships @@ -76,7 +76,13 @@ def get_object_relationships_path( Returns: Dictionary containing relationships array for the specific object """ - valid_types = ["dataSource", "entity", "featureView", "featureService"] + valid_types = [ + "dataSource", + "entity", + "featureView", + "featureService", + "feature", + ] if object_type not in valid_types: raise HTTPException( status_code=400, @@ -109,6 +115,7 @@ def get_complete_registry_data( - Relationships - Indirect relationships - Merged feature view data + - Features Args: project: Project name @@ -174,6 +181,13 @@ def get_complete_registry_data( grpc_handler.ListFeatureServices, feature_services_req ) + features_req = RegistryServer_pb2.ListFeaturesRequest( + project=project, + pagination=grpc_pagination, + sorting=grpc_sorting, + ) + features_response = grpc_call(grpc_handler.ListFeatures, features_req) + return { "project": project, "objects": { @@ -181,6 +195,7 @@ def get_complete_registry_data( "dataSources": data_sources_response.get("dataSources", []), "featureViews": feature_views_response.get("featureViews", []), "featureServices": feature_services_response.get("featureServices", []), + "features": features_response.get("features", []), }, "relationships": lineage_response.get("relationships", []), "indirectRelationships": lineage_response.get("indirectRelationships", []), @@ -189,6 +204,7 @@ def get_complete_registry_data( "dataSources": data_sources_response.get("pagination", {}), "featureViews": feature_views_response.get("pagination", {}), "featureServices": feature_services_response.get("pagination", {}), + "features": features_response.get("pagination", {}), "relationships": lineage_response.get("relationshipsPagination", {}), "indirectRelationships": lineage_response.get( "indirectRelationshipsPagination", {} @@ -277,6 +293,12 @@ def get_complete_registry_data_all( feature_services_response = grpc_call( grpc_handler.ListFeatureServices, feature_services_req ) + + features_req = RegistryServer_pb2.ListFeaturesRequest( + project=project_name, + ) + features_response = grpc_call(grpc_handler.ListFeatures, features_req) + # Add project field to each object for entity in entities_response.get("entities", []): entity["project"] = project_name @@ -286,6 +308,8 @@ def get_complete_registry_data_all( fv["project"] = project_name for fs in feature_services_response.get("featureServices", []): fs["project"] = project_name + for feat in features_response.get("features", []): + feat["project"] = project_name all_data.append( { "project": project_name, @@ -296,6 +320,7 @@ def get_complete_registry_data_all( "featureServices": feature_services_response.get( "featureServices", [] ), + "features": features_response.get("features", []), }, "relationships": lineage_response.get("relationships", []), "indirectRelationships": lineage_response.get( diff --git a/sdk/python/feast/api/registry/rest/rest_utils.py b/sdk/python/feast/api/registry/rest/rest_utils.py index 190357dd014..359a69b4139 100644 --- a/sdk/python/feast/api/registry/rest/rest_utils.py +++ b/sdk/python/feast/api/registry/rest/rest_utils.py @@ -68,7 +68,7 @@ def get_relationships_for_objects( Args: grpc_handler: The gRPC handler to use for calls objects: List of objects to get relationships for - object_type: Type of objects (dataSource, entity, featureView, featureService) + object_type: Type of objects (dataSource, entity, featureView, featureService, feature) project: Project name allow_cache: Whether to allow cached data @@ -78,10 +78,10 @@ def get_relationships_for_objects( relationships_map = {} for obj in objects: - # Extract object name from different possible structures obj_name = None - if isinstance(obj, dict): - # Consistent name extraction logic for all object types + if object_type == "feature": + obj_name = obj.get("name") + elif isinstance(obj, dict): obj_name = ( obj.get("name") or obj.get("spec", {}).get("name") @@ -89,13 +89,14 @@ def get_relationships_for_objects( ) if obj_name: - relationships_map[obj_name] = get_object_relationships( + rels = get_object_relationships( grpc_handler, object_type, obj_name, project, allow_cache, ) + relationships_map[obj_name] = rels return relationships_map diff --git a/sdk/python/feast/infra/registry/base_registry.py b/sdk/python/feast/infra/registry/base_registry.py index 8cd19eddee4..dabe58feb46 100644 --- a/sdk/python/feast/infra/registry/base_registry.py +++ b/sdk/python/feast/infra/registry/base_registry.py @@ -863,7 +863,7 @@ def get_object_relationships( Get relationships for a specific object. Args: project: Feast project name - object_type: Type of object (dataSource, entity, featureView, featureService) + object_type: Type of object (dataSource, entity, featureView, featureService, feature) object_name: Name of the object include_indirect: Whether to include indirect relationships allow_cache: Whether to allow returning data from a cached registry @@ -876,10 +876,10 @@ def get_object_relationships( registry_proto = self._build_registry_proto(project, allow_cache) lineage_generator = RegistryLineageGenerator() - - return lineage_generator.get_object_relationships( + relationships = lineage_generator.get_object_relationships( registry_proto, object_type, object_name, include_indirect=include_indirect ) + return relationships def _build_registry_proto( self, project: str, allow_cache: bool = False diff --git a/sdk/python/feast/lineage/registry_lineage.py b/sdk/python/feast/lineage/registry_lineage.py index e7e07f7953c..c0e8f8ed4c7 100644 --- a/sdk/python/feast/lineage/registry_lineage.py +++ b/sdk/python/feast/lineage/registry_lineage.py @@ -18,6 +18,7 @@ class FeastObjectType(Enum): ENTITY = "entity" FEATURE_VIEW = "featureView" FEATURE_SERVICE = "featureService" + FEATURE = "feature" @dataclass @@ -89,17 +90,16 @@ def _parse_direct_relationships(self, registry: Registry) -> List[EntityRelation and feature_service.spec.features ): for feature in feature_service.spec.features: - relationships.append( - EntityRelation( - source=EntityReference( - FeastObjectType.FEATURE_VIEW, feature.feature_view_name - ), - target=EntityReference( - FeastObjectType.FEATURE_SERVICE, - feature_service.spec.name, - ), - ) + rel = EntityRelation( + source=EntityReference( + FeastObjectType.FEATURE_VIEW, feature.feature_view_name + ), + target=EntityReference( + FeastObjectType.FEATURE_SERVICE, + feature_service.spec.name, + ), ) + relationships.append(rel) # Entity -> FeatureView and DataSource -> FeatureView relationships for feature_view in registry.feature_views: @@ -107,16 +107,26 @@ def _parse_direct_relationships(self, registry: Registry) -> List[EntityRelation # Entity relationships if hasattr(feature_view.spec, "entities"): for entity_name in feature_view.spec.entities: - relationships.append( - EntityRelation( - source=EntityReference( - FeastObjectType.ENTITY, entity_name - ), - target=EntityReference( - FeastObjectType.FEATURE_VIEW, feature_view.spec.name - ), - ) + rel = EntityRelation( + source=EntityReference(FeastObjectType.ENTITY, entity_name), + target=EntityReference( + FeastObjectType.FEATURE_VIEW, feature_view.spec.name + ), ) + relationships.append(rel) + + # Feature -> FeatureView relationships + if hasattr(feature_view.spec, "features"): + for feature in feature_view.spec.features: + rel = EntityRelation( + source=EntityReference( + FeastObjectType.FEATURE, feature.name + ), + target=EntityReference( + FeastObjectType.FEATURE_VIEW, feature_view.spec.name + ), + ) + relationships.append(rel) # Batch source relationship if ( @@ -160,6 +170,21 @@ def _parse_direct_relationships(self, registry: Registry) -> List[EntityRelation ) ) + # OnDemand FeatureView: Feature -> OnDemandFeatureView relationships + for odfv in registry.on_demand_feature_views: + if hasattr(odfv, "spec") and odfv.spec and hasattr(odfv.spec, "features"): + for feature in odfv.spec.features: + relationships.append( + EntityRelation( + source=EntityReference( + FeastObjectType.FEATURE, feature.name + ), + target=EntityReference( + FeastObjectType.FEATURE_VIEW, odfv.spec.name + ), + ) + ) + # OnDemand FeatureView relationships for odfv in registry.on_demand_feature_views: if ( diff --git a/sdk/python/feast/protos/feast/registry/RegistryServer_pb2.py b/sdk/python/feast/protos/feast/registry/RegistryServer_pb2.py index efe2ecea418..09741323317 100644 --- a/sdk/python/feast/protos/feast/registry/RegistryServer_pb2.py +++ b/sdk/python/feast/protos/feast/registry/RegistryServer_pb2.py @@ -28,7 +28,7 @@ from feast.protos.feast.core import Project_pb2 as feast_dot_core_dot_Project__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#feast/registry/RegistryServer.proto\x12\x0e\x66\x65\x61st.registry\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x19\x66\x65\x61st/core/Registry.proto\x1a\x17\x66\x65\x61st/core/Entity.proto\x1a\x1b\x66\x65\x61st/core/DataSource.proto\x1a\x1c\x66\x65\x61st/core/FeatureView.proto\x1a\"feast/core/StreamFeatureView.proto\x1a$feast/core/OnDemandFeatureView.proto\x1a\x1f\x66\x65\x61st/core/FeatureService.proto\x1a\x1d\x66\x65\x61st/core/SavedDataset.proto\x1a\"feast/core/ValidationProfile.proto\x1a\x1c\x66\x65\x61st/core/InfraObject.proto\x1a\x1b\x66\x65\x61st/core/Permission.proto\x1a\x18\x66\x65\x61st/core/Project.proto\"/\n\x10PaginationParams\x12\x0c\n\x04page\x18\x01 \x01(\x05\x12\r\n\x05limit\x18\x02 \x01(\x05\"4\n\rSortingParams\x12\x0f\n\x07sort_by\x18\x01 \x01(\t\x12\x12\n\nsort_order\x18\x02 \x01(\t\"\x83\x01\n\x12PaginationMetadata\x12\x0c\n\x04page\x18\x01 \x01(\x05\x12\r\n\x05limit\x18\x02 \x01(\x05\x12\x13\n\x0btotal_count\x18\x03 \x01(\x05\x12\x13\n\x0btotal_pages\x18\x04 \x01(\x05\x12\x10\n\x08has_next\x18\x05 \x01(\x08\x12\x14\n\x0chas_previous\x18\x06 \x01(\x08\"!\n\x0eRefreshRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\"W\n\x12UpdateInfraRequest\x12 \n\x05infra\x18\x01 \x01(\x0b\x32\x11.feast.core.Infra\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"7\n\x0fGetInfraRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\"B\n\x1aListProjectMetadataRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\"T\n\x1bListProjectMetadataResponse\x12\x35\n\x10project_metadata\x18\x01 \x03(\x0b\x32\x1b.feast.core.ProjectMetadata\"\xcb\x01\n\x1b\x41pplyMaterializationRequest\x12-\n\x0c\x66\x65\x61ture_view\x18\x01 \x01(\x0b\x32\x17.feast.core.FeatureView\x12\x0f\n\x07project\x18\x02 \x01(\t\x12.\n\nstart_date\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08\x65nd_date\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06\x63ommit\x18\x05 \x01(\x08\"Y\n\x12\x41pplyEntityRequest\x12\"\n\x06\x65ntity\x18\x01 \x01(\x0b\x32\x12.feast.core.Entity\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"F\n\x10GetEntityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x8b\x02\n\x13ListEntitiesRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12;\n\x04tags\x18\x03 \x03(\x0b\x32-.feast.registry.ListEntitiesRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"t\n\x14ListEntitiesResponse\x12$\n\x08\x65ntities\x18\x01 \x03(\x0b\x32\x12.feast.core.Entity\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"D\n\x13\x44\x65leteEntityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"f\n\x16\x41pplyDataSourceRequest\x12+\n\x0b\x64\x61ta_source\x18\x01 \x01(\x0b\x32\x16.feast.core.DataSource\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"J\n\x14GetDataSourceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x91\x02\n\x16ListDataSourcesRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12>\n\x04tags\x18\x03 \x03(\x0b\x32\x30.feast.registry.ListDataSourcesRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x7f\n\x17ListDataSourcesResponse\x12,\n\x0c\x64\x61ta_sources\x18\x01 \x03(\x0b\x32\x16.feast.core.DataSource\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"H\n\x17\x44\x65leteDataSourceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"\x81\x02\n\x17\x41pplyFeatureViewRequest\x12/\n\x0c\x66\x65\x61ture_view\x18\x01 \x01(\x0b\x32\x17.feast.core.FeatureViewH\x00\x12\x41\n\x16on_demand_feature_view\x18\x02 \x01(\x0b\x32\x1f.feast.core.OnDemandFeatureViewH\x00\x12<\n\x13stream_feature_view\x18\x03 \x01(\x0b\x32\x1d.feast.core.StreamFeatureViewH\x00\x12\x0f\n\x07project\x18\x04 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x05 \x01(\x08\x42\x13\n\x11\x62\x61se_feature_view\"K\n\x15GetFeatureViewRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x93\x02\n\x17ListFeatureViewsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12?\n\x04tags\x18\x03 \x03(\x0b\x32\x31.feast.registry.ListFeatureViewsRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x82\x01\n\x18ListFeatureViewsResponse\x12.\n\rfeature_views\x18\x01 \x03(\x0b\x32\x17.feast.core.FeatureView\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"I\n\x18\x44\x65leteFeatureViewRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"\xd6\x01\n\x0e\x41nyFeatureView\x12/\n\x0c\x66\x65\x61ture_view\x18\x01 \x01(\x0b\x32\x17.feast.core.FeatureViewH\x00\x12\x41\n\x16on_demand_feature_view\x18\x02 \x01(\x0b\x32\x1f.feast.core.OnDemandFeatureViewH\x00\x12<\n\x13stream_feature_view\x18\x03 \x01(\x0b\x32\x1d.feast.core.StreamFeatureViewH\x00\x42\x12\n\x10\x61ny_feature_view\"N\n\x18GetAnyFeatureViewRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"U\n\x19GetAnyFeatureViewResponse\x12\x38\n\x10\x61ny_feature_view\x18\x01 \x01(\x0b\x32\x1e.feast.registry.AnyFeatureView\"\x99\x02\n\x1aListAllFeatureViewsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12\x42\n\x04tags\x18\x03 \x03(\x0b\x32\x34.feast.registry.ListAllFeatureViewsRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x8c\x01\n\x1bListAllFeatureViewsResponse\x12\x35\n\rfeature_views\x18\x01 \x03(\x0b\x32\x1e.feast.registry.AnyFeatureView\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"Q\n\x1bGetStreamFeatureViewRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x9f\x02\n\x1dListStreamFeatureViewsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12\x45\n\x04tags\x18\x03 \x03(\x0b\x32\x37.feast.registry.ListStreamFeatureViewsRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x95\x01\n\x1eListStreamFeatureViewsResponse\x12;\n\x14stream_feature_views\x18\x01 \x03(\x0b\x32\x1d.feast.core.StreamFeatureView\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"S\n\x1dGetOnDemandFeatureViewRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\xa3\x02\n\x1fListOnDemandFeatureViewsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12G\n\x04tags\x18\x03 \x03(\x0b\x32\x39.feast.registry.ListOnDemandFeatureViewsRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x9c\x01\n ListOnDemandFeatureViewsResponse\x12@\n\x17on_demand_feature_views\x18\x01 \x03(\x0b\x32\x1f.feast.core.OnDemandFeatureView\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"r\n\x1a\x41pplyFeatureServiceRequest\x12\x33\n\x0f\x66\x65\x61ture_service\x18\x01 \x01(\x0b\x32\x1a.feast.core.FeatureService\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"N\n\x18GetFeatureServiceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x99\x02\n\x1aListFeatureServicesRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12\x42\n\x04tags\x18\x03 \x03(\x0b\x32\x34.feast.registry.ListFeatureServicesRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x8b\x01\n\x1bListFeatureServicesResponse\x12\x34\n\x10\x66\x65\x61ture_services\x18\x01 \x03(\x0b\x32\x1a.feast.core.FeatureService\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"L\n\x1b\x44\x65leteFeatureServiceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"l\n\x18\x41pplySavedDatasetRequest\x12/\n\rsaved_dataset\x18\x01 \x01(\x0b\x32\x18.feast.core.SavedDataset\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"L\n\x16GetSavedDatasetRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x95\x02\n\x18ListSavedDatasetsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12@\n\x04tags\x18\x03 \x03(\x0b\x32\x32.feast.registry.ListSavedDatasetsRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x85\x01\n\x19ListSavedDatasetsResponse\x12\x30\n\x0esaved_datasets\x18\x01 \x03(\x0b\x32\x18.feast.core.SavedDataset\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"J\n\x19\x44\x65leteSavedDatasetRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"\x81\x01\n\x1f\x41pplyValidationReferenceRequest\x12=\n\x14validation_reference\x18\x01 \x01(\x0b\x32\x1f.feast.core.ValidationReference\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"S\n\x1dGetValidationReferenceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\xa3\x02\n\x1fListValidationReferencesRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12G\n\x04tags\x18\x03 \x03(\x0b\x32\x39.feast.registry.ListValidationReferencesRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x9a\x01\n ListValidationReferencesResponse\x12>\n\x15validation_references\x18\x01 \x03(\x0b\x32\x1f.feast.core.ValidationReference\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"Q\n DeleteValidationReferenceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"e\n\x16\x41pplyPermissionRequest\x12*\n\npermission\x18\x01 \x01(\x0b\x32\x16.feast.core.Permission\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"J\n\x14GetPermissionRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x91\x02\n\x16ListPermissionsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12>\n\x04tags\x18\x03 \x03(\x0b\x32\x30.feast.registry.ListPermissionsRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"~\n\x17ListPermissionsResponse\x12+\n\x0bpermissions\x18\x01 \x03(\x0b\x32\x16.feast.core.Permission\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"H\n\x17\x44\x65letePermissionRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"K\n\x13\x41pplyProjectRequest\x12$\n\x07project\x18\x01 \x01(\x0b\x32\x13.feast.core.Project\x12\x0e\n\x06\x63ommit\x18\x02 \x01(\x08\"6\n\x11GetProjectRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\"\xfa\x01\n\x13ListProjectsRequest\x12\x13\n\x0b\x61llow_cache\x18\x01 \x01(\x08\x12;\n\x04tags\x18\x02 \x03(\x0b\x32-.feast.registry.ListProjectsRequest.TagsEntry\x12\x34\n\npagination\x18\x03 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x04 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"u\n\x14ListProjectsResponse\x12%\n\x08projects\x18\x01 \x03(\x0b\x32\x13.feast.core.Project\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"4\n\x14\x44\x65leteProjectRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x02 \x01(\x08\"-\n\x0f\x45ntityReference\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\"r\n\x0e\x45ntityRelation\x12/\n\x06source\x18\x01 \x01(\x0b\x32\x1f.feast.registry.EntityReference\x12/\n\x06target\x18\x02 \x01(\x0b\x32\x1f.feast.registry.EntityReference\"\xdf\x01\n\x19GetRegistryLineageRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12\x1a\n\x12\x66ilter_object_type\x18\x03 \x01(\t\x12\x1a\n\x12\x66ilter_object_name\x18\x04 \x01(\t\x12\x34\n\npagination\x18\x05 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x06 \x01(\x0b\x32\x1d.feast.registry.SortingParams\"\xa8\x02\n\x1aGetRegistryLineageResponse\x12\x35\n\rrelationships\x18\x01 \x03(\x0b\x32\x1e.feast.registry.EntityRelation\x12>\n\x16indirect_relationships\x18\x02 \x03(\x0b\x32\x1e.feast.registry.EntityRelation\x12\x44\n\x18relationships_pagination\x18\x03 \x01(\x0b\x32\".feast.registry.PaginationMetadata\x12M\n!indirect_relationships_pagination\x18\x04 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"\xef\x01\n\x1dGetObjectRelationshipsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0bobject_type\x18\x02 \x01(\t\x12\x13\n\x0bobject_name\x18\x03 \x01(\t\x12\x18\n\x10include_indirect\x18\x04 \x01(\x08\x12\x13\n\x0b\x61llow_cache\x18\x05 \x01(\x08\x12\x34\n\npagination\x18\x06 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x07 \x01(\x0b\x32\x1d.feast.registry.SortingParams\"\x8f\x01\n\x1eGetObjectRelationshipsResponse\x12\x35\n\rrelationships\x18\x01 \x03(\x0b\x32\x1e.feast.registry.EntityRelation\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata2\xb5\"\n\x0eRegistryServer\x12K\n\x0b\x41pplyEntity\x12\".feast.registry.ApplyEntityRequest\x1a\x16.google.protobuf.Empty\"\x00\x12\x43\n\tGetEntity\x12 .feast.registry.GetEntityRequest\x1a\x12.feast.core.Entity\"\x00\x12[\n\x0cListEntities\x12#.feast.registry.ListEntitiesRequest\x1a$.feast.registry.ListEntitiesResponse\"\x00\x12M\n\x0c\x44\x65leteEntity\x12#.feast.registry.DeleteEntityRequest\x1a\x16.google.protobuf.Empty\"\x00\x12S\n\x0f\x41pplyDataSource\x12&.feast.registry.ApplyDataSourceRequest\x1a\x16.google.protobuf.Empty\"\x00\x12O\n\rGetDataSource\x12$.feast.registry.GetDataSourceRequest\x1a\x16.feast.core.DataSource\"\x00\x12\x64\n\x0fListDataSources\x12&.feast.registry.ListDataSourcesRequest\x1a\'.feast.registry.ListDataSourcesResponse\"\x00\x12U\n\x10\x44\x65leteDataSource\x12\'.feast.registry.DeleteDataSourceRequest\x1a\x16.google.protobuf.Empty\"\x00\x12U\n\x10\x41pplyFeatureView\x12\'.feast.registry.ApplyFeatureViewRequest\x1a\x16.google.protobuf.Empty\"\x00\x12W\n\x11\x44\x65leteFeatureView\x12(.feast.registry.DeleteFeatureViewRequest\x1a\x16.google.protobuf.Empty\"\x00\x12j\n\x11GetAnyFeatureView\x12(.feast.registry.GetAnyFeatureViewRequest\x1a).feast.registry.GetAnyFeatureViewResponse\"\x00\x12p\n\x13ListAllFeatureViews\x12*.feast.registry.ListAllFeatureViewsRequest\x1a+.feast.registry.ListAllFeatureViewsResponse\"\x00\x12R\n\x0eGetFeatureView\x12%.feast.registry.GetFeatureViewRequest\x1a\x17.feast.core.FeatureView\"\x00\x12g\n\x10ListFeatureViews\x12\'.feast.registry.ListFeatureViewsRequest\x1a(.feast.registry.ListFeatureViewsResponse\"\x00\x12\x64\n\x14GetStreamFeatureView\x12+.feast.registry.GetStreamFeatureViewRequest\x1a\x1d.feast.core.StreamFeatureView\"\x00\x12y\n\x16ListStreamFeatureViews\x12-.feast.registry.ListStreamFeatureViewsRequest\x1a..feast.registry.ListStreamFeatureViewsResponse\"\x00\x12j\n\x16GetOnDemandFeatureView\x12-.feast.registry.GetOnDemandFeatureViewRequest\x1a\x1f.feast.core.OnDemandFeatureView\"\x00\x12\x7f\n\x18ListOnDemandFeatureViews\x12/.feast.registry.ListOnDemandFeatureViewsRequest\x1a\x30.feast.registry.ListOnDemandFeatureViewsResponse\"\x00\x12[\n\x13\x41pplyFeatureService\x12*.feast.registry.ApplyFeatureServiceRequest\x1a\x16.google.protobuf.Empty\"\x00\x12[\n\x11GetFeatureService\x12(.feast.registry.GetFeatureServiceRequest\x1a\x1a.feast.core.FeatureService\"\x00\x12p\n\x13ListFeatureServices\x12*.feast.registry.ListFeatureServicesRequest\x1a+.feast.registry.ListFeatureServicesResponse\"\x00\x12]\n\x14\x44\x65leteFeatureService\x12+.feast.registry.DeleteFeatureServiceRequest\x1a\x16.google.protobuf.Empty\"\x00\x12W\n\x11\x41pplySavedDataset\x12(.feast.registry.ApplySavedDatasetRequest\x1a\x16.google.protobuf.Empty\"\x00\x12U\n\x0fGetSavedDataset\x12&.feast.registry.GetSavedDatasetRequest\x1a\x18.feast.core.SavedDataset\"\x00\x12j\n\x11ListSavedDatasets\x12(.feast.registry.ListSavedDatasetsRequest\x1a).feast.registry.ListSavedDatasetsResponse\"\x00\x12Y\n\x12\x44\x65leteSavedDataset\x12).feast.registry.DeleteSavedDatasetRequest\x1a\x16.google.protobuf.Empty\"\x00\x12\x65\n\x18\x41pplyValidationReference\x12/.feast.registry.ApplyValidationReferenceRequest\x1a\x16.google.protobuf.Empty\"\x00\x12j\n\x16GetValidationReference\x12-.feast.registry.GetValidationReferenceRequest\x1a\x1f.feast.core.ValidationReference\"\x00\x12\x7f\n\x18ListValidationReferences\x12/.feast.registry.ListValidationReferencesRequest\x1a\x30.feast.registry.ListValidationReferencesResponse\"\x00\x12g\n\x19\x44\x65leteValidationReference\x12\x30.feast.registry.DeleteValidationReferenceRequest\x1a\x16.google.protobuf.Empty\"\x00\x12S\n\x0f\x41pplyPermission\x12&.feast.registry.ApplyPermissionRequest\x1a\x16.google.protobuf.Empty\"\x00\x12O\n\rGetPermission\x12$.feast.registry.GetPermissionRequest\x1a\x16.feast.core.Permission\"\x00\x12\x64\n\x0fListPermissions\x12&.feast.registry.ListPermissionsRequest\x1a\'.feast.registry.ListPermissionsResponse\"\x00\x12U\n\x10\x44\x65letePermission\x12\'.feast.registry.DeletePermissionRequest\x1a\x16.google.protobuf.Empty\"\x00\x12M\n\x0c\x41pplyProject\x12#.feast.registry.ApplyProjectRequest\x1a\x16.google.protobuf.Empty\"\x00\x12\x46\n\nGetProject\x12!.feast.registry.GetProjectRequest\x1a\x13.feast.core.Project\"\x00\x12[\n\x0cListProjects\x12#.feast.registry.ListProjectsRequest\x1a$.feast.registry.ListProjectsResponse\"\x00\x12O\n\rDeleteProject\x12$.feast.registry.DeleteProjectRequest\x1a\x16.google.protobuf.Empty\"\x00\x12]\n\x14\x41pplyMaterialization\x12+.feast.registry.ApplyMaterializationRequest\x1a\x16.google.protobuf.Empty\"\x00\x12p\n\x13ListProjectMetadata\x12*.feast.registry.ListProjectMetadataRequest\x1a+.feast.registry.ListProjectMetadataResponse\"\x00\x12K\n\x0bUpdateInfra\x12\".feast.registry.UpdateInfraRequest\x1a\x16.google.protobuf.Empty\"\x00\x12@\n\x08GetInfra\x12\x1f.feast.registry.GetInfraRequest\x1a\x11.feast.core.Infra\"\x00\x12:\n\x06\x43ommit\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12\x43\n\x07Refresh\x12\x1e.feast.registry.RefreshRequest\x1a\x16.google.protobuf.Empty\"\x00\x12\x37\n\x05Proto\x12\x16.google.protobuf.Empty\x1a\x14.feast.core.Registry\"\x00\x12m\n\x12GetRegistryLineage\x12).feast.registry.GetRegistryLineageRequest\x1a*.feast.registry.GetRegistryLineageResponse\"\x00\x12y\n\x16GetObjectRelationships\x12-.feast.registry.GetObjectRelationshipsRequest\x1a..feast.registry.GetObjectRelationshipsResponse\"\x00\x42\x35Z3github.com/feast-dev/feast/go/protos/feast/registryb\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#feast/registry/RegistryServer.proto\x12\x0e\x66\x65\x61st.registry\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x19\x66\x65\x61st/core/Registry.proto\x1a\x17\x66\x65\x61st/core/Entity.proto\x1a\x1b\x66\x65\x61st/core/DataSource.proto\x1a\x1c\x66\x65\x61st/core/FeatureView.proto\x1a\"feast/core/StreamFeatureView.proto\x1a$feast/core/OnDemandFeatureView.proto\x1a\x1f\x66\x65\x61st/core/FeatureService.proto\x1a\x1d\x66\x65\x61st/core/SavedDataset.proto\x1a\"feast/core/ValidationProfile.proto\x1a\x1c\x66\x65\x61st/core/InfraObject.proto\x1a\x1b\x66\x65\x61st/core/Permission.proto\x1a\x18\x66\x65\x61st/core/Project.proto\"/\n\x10PaginationParams\x12\x0c\n\x04page\x18\x01 \x01(\x05\x12\r\n\x05limit\x18\x02 \x01(\x05\"4\n\rSortingParams\x12\x0f\n\x07sort_by\x18\x01 \x01(\t\x12\x12\n\nsort_order\x18\x02 \x01(\t\"\x83\x01\n\x12PaginationMetadata\x12\x0c\n\x04page\x18\x01 \x01(\x05\x12\r\n\x05limit\x18\x02 \x01(\x05\x12\x13\n\x0btotal_count\x18\x03 \x01(\x05\x12\x13\n\x0btotal_pages\x18\x04 \x01(\x05\x12\x10\n\x08has_next\x18\x05 \x01(\x08\x12\x14\n\x0chas_previous\x18\x06 \x01(\x08\"!\n\x0eRefreshRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\"W\n\x12UpdateInfraRequest\x12 \n\x05infra\x18\x01 \x01(\x0b\x32\x11.feast.core.Infra\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"7\n\x0fGetInfraRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\"B\n\x1aListProjectMetadataRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\"T\n\x1bListProjectMetadataResponse\x12\x35\n\x10project_metadata\x18\x01 \x03(\x0b\x32\x1b.feast.core.ProjectMetadata\"\xcb\x01\n\x1b\x41pplyMaterializationRequest\x12-\n\x0c\x66\x65\x61ture_view\x18\x01 \x01(\x0b\x32\x17.feast.core.FeatureView\x12\x0f\n\x07project\x18\x02 \x01(\t\x12.\n\nstart_date\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08\x65nd_date\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06\x63ommit\x18\x05 \x01(\x08\"Y\n\x12\x41pplyEntityRequest\x12\"\n\x06\x65ntity\x18\x01 \x01(\x0b\x32\x12.feast.core.Entity\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"F\n\x10GetEntityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x8b\x02\n\x13ListEntitiesRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12;\n\x04tags\x18\x03 \x03(\x0b\x32-.feast.registry.ListEntitiesRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"t\n\x14ListEntitiesResponse\x12$\n\x08\x65ntities\x18\x01 \x03(\x0b\x32\x12.feast.core.Entity\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"D\n\x13\x44\x65leteEntityRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"f\n\x16\x41pplyDataSourceRequest\x12+\n\x0b\x64\x61ta_source\x18\x01 \x01(\x0b\x32\x16.feast.core.DataSource\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"J\n\x14GetDataSourceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x91\x02\n\x16ListDataSourcesRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12>\n\x04tags\x18\x03 \x03(\x0b\x32\x30.feast.registry.ListDataSourcesRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x7f\n\x17ListDataSourcesResponse\x12,\n\x0c\x64\x61ta_sources\x18\x01 \x03(\x0b\x32\x16.feast.core.DataSource\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"H\n\x17\x44\x65leteDataSourceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"\x81\x02\n\x17\x41pplyFeatureViewRequest\x12/\n\x0c\x66\x65\x61ture_view\x18\x01 \x01(\x0b\x32\x17.feast.core.FeatureViewH\x00\x12\x41\n\x16on_demand_feature_view\x18\x02 \x01(\x0b\x32\x1f.feast.core.OnDemandFeatureViewH\x00\x12<\n\x13stream_feature_view\x18\x03 \x01(\x0b\x32\x1d.feast.core.StreamFeatureViewH\x00\x12\x0f\n\x07project\x18\x04 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x05 \x01(\x08\x42\x13\n\x11\x62\x61se_feature_view\"K\n\x15GetFeatureViewRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x93\x02\n\x17ListFeatureViewsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12?\n\x04tags\x18\x03 \x03(\x0b\x32\x31.feast.registry.ListFeatureViewsRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x82\x01\n\x18ListFeatureViewsResponse\x12.\n\rfeature_views\x18\x01 \x03(\x0b\x32\x17.feast.core.FeatureView\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"I\n\x18\x44\x65leteFeatureViewRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"\xd6\x01\n\x0e\x41nyFeatureView\x12/\n\x0c\x66\x65\x61ture_view\x18\x01 \x01(\x0b\x32\x17.feast.core.FeatureViewH\x00\x12\x41\n\x16on_demand_feature_view\x18\x02 \x01(\x0b\x32\x1f.feast.core.OnDemandFeatureViewH\x00\x12<\n\x13stream_feature_view\x18\x03 \x01(\x0b\x32\x1d.feast.core.StreamFeatureViewH\x00\x42\x12\n\x10\x61ny_feature_view\"N\n\x18GetAnyFeatureViewRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"U\n\x19GetAnyFeatureViewResponse\x12\x38\n\x10\x61ny_feature_view\x18\x01 \x01(\x0b\x32\x1e.feast.registry.AnyFeatureView\"\x99\x02\n\x1aListAllFeatureViewsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12\x42\n\x04tags\x18\x03 \x03(\x0b\x32\x34.feast.registry.ListAllFeatureViewsRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x8c\x01\n\x1bListAllFeatureViewsResponse\x12\x35\n\rfeature_views\x18\x01 \x03(\x0b\x32\x1e.feast.registry.AnyFeatureView\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"Q\n\x1bGetStreamFeatureViewRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x9f\x02\n\x1dListStreamFeatureViewsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12\x45\n\x04tags\x18\x03 \x03(\x0b\x32\x37.feast.registry.ListStreamFeatureViewsRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x95\x01\n\x1eListStreamFeatureViewsResponse\x12;\n\x14stream_feature_views\x18\x01 \x03(\x0b\x32\x1d.feast.core.StreamFeatureView\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"S\n\x1dGetOnDemandFeatureViewRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\xa3\x02\n\x1fListOnDemandFeatureViewsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12G\n\x04tags\x18\x03 \x03(\x0b\x32\x39.feast.registry.ListOnDemandFeatureViewsRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x9c\x01\n ListOnDemandFeatureViewsResponse\x12@\n\x17on_demand_feature_views\x18\x01 \x03(\x0b\x32\x1f.feast.core.OnDemandFeatureView\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"r\n\x1a\x41pplyFeatureServiceRequest\x12\x33\n\x0f\x66\x65\x61ture_service\x18\x01 \x01(\x0b\x32\x1a.feast.core.FeatureService\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"N\n\x18GetFeatureServiceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x99\x02\n\x1aListFeatureServicesRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12\x42\n\x04tags\x18\x03 \x03(\x0b\x32\x34.feast.registry.ListFeatureServicesRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x8b\x01\n\x1bListFeatureServicesResponse\x12\x34\n\x10\x66\x65\x61ture_services\x18\x01 \x03(\x0b\x32\x1a.feast.core.FeatureService\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"L\n\x1b\x44\x65leteFeatureServiceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"l\n\x18\x41pplySavedDatasetRequest\x12/\n\rsaved_dataset\x18\x01 \x01(\x0b\x32\x18.feast.core.SavedDataset\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"L\n\x16GetSavedDatasetRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x95\x02\n\x18ListSavedDatasetsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12@\n\x04tags\x18\x03 \x03(\x0b\x32\x32.feast.registry.ListSavedDatasetsRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x85\x01\n\x19ListSavedDatasetsResponse\x12\x30\n\x0esaved_datasets\x18\x01 \x03(\x0b\x32\x18.feast.core.SavedDataset\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"J\n\x19\x44\x65leteSavedDatasetRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"\x81\x01\n\x1f\x41pplyValidationReferenceRequest\x12=\n\x14validation_reference\x18\x01 \x01(\x0b\x32\x1f.feast.core.ValidationReference\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"S\n\x1dGetValidationReferenceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\xa3\x02\n\x1fListValidationReferencesRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12G\n\x04tags\x18\x03 \x03(\x0b\x32\x39.feast.registry.ListValidationReferencesRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x9a\x01\n ListValidationReferencesResponse\x12>\n\x15validation_references\x18\x01 \x03(\x0b\x32\x1f.feast.core.ValidationReference\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"Q\n DeleteValidationReferenceRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"e\n\x16\x41pplyPermissionRequest\x12*\n\npermission\x18\x01 \x01(\x0b\x32\x16.feast.core.Permission\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"J\n\x14GetPermissionRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x03 \x01(\x08\"\x91\x02\n\x16ListPermissionsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12>\n\x04tags\x18\x03 \x03(\x0b\x32\x30.feast.registry.ListPermissionsRequest.TagsEntry\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"~\n\x17ListPermissionsResponse\x12+\n\x0bpermissions\x18\x01 \x03(\x0b\x32\x16.feast.core.Permission\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"H\n\x17\x44\x65letePermissionRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07project\x18\x02 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x03 \x01(\x08\"K\n\x13\x41pplyProjectRequest\x12$\n\x07project\x18\x01 \x01(\x0b\x32\x13.feast.core.Project\x12\x0e\n\x06\x63ommit\x18\x02 \x01(\x08\"6\n\x11GetProjectRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\"\xfa\x01\n\x13ListProjectsRequest\x12\x13\n\x0b\x61llow_cache\x18\x01 \x01(\x08\x12;\n\x04tags\x18\x02 \x03(\x0b\x32-.feast.registry.ListProjectsRequest.TagsEntry\x12\x34\n\npagination\x18\x03 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x04 \x01(\x0b\x32\x1d.feast.registry.SortingParams\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"u\n\x14ListProjectsResponse\x12%\n\x08projects\x18\x01 \x03(\x0b\x32\x13.feast.core.Project\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"4\n\x14\x44\x65leteProjectRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x63ommit\x18\x02 \x01(\x08\"-\n\x0f\x45ntityReference\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\"r\n\x0e\x45ntityRelation\x12/\n\x06source\x18\x01 \x01(\x0b\x32\x1f.feast.registry.EntityReference\x12/\n\x06target\x18\x02 \x01(\x0b\x32\x1f.feast.registry.EntityReference\"\xdf\x01\n\x19GetRegistryLineageRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x02 \x01(\x08\x12\x1a\n\x12\x66ilter_object_type\x18\x03 \x01(\t\x12\x1a\n\x12\x66ilter_object_name\x18\x04 \x01(\t\x12\x34\n\npagination\x18\x05 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x06 \x01(\x0b\x32\x1d.feast.registry.SortingParams\"\xa8\x02\n\x1aGetRegistryLineageResponse\x12\x35\n\rrelationships\x18\x01 \x03(\x0b\x32\x1e.feast.registry.EntityRelation\x12>\n\x16indirect_relationships\x18\x02 \x03(\x0b\x32\x1e.feast.registry.EntityRelation\x12\x44\n\x18relationships_pagination\x18\x03 \x01(\x0b\x32\".feast.registry.PaginationMetadata\x12M\n!indirect_relationships_pagination\x18\x04 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"\xef\x01\n\x1dGetObjectRelationshipsRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x13\n\x0bobject_type\x18\x02 \x01(\t\x12\x13\n\x0bobject_name\x18\x03 \x01(\t\x12\x18\n\x10include_indirect\x18\x04 \x01(\x08\x12\x13\n\x0b\x61llow_cache\x18\x05 \x01(\x08\x12\x34\n\npagination\x18\x06 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x07 \x01(\x0b\x32\x1d.feast.registry.SortingParams\"\x8f\x01\n\x1eGetObjectRelationshipsResponse\x12\x35\n\rrelationships\x18\x01 \x03(\x0b\x32\x1e.feast.registry.EntityRelation\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"\xae\x01\n\x07\x46\x65\x61ture\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x14\n\x0c\x66\x65\x61ture_view\x18\x02 \x01(\t\x12\x0c\n\x04type\x18\x03 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x04 \x01(\t\x12/\n\x04tags\x18\x07 \x03(\x0b\x32!.feast.registry.Feature.TagsEntry\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xc5\x01\n\x13ListFeaturesRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x14\n\x0c\x66\x65\x61ture_view\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x13\n\x0b\x61llow_cache\x18\x06 \x01(\x08\x12\x34\n\npagination\x18\x04 \x01(\x0b\x32 .feast.registry.PaginationParams\x12.\n\x07sorting\x18\x05 \x01(\x0b\x32\x1d.feast.registry.SortingParams\"y\n\x14ListFeaturesResponse\x12)\n\x08\x66\x65\x61tures\x18\x01 \x03(\x0b\x32\x17.feast.registry.Feature\x12\x36\n\npagination\x18\x02 \x01(\x0b\x32\".feast.registry.PaginationMetadata\"H\n\x11GetFeatureRequest\x12\x0f\n\x07project\x18\x01 \x01(\t\x12\x14\n\x0c\x66\x65\x61ture_view\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t2\xde#\n\x0eRegistryServer\x12K\n\x0b\x41pplyEntity\x12\".feast.registry.ApplyEntityRequest\x1a\x16.google.protobuf.Empty\"\x00\x12\x43\n\tGetEntity\x12 .feast.registry.GetEntityRequest\x1a\x12.feast.core.Entity\"\x00\x12[\n\x0cListEntities\x12#.feast.registry.ListEntitiesRequest\x1a$.feast.registry.ListEntitiesResponse\"\x00\x12M\n\x0c\x44\x65leteEntity\x12#.feast.registry.DeleteEntityRequest\x1a\x16.google.protobuf.Empty\"\x00\x12S\n\x0f\x41pplyDataSource\x12&.feast.registry.ApplyDataSourceRequest\x1a\x16.google.protobuf.Empty\"\x00\x12O\n\rGetDataSource\x12$.feast.registry.GetDataSourceRequest\x1a\x16.feast.core.DataSource\"\x00\x12\x64\n\x0fListDataSources\x12&.feast.registry.ListDataSourcesRequest\x1a\'.feast.registry.ListDataSourcesResponse\"\x00\x12U\n\x10\x44\x65leteDataSource\x12\'.feast.registry.DeleteDataSourceRequest\x1a\x16.google.protobuf.Empty\"\x00\x12U\n\x10\x41pplyFeatureView\x12\'.feast.registry.ApplyFeatureViewRequest\x1a\x16.google.protobuf.Empty\"\x00\x12W\n\x11\x44\x65leteFeatureView\x12(.feast.registry.DeleteFeatureViewRequest\x1a\x16.google.protobuf.Empty\"\x00\x12j\n\x11GetAnyFeatureView\x12(.feast.registry.GetAnyFeatureViewRequest\x1a).feast.registry.GetAnyFeatureViewResponse\"\x00\x12p\n\x13ListAllFeatureViews\x12*.feast.registry.ListAllFeatureViewsRequest\x1a+.feast.registry.ListAllFeatureViewsResponse\"\x00\x12R\n\x0eGetFeatureView\x12%.feast.registry.GetFeatureViewRequest\x1a\x17.feast.core.FeatureView\"\x00\x12g\n\x10ListFeatureViews\x12\'.feast.registry.ListFeatureViewsRequest\x1a(.feast.registry.ListFeatureViewsResponse\"\x00\x12\x64\n\x14GetStreamFeatureView\x12+.feast.registry.GetStreamFeatureViewRequest\x1a\x1d.feast.core.StreamFeatureView\"\x00\x12y\n\x16ListStreamFeatureViews\x12-.feast.registry.ListStreamFeatureViewsRequest\x1a..feast.registry.ListStreamFeatureViewsResponse\"\x00\x12j\n\x16GetOnDemandFeatureView\x12-.feast.registry.GetOnDemandFeatureViewRequest\x1a\x1f.feast.core.OnDemandFeatureView\"\x00\x12\x7f\n\x18ListOnDemandFeatureViews\x12/.feast.registry.ListOnDemandFeatureViewsRequest\x1a\x30.feast.registry.ListOnDemandFeatureViewsResponse\"\x00\x12[\n\x13\x41pplyFeatureService\x12*.feast.registry.ApplyFeatureServiceRequest\x1a\x16.google.protobuf.Empty\"\x00\x12[\n\x11GetFeatureService\x12(.feast.registry.GetFeatureServiceRequest\x1a\x1a.feast.core.FeatureService\"\x00\x12p\n\x13ListFeatureServices\x12*.feast.registry.ListFeatureServicesRequest\x1a+.feast.registry.ListFeatureServicesResponse\"\x00\x12]\n\x14\x44\x65leteFeatureService\x12+.feast.registry.DeleteFeatureServiceRequest\x1a\x16.google.protobuf.Empty\"\x00\x12W\n\x11\x41pplySavedDataset\x12(.feast.registry.ApplySavedDatasetRequest\x1a\x16.google.protobuf.Empty\"\x00\x12U\n\x0fGetSavedDataset\x12&.feast.registry.GetSavedDatasetRequest\x1a\x18.feast.core.SavedDataset\"\x00\x12j\n\x11ListSavedDatasets\x12(.feast.registry.ListSavedDatasetsRequest\x1a).feast.registry.ListSavedDatasetsResponse\"\x00\x12Y\n\x12\x44\x65leteSavedDataset\x12).feast.registry.DeleteSavedDatasetRequest\x1a\x16.google.protobuf.Empty\"\x00\x12\x65\n\x18\x41pplyValidationReference\x12/.feast.registry.ApplyValidationReferenceRequest\x1a\x16.google.protobuf.Empty\"\x00\x12j\n\x16GetValidationReference\x12-.feast.registry.GetValidationReferenceRequest\x1a\x1f.feast.core.ValidationReference\"\x00\x12\x7f\n\x18ListValidationReferences\x12/.feast.registry.ListValidationReferencesRequest\x1a\x30.feast.registry.ListValidationReferencesResponse\"\x00\x12g\n\x19\x44\x65leteValidationReference\x12\x30.feast.registry.DeleteValidationReferenceRequest\x1a\x16.google.protobuf.Empty\"\x00\x12S\n\x0f\x41pplyPermission\x12&.feast.registry.ApplyPermissionRequest\x1a\x16.google.protobuf.Empty\"\x00\x12O\n\rGetPermission\x12$.feast.registry.GetPermissionRequest\x1a\x16.feast.core.Permission\"\x00\x12\x64\n\x0fListPermissions\x12&.feast.registry.ListPermissionsRequest\x1a\'.feast.registry.ListPermissionsResponse\"\x00\x12U\n\x10\x44\x65letePermission\x12\'.feast.registry.DeletePermissionRequest\x1a\x16.google.protobuf.Empty\"\x00\x12M\n\x0c\x41pplyProject\x12#.feast.registry.ApplyProjectRequest\x1a\x16.google.protobuf.Empty\"\x00\x12\x46\n\nGetProject\x12!.feast.registry.GetProjectRequest\x1a\x13.feast.core.Project\"\x00\x12[\n\x0cListProjects\x12#.feast.registry.ListProjectsRequest\x1a$.feast.registry.ListProjectsResponse\"\x00\x12O\n\rDeleteProject\x12$.feast.registry.DeleteProjectRequest\x1a\x16.google.protobuf.Empty\"\x00\x12]\n\x14\x41pplyMaterialization\x12+.feast.registry.ApplyMaterializationRequest\x1a\x16.google.protobuf.Empty\"\x00\x12p\n\x13ListProjectMetadata\x12*.feast.registry.ListProjectMetadataRequest\x1a+.feast.registry.ListProjectMetadataResponse\"\x00\x12K\n\x0bUpdateInfra\x12\".feast.registry.UpdateInfraRequest\x1a\x16.google.protobuf.Empty\"\x00\x12@\n\x08GetInfra\x12\x1f.feast.registry.GetInfraRequest\x1a\x11.feast.core.Infra\"\x00\x12:\n\x06\x43ommit\x12\x16.google.protobuf.Empty\x1a\x16.google.protobuf.Empty\"\x00\x12\x43\n\x07Refresh\x12\x1e.feast.registry.RefreshRequest\x1a\x16.google.protobuf.Empty\"\x00\x12\x37\n\x05Proto\x12\x16.google.protobuf.Empty\x1a\x14.feast.core.Registry\"\x00\x12m\n\x12GetRegistryLineage\x12).feast.registry.GetRegistryLineageRequest\x1a*.feast.registry.GetRegistryLineageResponse\"\x00\x12y\n\x16GetObjectRelationships\x12-.feast.registry.GetObjectRelationshipsRequest\x1a..feast.registry.GetObjectRelationshipsResponse\"\x00\x12[\n\x0cListFeatures\x12#.feast.registry.ListFeaturesRequest\x1a$.feast.registry.ListFeaturesResponse\"\x00\x12J\n\nGetFeature\x12!.feast.registry.GetFeatureRequest\x1a\x17.feast.registry.Feature\"\x00\x42\x35Z3github.com/feast-dev/feast/go/protos/feast/registryb\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -58,6 +58,8 @@ _globals['_LISTPERMISSIONSREQUEST_TAGSENTRY']._serialized_options = b'8\001' _globals['_LISTPROJECTSREQUEST_TAGSENTRY']._options = None _globals['_LISTPROJECTSREQUEST_TAGSENTRY']._serialized_options = b'8\001' + _globals['_FEATURE_TAGSENTRY']._options = None + _globals['_FEATURE_TAGSENTRY']._serialized_options = b'8\001' _globals['_PAGINATIONPARAMS']._serialized_start=487 _globals['_PAGINATIONPARAMS']._serialized_end=534 _globals['_SORTINGPARAMS']._serialized_start=536 @@ -212,6 +214,16 @@ _globals['_GETOBJECTRELATIONSHIPSREQUEST']._serialized_end=9517 _globals['_GETOBJECTRELATIONSHIPSRESPONSE']._serialized_start=9520 _globals['_GETOBJECTRELATIONSHIPSRESPONSE']._serialized_end=9663 - _globals['_REGISTRYSERVER']._serialized_start=9666 - _globals['_REGISTRYSERVER']._serialized_end=14071 + _globals['_FEATURE']._serialized_start=9666 + _globals['_FEATURE']._serialized_end=9840 + _globals['_FEATURE_TAGSENTRY']._serialized_start=1653 + _globals['_FEATURE_TAGSENTRY']._serialized_end=1696 + _globals['_LISTFEATURESREQUEST']._serialized_start=9843 + _globals['_LISTFEATURESREQUEST']._serialized_end=10040 + _globals['_LISTFEATURESRESPONSE']._serialized_start=10042 + _globals['_LISTFEATURESRESPONSE']._serialized_end=10163 + _globals['_GETFEATUREREQUEST']._serialized_start=10165 + _globals['_GETFEATUREREQUEST']._serialized_end=10237 + _globals['_REGISTRYSERVER']._serialized_start=10240 + _globals['_REGISTRYSERVER']._serialized_end=14814 # @@protoc_insertion_point(module_scope) diff --git a/sdk/python/feast/protos/feast/registry/RegistryServer_pb2.pyi b/sdk/python/feast/protos/feast/registry/RegistryServer_pb2.pyi index ef4bdac6607..8784123607f 100644 --- a/sdk/python/feast/protos/feast/registry/RegistryServer_pb2.pyi +++ b/sdk/python/feast/protos/feast/registry/RegistryServer_pb2.pyi @@ -1694,3 +1694,119 @@ class GetObjectRelationshipsResponse(google.protobuf.message.Message): def ClearField(self, field_name: typing_extensions.Literal["pagination", b"pagination", "relationships", b"relationships"]) -> None: ... global___GetObjectRelationshipsResponse = GetObjectRelationshipsResponse + +class Feature(google.protobuf.message.Message): + """Feature messages""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class TagsEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str = ..., + value: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + + NAME_FIELD_NUMBER: builtins.int + FEATURE_VIEW_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + DESCRIPTION_FIELD_NUMBER: builtins.int + TAGS_FIELD_NUMBER: builtins.int + name: builtins.str + feature_view: builtins.str + type: builtins.str + description: builtins.str + @property + def tags(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: ... + def __init__( + self, + *, + name: builtins.str = ..., + feature_view: builtins.str = ..., + type: builtins.str = ..., + description: builtins.str = ..., + tags: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["description", b"description", "feature_view", b"feature_view", "name", b"name", "tags", b"tags", "type", b"type"]) -> None: ... + +global___Feature = Feature + +class ListFeaturesRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PROJECT_FIELD_NUMBER: builtins.int + FEATURE_VIEW_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + ALLOW_CACHE_FIELD_NUMBER: builtins.int + PAGINATION_FIELD_NUMBER: builtins.int + SORTING_FIELD_NUMBER: builtins.int + project: builtins.str + feature_view: builtins.str + name: builtins.str + allow_cache: builtins.bool + @property + def pagination(self) -> global___PaginationParams: ... + @property + def sorting(self) -> global___SortingParams: ... + def __init__( + self, + *, + project: builtins.str = ..., + feature_view: builtins.str = ..., + name: builtins.str = ..., + allow_cache: builtins.bool = ..., + pagination: global___PaginationParams | None = ..., + sorting: global___SortingParams | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["pagination", b"pagination", "sorting", b"sorting"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["allow_cache", b"allow_cache", "feature_view", b"feature_view", "name", b"name", "pagination", b"pagination", "project", b"project", "sorting", b"sorting"]) -> None: ... + +global___ListFeaturesRequest = ListFeaturesRequest + +class ListFeaturesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + FEATURES_FIELD_NUMBER: builtins.int + PAGINATION_FIELD_NUMBER: builtins.int + @property + def features(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Feature]: ... + @property + def pagination(self) -> global___PaginationMetadata: ... + def __init__( + self, + *, + features: collections.abc.Iterable[global___Feature] | None = ..., + pagination: global___PaginationMetadata | None = ..., + ) -> None: ... + def HasField(self, field_name: typing_extensions.Literal["pagination", b"pagination"]) -> builtins.bool: ... + def ClearField(self, field_name: typing_extensions.Literal["features", b"features", "pagination", b"pagination"]) -> None: ... + +global___ListFeaturesResponse = ListFeaturesResponse + +class GetFeatureRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PROJECT_FIELD_NUMBER: builtins.int + FEATURE_VIEW_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + project: builtins.str + feature_view: builtins.str + name: builtins.str + def __init__( + self, + *, + project: builtins.str = ..., + feature_view: builtins.str = ..., + name: builtins.str = ..., + ) -> None: ... + def ClearField(self, field_name: typing_extensions.Literal["feature_view", b"feature_view", "name", b"name", "project", b"project"]) -> None: ... + +global___GetFeatureRequest = GetFeatureRequest diff --git a/sdk/python/feast/protos/feast/registry/RegistryServer_pb2_grpc.py b/sdk/python/feast/protos/feast/registry/RegistryServer_pb2_grpc.py index a0a8d89b9d6..84de666b6b1 100644 --- a/sdk/python/feast/protos/feast/registry/RegistryServer_pb2_grpc.py +++ b/sdk/python/feast/protos/feast/registry/RegistryServer_pb2_grpc.py @@ -262,6 +262,16 @@ def __init__(self, channel): request_serializer=feast_dot_registry_dot_RegistryServer__pb2.GetObjectRelationshipsRequest.SerializeToString, response_deserializer=feast_dot_registry_dot_RegistryServer__pb2.GetObjectRelationshipsResponse.FromString, ) + self.ListFeatures = channel.unary_unary( + '/feast.registry.RegistryServer/ListFeatures', + request_serializer=feast_dot_registry_dot_RegistryServer__pb2.ListFeaturesRequest.SerializeToString, + response_deserializer=feast_dot_registry_dot_RegistryServer__pb2.ListFeaturesResponse.FromString, + ) + self.GetFeature = channel.unary_unary( + '/feast.registry.RegistryServer/GetFeature', + request_serializer=feast_dot_registry_dot_RegistryServer__pb2.GetFeatureRequest.SerializeToString, + response_deserializer=feast_dot_registry_dot_RegistryServer__pb2.Feature.FromString, + ) class RegistryServerServicer(object): @@ -561,6 +571,19 @@ def GetObjectRelationships(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def ListFeatures(self, request, context): + """Feature RPCs + """ + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def GetFeature(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def add_RegistryServerServicer_to_server(servicer, server): rpc_method_handlers = { @@ -799,6 +822,16 @@ def add_RegistryServerServicer_to_server(servicer, server): request_deserializer=feast_dot_registry_dot_RegistryServer__pb2.GetObjectRelationshipsRequest.FromString, response_serializer=feast_dot_registry_dot_RegistryServer__pb2.GetObjectRelationshipsResponse.SerializeToString, ), + 'ListFeatures': grpc.unary_unary_rpc_method_handler( + servicer.ListFeatures, + request_deserializer=feast_dot_registry_dot_RegistryServer__pb2.ListFeaturesRequest.FromString, + response_serializer=feast_dot_registry_dot_RegistryServer__pb2.ListFeaturesResponse.SerializeToString, + ), + 'GetFeature': grpc.unary_unary_rpc_method_handler( + servicer.GetFeature, + request_deserializer=feast_dot_registry_dot_RegistryServer__pb2.GetFeatureRequest.FromString, + response_serializer=feast_dot_registry_dot_RegistryServer__pb2.Feature.SerializeToString, + ), } generic_handler = grpc.method_handlers_generic_handler( 'feast.registry.RegistryServer', rpc_method_handlers) @@ -1607,3 +1640,37 @@ def GetObjectRelationships(request, feast_dot_registry_dot_RegistryServer__pb2.GetObjectRelationshipsResponse.FromString, options, channel_credentials, insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ListFeatures(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/feast.registry.RegistryServer/ListFeatures', + feast_dot_registry_dot_RegistryServer__pb2.ListFeaturesRequest.SerializeToString, + feast_dot_registry_dot_RegistryServer__pb2.ListFeaturesResponse.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def GetFeature(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/feast.registry.RegistryServer/GetFeature', + feast_dot_registry_dot_RegistryServer__pb2.GetFeatureRequest.SerializeToString, + feast_dot_registry_dot_RegistryServer__pb2.Feature.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/sdk/python/feast/registry_server.py b/sdk/python/feast/registry_server.py index 56c3eb5a37d..f3e73ab4f02 100644 --- a/sdk/python/feast/registry_server.py +++ b/sdk/python/feast/registry_server.py @@ -36,6 +36,7 @@ ) from feast.project import Project from feast.protos.feast.registry import RegistryServer_pb2, RegistryServer_pb2_grpc +from feast.protos.feast.registry.RegistryServer_pb2 import Feature, ListFeaturesResponse from feast.saved_dataset import SavedDataset, ValidationReference from feast.stream_feature_view import StreamFeatureView @@ -989,6 +990,77 @@ def Refresh(self, request, context): def Proto(self, request, context): return self.proxied_registry.proto() + def ListFeatures(self, request: RegistryServer_pb2.ListFeaturesRequest, context): + """ + List all features in the registry, optionally filtered by project, feature_view, or name. + """ + allow_cache = request.allow_cache if hasattr(request, "allow_cache") else True + feature_views = self.proxied_registry.list_all_feature_views( + project=request.project, + allow_cache=allow_cache, + ) + permitted_fvs = permitted_resources( + resources=cast(list[FeastObject], feature_views), + actions=AuthzedAction.DESCRIBE, + ) + features = [] + for fv in permitted_fvs: + fv_name = getattr(fv, "name", None) + for feature in getattr(fv, "features", []): + if request.feature_view and fv_name != request.feature_view: + continue + if request.name and feature.name != request.name: + continue + features.append( + Feature( + name=feature.name, + feature_view=fv_name if fv_name is not None else "", + type=str(feature.dtype) + if hasattr(feature, "dtype") + else str(feature.valueType), + description=getattr(feature, "description", ""), + tags=getattr(feature, "tags", {}), + ) + ) + paginated_features, pagination_metadata = apply_pagination_and_sorting( + features, + pagination=request.pagination if request.HasField("pagination") else None, + sorting=request.sorting if request.HasField("sorting") else None, + ) + return ListFeaturesResponse( + features=paginated_features, pagination=pagination_metadata + ) + + def GetFeature(self, request: RegistryServer_pb2.GetFeatureRequest, context): + """ + Get a single feature by project, feature_view, and name. + """ + allow_cache = getattr(request, "allow_cache", True) + feature_views = self.proxied_registry.list_all_feature_views( + project=request.project, + allow_cache=allow_cache, + ) + permitted_fvs = permitted_resources( + resources=cast(list[FeastObject], feature_views), + actions=AuthzedAction.DESCRIBE, + ) + for fv in permitted_fvs: + fv_name = getattr(fv, "name", None) + for feature in getattr(fv, "features", []): + if fv_name == request.feature_view and feature.name == request.name: + return Feature( + name=feature.name, + feature_view=fv_name if fv_name is not None else "", + type=str(feature.dtype) + if hasattr(feature, "dtype") + else str(feature.valueType), + description=getattr(feature, "description", ""), + tags=getattr(feature, "tags", {}), + ) + context.set_code(grpc.StatusCode.NOT_FOUND) + context.set_details("Feature not found") + return Feature() + def start_server( store: FeatureStore, diff --git a/sdk/python/tests/unit/api/test_api_rest_registry.py b/sdk/python/tests/unit/api/test_api_rest_registry.py index 6ac7018eb6a..2748994ba4e 100644 --- a/sdk/python/tests/unit/api/test_api_rest_registry.py +++ b/sdk/python/tests/unit/api/test_api_rest_registry.py @@ -841,6 +841,131 @@ def test_lineage_sorting_via_rest(fastapi_test_app_with_multiple_objects): assert "relationships" in data +def test_features_list_via_rest(fastapi_test_app): + """Test the /features endpoint (list features in a project).""" + response = fastapi_test_app.get("/features?project=demo_project") + assert response.status_code == 200 + data = response.json() + assert "features" in data + assert "pagination" in data + for feature in data["features"]: + assert "name" in feature + assert "featureView" in feature + assert "type" in feature + + pagination = data["pagination"] + assert "totalCount" in pagination + assert "totalPages" in pagination + + +def test_features_list_with_relationships_via_rest(fastapi_test_app): + """Test the /features endpoint with include_relationships.""" + response = fastapi_test_app.get( + "/features?project=demo_project&include_relationships=true" + ) + assert response.status_code == 200 + data = response.json() + assert "features" in data + assert "relationships" in data + assert isinstance(data["relationships"], dict) + for k, v in data["relationships"].items(): + assert isinstance(v, list) + for rel in v: + assert "source" in rel and "target" in rel + + +def test_features_get_via_rest(fastapi_test_app): + """Test the /features/{feature_view}/{name} endpoint (get single feature).""" + response = fastapi_test_app.get("/features/user_profile/age?project=demo_project") + assert response.status_code == 200 + data = response.json() + assert data["name"] == "age" + assert data["featureView"] == "user_profile" + assert data["type"] == "Int64" + + response = fastapi_test_app.get( + "/features/user_profile/age?project=demo_project&include_relationships=true" + ) + assert response.status_code == 200 + data = response.json() + assert "relationships" in data + assert isinstance(data["relationships"], list) + for rel in data["relationships"]: + assert "source" in rel and "target" in rel + + +def test_features_list_all_via_rest(fastapi_test_app): + """Test the /features/all endpoint (all projects).""" + response = fastapi_test_app.get("/features/all") + assert response.status_code == 200 + data = response.json() + assert "features" in data + assert "pagination" in data + for feature in data["features"]: + assert "project" in feature + assert "name" in feature + assert "featureView" in feature + assert "type" in feature + + +def test_features_filtering_and_sorting_via_rest(fastapi_test_app): + """Test filtering and sorting for /features endpoint.""" + response = fastapi_test_app.get( + "/features?project=demo_project&feature_view=user_profile" + ) + assert response.status_code == 200 + data = response.json() + for feature in data["features"]: + assert feature["featureView"] == "user_profile" + + response = fastapi_test_app.get("/features?project=demo_project&name=age") + assert response.status_code == 200 + data = response.json() + for feature in data["features"]: + assert feature["name"] == "age" + + response = fastapi_test_app.get( + "/features?project=demo_project&sort_by=name&sort_order=asc" + ) + assert response.status_code == 200 + data = response.json() + names = [f["name"] for f in data["features"]] + assert names == sorted(names) + + +def test_features_pagination_via_rest(fastapi_test_app_with_multiple_objects): + """Test pagination for /features endpoint.""" + client = fastapi_test_app_with_multiple_objects + response = client.get("/features?project=demo_project&page=1&limit=2") + assert response.status_code == 200 + data = response.json() + assert "features" in data + assert "pagination" in data + assert data["pagination"]["page"] == 1 + assert data["pagination"]["limit"] == 2 + + response = client.get("/features?project=demo_project&page=2&limit=2") + assert response.status_code == 200 + data = response.json() + assert data["pagination"]["page"] == 2 + + +def test_lineage_features_object_type_via_rest(fastapi_test_app): + """Test lineage endpoints for features as a first-class object.""" + response = fastapi_test_app.get("/lineage/objects/feature/age?project=demo_project") + assert response.status_code == 200 + data = response.json() + assert "relationships" in data + assert isinstance(data["relationships"], list) + response = fastapi_test_app.get( + "/lineage/registry?project=demo_project&filter_object_type=feature" + ) + assert response.status_code == 200 + data = response.json() + assert "relationships" in data + assert isinstance(data["relationships"], list) + + def test_feature_view_type_identification(): """Test that we can properly identify feature view types from their structure.""" from feast.api.registry.rest.feature_views import _extract_feature_view_from_any diff --git a/sdk/python/tests/unit/api/test_api_rest_registry_server.py b/sdk/python/tests/unit/api/test_api_rest_registry_server.py index 2d1ca6e29f0..8124a46bf75 100644 --- a/sdk/python/tests/unit/api/test_api_rest_registry_server.py +++ b/sdk/python/tests/unit/api/test_api_rest_registry_server.py @@ -77,3 +77,6 @@ def test_routes_registered_in_app(mock_store_and_registry): assert "/saved_datasets/all" in route_paths assert "/lineage/registry/all" in route_paths assert "/lineage/complete/all" in route_paths + assert "/features" in route_paths + assert "/features/all" in route_paths + assert "/features/{feature_view}/{name}" in route_paths