From c87ab2a925f302bdf4ea963b4eff43aec6e17cd4 Mon Sep 17 00:00:00 2001 From: Shuchu Han Date: Mon, 22 Sep 2025 22:46:32 -0400 Subject: [PATCH 1/2] fix: Update the deprecated functions in Go feature server. Signed-off-by: Shuchu Han --- go/infra/docker/feature-server/Dockerfile | 2 +- go/internal/feast/model/featureview.go | 9 +++------ go/internal/feast/registry/local.go | 5 ++--- go/internal/feast/registry/registry_test.go | 4 ++-- go/internal/feast/registry/s3.go | 4 ++-- go/internal/feast/server/grpc_server_test.go | 11 ++++++----- go/internal/feast/server/logging/logger_test.go | 6 +++--- go/internal/feast/server/logging/offlinestoresink.go | 3 +-- 8 files changed, 20 insertions(+), 24 deletions(-) diff --git a/go/infra/docker/feature-server/Dockerfile b/go/infra/docker/feature-server/Dockerfile index cf63bb45594..a02a98332ab 100644 --- a/go/infra/docker/feature-server/Dockerfile +++ b/go/infra/docker/feature-server/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22.5 +FROM golang:1.23.12 # Update the package list and install the ca-certificates package RUN apt-get update && apt-get install -y ca-certificates diff --git a/go/internal/feast/model/featureview.go b/go/internal/feast/model/featureview.go index b6fde78658e..bdab3adb14f 100644 --- a/go/internal/feast/model/featureview.go +++ b/go/internal/feast/model/featureview.go @@ -1,6 +1,8 @@ package model import ( + "slices" + durationpb "google.golang.org/protobuf/types/known/durationpb" "github.com/feast-dev/feast/go/protos/feast/core" @@ -66,10 +68,5 @@ func (fv *FeatureView) NewFeatureViewFromBase(base *BaseFeatureView) *FeatureVie } func (fv *FeatureView) HasEntity(name string) bool { - for _, entityName := range fv.EntityNames { - if entityName == name { - return true - } - } - return false + return slices.Contains(fv.EntityNames, name) } diff --git a/go/internal/feast/registry/local.go b/go/internal/feast/registry/local.go index 58c6426368a..2703194b976 100644 --- a/go/internal/feast/registry/local.go +++ b/go/internal/feast/registry/local.go @@ -2,7 +2,6 @@ package registry import ( "github.com/google/uuid" - "io/ioutil" "os" "path/filepath" @@ -33,7 +32,7 @@ func NewFileRegistryStore(config *RegistryConfig, repoPath string) *FileRegistry // GetRegistryProto reads and parses the registry proto from the file path. func (r *FileRegistryStore) GetRegistryProto() (*core.Registry, error) { registry := &core.Registry{} - in, err := ioutil.ReadFile(r.filePath) + in, err := os.ReadFile(r.filePath) if err != nil { return nil, err } @@ -58,7 +57,7 @@ func (r *FileRegistryStore) writeRegistry(rp *core.Registry) error { if err != nil { return err } - err = ioutil.WriteFile(r.filePath, bytes, 0644) + err = os.WriteFile(r.filePath, bytes, 0644) if err != nil { return err } diff --git a/go/internal/feast/registry/registry_test.go b/go/internal/feast/registry/registry_test.go index 0801632a70d..3e544d486e5 100644 --- a/go/internal/feast/registry/registry_test.go +++ b/go/internal/feast/registry/registry_test.go @@ -3,7 +3,7 @@ package registry import ( "context" "errors" - "io/ioutil" + "io" "net/url" "strings" "testing" @@ -16,7 +16,7 @@ func TestGetOnlineFeaturesS3Registry(t *testing.T) { mockS3Client := &MockS3Client{ GetObjectFn: func(ctx context.Context, params *s3.GetObjectInput, optFns ...func(*s3.Options)) (*s3.GetObjectOutput, error) { return &s3.GetObjectOutput{ - Body: ioutil.NopCloser(strings.NewReader("mock data")), + Body: io.NopCloser(strings.NewReader("mock data")), }, nil }, DeleteObjectFn: func(ctx context.Context, params *s3.DeleteObjectInput, optFns ...func(*s3.Options)) (*s3.DeleteObjectOutput, error) { diff --git a/go/internal/feast/registry/s3.go b/go/internal/feast/registry/s3.go index 0979dac64d0..71ca8e2f135 100644 --- a/go/internal/feast/registry/s3.go +++ b/go/internal/feast/registry/s3.go @@ -3,7 +3,7 @@ package registry import ( "context" "errors" - "io/ioutil" + "io" "strings" "time" @@ -65,7 +65,7 @@ func (r *S3RegistryStore) GetRegistryProto() (*core.Registry, error) { } defer output.Body.Close() - data, err := ioutil.ReadAll(output.Body) + data, err := io.ReadAll(output.Body) if err != nil { return nil, err } diff --git a/go/internal/feast/server/grpc_server_test.go b/go/internal/feast/server/grpc_server_test.go index 3ef7a6aa8a3..fa0fd26c082 100644 --- a/go/internal/feast/server/grpc_server_test.go +++ b/go/internal/feast/server/grpc_server_test.go @@ -2,7 +2,6 @@ package server import ( "context" - "io/ioutil" "net" "os" "path/filepath" @@ -21,6 +20,7 @@ import ( "github.com/apache/arrow/go/v17/parquet/pqarrow" "github.com/stretchr/testify/assert" "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/test/bufconn" "github.com/feast-dev/feast/go/internal/feast" @@ -84,9 +84,9 @@ func getClient(ctx context.Context, offlineStoreType string, basePath string, lo } }() - conn, _ := grpc.DialContext(ctx, "", grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { + conn, _ := grpc.NewClient("passthrough:///bufnet", grpc.WithContextDialer(func(context.Context, string) (net.Conn, error) { return listener.Dial() - }), grpc.WithInsecure()) + }), grpc.WithTransportCredentials(insecure.NewCredentials())) closer := func() { listener.Close() @@ -216,7 +216,7 @@ func TestGetOnlineFeaturesSqliteWithLogging(t *testing.T) { // Wait for logger to flush. require.Eventually(t, func() bool { - files, err := ioutil.ReadDir(logPath) + files, err := os.ReadDir(logPath) if err != nil || len(files) == 0 { return false } @@ -224,7 +224,8 @@ func TestGetOnlineFeaturesSqliteWithLogging(t *testing.T) { return err == nil && stat.Size() > 0 }, 1*time.Second, 100*time.Millisecond) - files, err := ioutil.ReadDir(logPath) + files, err := os.ReadDir(logPath) + assert.Nil(t, err) logFile := filepath.Join(logPath, files[0].Name()) pf, err := file.OpenParquetFile(logFile, false) assert.Nil(t, err) diff --git a/go/internal/feast/server/logging/logger_test.go b/go/internal/feast/server/logging/logger_test.go index b81179f2d29..3c16b0061d9 100644 --- a/go/internal/feast/server/logging/logger_test.go +++ b/go/internal/feast/server/logging/logger_test.go @@ -2,7 +2,7 @@ package logging import ( "context" - "io/ioutil" + "os" "path/filepath" "testing" "time" @@ -114,11 +114,11 @@ func TestLogAndFlushToFile(t *testing.T) { )) require.Eventually(t, func() bool { - files, _ := ioutil.ReadDir(sink.path) + files, _ := os.ReadDir(sink.path) return len(files) > 0 }, 60*time.Second, 100*time.Millisecond) - files, _ := ioutil.ReadDir(sink.path) + files, _ := os.ReadDir(sink.path) pf, err := file.OpenParquetFile(filepath.Join(sink.path, files[0].Name()), false) assert.Nil(t, err) diff --git a/go/internal/feast/server/logging/offlinestoresink.go b/go/internal/feast/server/logging/offlinestoresink.go index b0f247ce6e1..dd4f0deec71 100644 --- a/go/internal/feast/server/logging/offlinestoresink.go +++ b/go/internal/feast/server/logging/offlinestoresink.go @@ -3,7 +3,6 @@ package logging import ( "fmt" "io" - "io/ioutil" "log" "os" "path/filepath" @@ -33,7 +32,7 @@ func (s *OfflineStoreSink) getOrCreateDatasetDir() (string, error) { if s.datasetDir != "" { return s.datasetDir, nil } - dir, err := ioutil.TempDir("", "*") + dir, err := os.MkdirTemp("", "*") if err != nil { return "", err } From 6ea07fb1a501d58406fdde75d91d0cdd6aefb0ad Mon Sep 17 00:00:00 2001 From: Aniket Paluskar <64416568+aniketpalu@users.noreply.github.com> Date: Tue, 23 Sep 2025 16:18:26 +0530 Subject: [PATCH 2/2] feat: Add 'featureView' in global search api result for features. (#5626) * Added featureView in the response for search results of features Signed-off-by: Aniket Paluskar * Added tests to check featureView addition in feature search results Signed-off-by: Aniket Paluskar --------- Signed-off-by: Aniket Paluskar Signed-off-by: Shuchu Han --- sdk/python/feast/api/registry/rest/search.py | 1 + sdk/python/tests/unit/api/test_search_api.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/sdk/python/feast/api/registry/rest/search.py b/sdk/python/feast/api/registry/rest/search.py index 6e592ac86d4..e670a8981ac 100644 --- a/sdk/python/feast/api/registry/rest/search.py +++ b/sdk/python/feast/api/registry/rest/search.py @@ -167,6 +167,7 @@ def search_resources( "name": feature.get("name", ""), "description": feature.get("description", ""), "project": current_project, + "featureView": feature.get("featureView", ""), "tags": feature.get("tags", {}), } ) diff --git a/sdk/python/tests/unit/api/test_search_api.py b/sdk/python/tests/unit/api/test_search_api.py index 06b670a16c5..48e422c5fe1 100644 --- a/sdk/python/tests/unit/api/test_search_api.py +++ b/sdk/python/tests/unit/api/test_search_api.py @@ -802,6 +802,15 @@ def test_search_query_functionality(self, shared_search_responses): "Expected individual features to appear in search results, but found none" ) + for feature_result in feature_results: + assert "featureView" in feature_result + assert feature_result["featureView"] in [ + "user_features", + "product_features", + "transaction_features", + "user_on_demand_features", + ] + # Verify we have features that likely come from different feature views feature_names = {f["name"] for f in feature_results}