Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions pkg/blueprint_client/get_by_id.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package bpc

import (
"context"
"fmt"

"connectrpc.com/connect"
kvv1 "github.com/steady-bytes/draft/api/core/registry/key_value/v1"
kvv1Connect "github.com/steady-bytes/draft/api/core/registry/key_value/v1/v1connect"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/anypb"
)

func GetById[T protoreflect.ProtoMessage](
ctx context.Context,
client kvv1Connect.KeyValueServiceClient,
id string,
factory func() T) (T, error) {
if id == "" {
return factory(), fmt.Errorf("ID cannot be empty")
}

t := factory()

val, err := anypb.New(*new(T))
if err != nil {
return t, err
}

res, err := client.Get(ctx, connect.NewRequest(&kvv1.GetRequest{
Key: id,
Value: val,
}))
if err != nil {
return t, err
}

if res.Msg == nil || res.Msg.GetValue() == nil {
return t, fmt.Errorf("item with ID %s not found", id)
}

if err := res.Msg.GetValue().UnmarshalTo(t); err != nil {
return t, fmt.Errorf("failed to unmarshal item: %v", err)
}

return t, nil
}
11 changes: 11 additions & 0 deletions pkg/blueprint_client/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module github.com/steady-bytes/bpc/pkg/blueprint_client

go 1.24.0

require (
connectrpc.com/connect v1.18.1
github.com/steady-bytes/draft/api v1.1.0
google.golang.org/protobuf v1.36.7
)

require github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
14 changes: 14 additions & 0 deletions pkg/blueprint_client/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw=
connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8=
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/steady-bytes/draft/api v1.1.0 h1:nOPLbpkWrdGAWFz22SPsSO4HR8IlckvUq7ZD5l0jn4A=
github.com/steady-bytes/draft/api v1.1.0/go.mod h1:zwZNNg8uIoQb3OqibvvcdgJPymawy2rUWh6M6Nk43a0=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
47 changes: 47 additions & 0 deletions pkg/blueprint_client/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package bpc

import (
"context"
"fmt"

"connectrpc.com/connect"
kvv1 "github.com/steady-bytes/draft/api/core/registry/key_value/v1"
kvv1Connect "github.com/steady-bytes/draft/api/core/registry/key_value/v1/v1connect"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/anypb"
)

// list retrieves all items of type T from the key-value store.
// It's understood that the response of this is not limited in size
// so use with caution in production environments.
// Blueprint needs some additional logic to handle pagination or limits.
// I don't think this can actually be improved without Blueprint having
// indexing and pagination capabilities.
func List[T protoreflect.ProtoMessage](ctx context.Context, client kvv1Connect.KeyValueServiceClient, factory func() T) ([]T, error) {
val, err := anypb.New(*new(T))
if err != nil {
return nil, err
}

res, err := client.List(ctx, connect.NewRequest(&kvv1.ListRequest{
Value: val,
}))
if err != nil {
return nil, err
}

if res.Msg == nil || len(res.Msg.GetValues()) == 0 {
return nil, fmt.Errorf("no items found")
}

var items []T
for _, v := range res.Msg.GetValues() {
item := factory()
if err := v.UnmarshalTo(item); err != nil {
return nil, fmt.Errorf("failed to unmarshal item: %v", err)
}
items = append(items, item)
}

return items, nil
}
46 changes: 46 additions & 0 deletions pkg/blueprint_client/list_and_filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package bpc

import (
"context"

kvv1 "github.com/steady-bytes/draft/api/core/registry/key_value/v1"
kvv1Connect "github.com/steady-bytes/draft/api/core/registry/key_value/v1/v1connect"
"google.golang.org/protobuf/reflect/protoreflect"
)

func ListAndFilter[T protoreflect.ProtoMessage](ctx context.Context, client kvv1Connect.KeyValueServiceClient, filter *kvv1.Statement, factory func() T) ([]T, error) {
// This function can be used to list and filter items based on the provided filter.
// For now, it just calls the list function.
items, err := List[T](ctx, client, factory)
if err != nil {
return nil, err
}

// filter items based on the key/value pairs in the filter
filters := filter.GetWhere().(*kvv1.Statement_KeyVal).KeyVal.GetMatch()

// the new slice of T that will hold the filtered items
var filtered []T

// use proto reflect to filter items based on the the key/value pairs in the filter
// NOTE: This is a simple filtering logic that checks if the field value matches the filter value in
// In the request. It's also understood this is no the most efficient way to filter items,
// but it serves as a starting point for filtering logic.
for _, item := range items {
itemValue := item.ProtoReflect()
matched := true
for key, value := range filters {
field := itemValue.Descriptor().Fields().ByName(protoreflect.Name(key))
if field == nil || itemValue.Get(field).String() != value {
matched = false
break
}
}

if matched {
filtered = append(filtered, item)
}
}

return filtered, nil
}
41 changes: 41 additions & 0 deletions pkg/blueprint_client/save.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package bpc

import (
"context"
"fmt"

"connectrpc.com/connect"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/anypb"

kvv1 "github.com/steady-bytes/draft/api/core/registry/key_value/v1"
kvv1Connect "github.com/steady-bytes/draft/api/core/registry/key_value/v1/v1connect"
)

func Save[T protoreflect.ProtoMessage](
ctx context.Context,
client kvv1Connect.KeyValueServiceClient,
key string,
item T,
) (T, error) {
if key == "" {
// I am starting to consider that I should make a generic error type for the repository lib
return item, fmt.Errorf("key cannot be empty")
}

val, err := anypb.New(item)
if err != nil {
return item, err
}

req := connect.NewRequest(&kvv1.SetRequest{
Key: key,
Value: val,
})

if _, err := client.Set(ctx, req); err != nil {
return item, err
}

return item, nil
}