From d14010bbae79843dadbc831ef7e086eebc9b6c45 Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Wed, 16 Jul 2025 17:46:38 -0700 Subject: [PATCH 01/12] WIP - J5 Reflection List --- go.mod | 10 +- go.sum | 10 +- internal/pgstore/path.go | 405 ++---- internal/pgstore/path_test.go | 133 +- .../gen/test/v1/test_pb/bar.j5s.pb.go | 868 ------------ .../gen/test/v1/test_pb/bar.j5s_j5.pb.go | 23 + .../gen/test/v1/test_pb/bar.j5s_psm.pb.go | 618 --------- .../gen/test/v1/test_pb/bar.j5s_sugar.pb.go | 120 -- .../gen/test/v1/test_pb/foo.j5s.pb.go | 1201 ----------------- .../gen/test/v1/test_pb/foo.j5s_j5.pb.go | 29 + .../gen/test/v1/test_pb/foo.j5s_psm.pb.go | 628 --------- .../gen/test/v1/test_pb/foo.j5s_sugar.pb.go | 120 -- .../gen/test/v1/test_spb/bar.p.j5s.pb.go | 649 --------- .../gen/test/v1/test_spb/bar.p.j5s_grpc.pb.go | 186 --- .../gen/test/v1/test_spb/bar.p.j5s_j5.pb.go | 26 + .../v1/test_spb/bar.p.j5s_psm_query.pb.go | 117 -- .../test/v1/test_spb/bar.p.j5s_sugar.pb.go | 3 - .../gen/test/v1/test_spb/foo.p.j5s.pb.go | 768 ----------- .../gen/test/v1/test_spb/foo.p.j5s_grpc.pb.go | 277 ---- .../gen/test/v1/test_spb/foo.p.j5s_j5.pb.go | 32 + .../v1/test_spb/foo.p.j5s_psm_query.pb.go | 115 -- .../test/v1/test_spb/foo.p.j5s_sugar.pb.go | 3 - .../gen/test/v1/test_tpb/bar.p.j5s.pb.go | 235 ---- .../gen/test/v1/test_tpb/bar.p.j5s_grpc.pb.go | 111 -- .../gen/test/v1/test_tpb/bar.p.j5s_j5.pb.go | 11 + .../v1/test_tpb/bar.p.j5s_o5_messaging.pb.go | 95 -- .../v1/test_tpb/bar.p.j5s_psm_publish.pb.go | 35 - .../test/v1/test_tpb/bar.p.j5s_sugar.pb.go | 3 - .../gen/test/v1/test_tpb/foo.p.j5s.pb.go | 235 ---- .../gen/test/v1/test_tpb/foo.p.j5s_grpc.pb.go | 111 -- .../gen/test/v1/test_tpb/foo.p.j5s_j5.pb.go | 11 + .../v1/test_tpb/foo.p.j5s_o5_messaging.pb.go | 95 -- .../v1/test_tpb/foo.p.j5s_psm_publish.pb.go | 35 - .../test/v1/test_tpb/foo.p.j5s_sugar.pb.go | 3 - j5.yaml | 11 + pquery/filter.go | 835 +++++------- pquery/getter.go | 72 +- pquery/lister.go | 218 +-- pquery/query.go | 16 +- pquery/search.go | 78 +- pquery/sort.go | 12 +- psm/builder.go | 21 +- psm/gen_interfaces.go | 14 +- psm/state_query.go | 11 +- psm/table_map.go | 12 +- 45 files changed, 931 insertions(+), 7690 deletions(-) delete mode 100644 internal/testproto/gen/test/v1/test_pb/bar.j5s.pb.go create mode 100644 internal/testproto/gen/test/v1/test_pb/bar.j5s_j5.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_pb/bar.j5s_psm.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_pb/bar.j5s_sugar.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_pb/foo.j5s.pb.go create mode 100644 internal/testproto/gen/test/v1/test_pb/foo.j5s_j5.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_pb/foo.j5s_psm.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_pb/foo.j5s_sugar.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_spb/bar.p.j5s.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_spb/bar.p.j5s_grpc.pb.go create mode 100644 internal/testproto/gen/test/v1/test_spb/bar.p.j5s_j5.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_spb/bar.p.j5s_psm_query.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_spb/bar.p.j5s_sugar.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_spb/foo.p.j5s.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_spb/foo.p.j5s_grpc.pb.go create mode 100644 internal/testproto/gen/test/v1/test_spb/foo.p.j5s_j5.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_spb/foo.p.j5s_psm_query.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_spb/foo.p.j5s_sugar.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_tpb/bar.p.j5s.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_grpc.pb.go create mode 100644 internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_j5.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_o5_messaging.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_psm_publish.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_sugar.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_tpb/foo.p.j5s.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_grpc.pb.go create mode 100644 internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_j5.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_o5_messaging.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_psm_publish.pb.go delete mode 100644 internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_sugar.pb.go diff --git a/go.mod b/go.mod index 1e297c8..6214a5a 100644 --- a/go.mod +++ b/go.mod @@ -3,22 +3,20 @@ module github.com/pentops/protostate go 1.24.0 require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250603165357-b52ab10f4468.1 buf.build/go/protovalidate v0.12.0 buf.build/go/protoyaml v0.6.0 github.com/elgris/sqrl v0.0.0-20210727210741-7e0198b30236 github.com/google/uuid v1.6.0 github.com/iancoleman/strcase v0.3.0 github.com/lib/pq v1.10.9 - github.com/pentops/flowtest v0.0.0-20250611222350-b5c7162d9db1 + github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21 github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 github.com/pentops/j5 v0.0.0-20250625220400-ddb847893140 github.com/pentops/log.go v0.0.16 - github.com/pentops/o5-messaging v0.0.0-20250520213617-fba07334e9aa + github.com/pentops/o5-messaging v0.0.0-20250619024104-7e07c29129f0 github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b github.com/pentops/sqrlx.go v0.0.0-20250520210217-2f46de329c7a github.com/stretchr/testify v1.10.0 - google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 google.golang.org/grpc v1.72.2 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 @@ -26,6 +24,7 @@ require ( ) require ( + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250603165357-b52ab10f4468.1 // indirect cel.dev/expr v0.24.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/bufbuild/protocompile v0.14.1 // indirect @@ -48,5 +47,8 @@ require ( golang.org/x/sync v0.14.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.25.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect ) + +replace github.com/pentops/j5 => /Users/daemonl/pentops/j5 diff --git a/go.sum b/go.sum index cac2e12..ff801f4 100644 --- a/go.sum +++ b/go.sum @@ -73,16 +73,14 @@ github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stg github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pentops/flowtest v0.0.0-20250611222350-b5c7162d9db1 h1:HFhoMQYMnf4rhXqFz9rJznYcLexvzA/tSoZW6024nUs= -github.com/pentops/flowtest v0.0.0-20250611222350-b5c7162d9db1/go.mod h1:vNp8crAKcH0f/sZU9frkmQLUeDsTIgMqV14kQtkAqC0= +github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21 h1:EIMCYtccNxV59aqpmPV/6iJcREagf5WiPg7KXLe3XPk= +github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21/go.mod h1:vNp8crAKcH0f/sZU9frkmQLUeDsTIgMqV14kQtkAqC0= github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 h1:s5qtWT2/s79gy/wm3/bwvKYLK6u2AkW05JiLPqxraP0= github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63/go.mod h1:I58JIVvL1/nP4CEHGKGbBhvWIEA9mVkGeoviemaqanU= -github.com/pentops/j5 v0.0.0-20250625220400-ddb847893140 h1:TEvkzT43GUvXuTGC+8BOVWKLTmS33VncFP/EX4LL5gg= -github.com/pentops/j5 v0.0.0-20250625220400-ddb847893140/go.mod h1:DZbBKepsGataOEtfB8AjkRiejRtLGQcBejTUYJK5wlY= github.com/pentops/log.go v0.0.16 h1:oxCuHSBOBPjfUVSXyOSEEdYUwytysj4T29/7T2FBp9Q= github.com/pentops/log.go v0.0.16/go.mod h1:yR34x8aMlvhdGvqgIU4+0MiLjJTKt0vpcgUnVN2nZV4= -github.com/pentops/o5-messaging v0.0.0-20250520213617-fba07334e9aa h1:Sdnc9mrRSefBbbrwmpq/31ABuXBwtch2KGd68ORJS44= -github.com/pentops/o5-messaging v0.0.0-20250520213617-fba07334e9aa/go.mod h1:HC8BSCybYzZGmN8x2dpbdxZCAZeziHfZPb4r8pzLUhc= +github.com/pentops/o5-messaging v0.0.0-20250619024104-7e07c29129f0 h1:aAt5rIhzFyF1RdT262LGHL0V4vBxcGCTWIeC1C0vZ20= +github.com/pentops/o5-messaging v0.0.0-20250619024104-7e07c29129f0/go.mod h1:kNBuV8uil9ZtX+eUqTbp/FkfUeK7Dkqxokfa6ASt04Y= github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b h1:UfL+A9/Nwi0QWrW7K06L8vYA+LsFRowWFtVYZICil+s= github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b/go.mod h1:U4AOJK8uL3IkJx1t5MBsOXudjZ111il31WE9UxL8Wz8= github.com/pentops/sqrlx.go v0.0.0-20250520210217-2f46de329c7a h1:v80copaLBILLt0Bozh6ZKNk8GeCNsblhB14d+ZIJUtc= diff --git a/internal/pgstore/path.go b/internal/pgstore/path.go index 08202e5..7172f7d 100644 --- a/internal/pgstore/path.go +++ b/internal/pgstore/path.go @@ -4,11 +4,8 @@ import ( "fmt" "strings" - "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" + "github.com/pentops/j5/lib/j5reflect" "github.com/pentops/j5/lib/j5schema" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/types/descriptorpb" ) type Table interface { @@ -28,20 +25,7 @@ type NestedField struct { ValueColumn *string } -type ProtoFieldSpec struct { - // The column holding the data, either directly or JSONB from here using the - // path - ColumnName string - - // The path from the column root to the node being specified (not the path - // of the node from a 'root node' of the table) - Path ProtoPathSpec - - // The path from the nominal table root message to the node being specified - PathFromRoot ProtoPathSpec -} - -func (nf *NestedField) ProtoChild(name protoreflect.Name) (*NestedField, error) { +func (nf *NestedField) ProtoChild(name string) (*NestedField, error) { pathChild, err := nf.Path.Child(name) if err != nil { return nil, err @@ -63,67 +47,50 @@ func (nf *NestedField) Selector(inTable string) string { } type pathNode struct { - name protoreflect.Name - field protoreflect.FieldDescriptor - oneof protoreflect.OneofDescriptor + name string + field *j5schema.ObjectProperty + //oneof protoreflect.OneofDescriptor } type Path struct { - root protoreflect.MessageDescriptor + root *j5schema.ObjectSchema path []pathNode - leafField protoreflect.FieldDescriptor - leafOneof protoreflect.OneofDescriptor + leafField *j5schema.ObjectProperty + leafOneof *j5schema.OneofSchema // If the leaf is a oneof, this is the oneof schema } -func (pp Path) Root() protoreflect.MessageDescriptor { +func (pp Path) Root() *j5schema.ObjectSchema { return pp.root } -func (pp Path) LeafField() protoreflect.FieldDescriptor { +func (pp Path) LeafField() *j5schema.ObjectProperty { return pp.leafField } -func (pp Path) LeafOneof() protoreflect.OneofDescriptor { - return pp.leafOneof -} - -// LeafOneofWrapper handles the special case where the leaf is a oneof inside of -// a oneof wrapper, then this returns the field definition of the object which -// references the wrapper. -func (pp Path) LeafOneofWrapper() protoreflect.FieldDescriptor { - if pp.leafOneof == nil { - return nil - } - parentMsg := pp.leafOneof.Parent().(protoreflect.MessageDescriptor) - if !j5schema.IsOneofWrapper(parentMsg) { - return nil - } - - // The leaf is a proto oneof in a j5 oneof wrapper. - // path[-1] should be the message - // path[-2] should be the field - fieldNode := pp.path[len(pp.path)-2] - return fieldNode.field -} - -func (pp Path) Leaf() protoreflect.Descriptor { - if pp.leafField != nil { - return pp.leafField - } +func (pp Path) LeafOneof() *j5schema.OneofSchema { return pp.leafOneof } -func (pp Path) Child(name protoreflect.Name) (*Path, error) { +func (pp Path) Child(name string) (*Path, error) { if pp.leafField == nil { return nil, fmt.Errorf("child requires a message field") } - if pp.leafField.Kind() != protoreflect.MessageKind { + + var propSet j5schema.PropertySet + + switch st := pp.leafField.Schema.(type) { + case *j5schema.ObjectField: + propSet = st.ObjectSchema().Properties + case *j5schema.OneofField: + propSet = st.OneofSchema().Properties + default: return nil, fmt.Errorf("child requires a message field") } - field := pp.leafField.Message().Fields().ByName(name) + + field := propSet.ByJSONName(name) if field == nil { - return nil, fmt.Errorf("field %s not found in message %s", name, pp.leafField.Message().FullName()) + return nil, fmt.Errorf("field %s not found in message %s", name, pp.leafField.FullName()) } return &Path{ root: pp.root, @@ -155,30 +122,44 @@ func (pp *Path) JSONBArrowPath() string { elements := make([]string, 0, len(pp.path)) end, path := pp.path[len(pp.path)-1], pp.path[:len(pp.path)-1] for _, part := range path { - if part.oneof != nil { - continue // Ignore the node, it isn't in the JSONB tree + if obj, ok := part.field.Schema.(*j5schema.ObjectField); ok { + if obj.Flatten { + continue // Flattened fields are not in the JSONB tree + } } - if part.field.IsList() { - panic("list fields not supported by JSONBArrowPath()") + if part.field == nil { + panic(fmt.Sprintf("invalid path: %v", pp.DebugName())) + } + + switch part.field.Schema.(type) { + case *j5schema.MapField: + panic("map fields not supported by JSONBArrowPath()") + case *j5schema.ArrayField: + panic("array fields not supported by JSONBArrowPath()") } - elements = append(elements, fmt.Sprintf("->'%s'", part.field.JSONName())) + elements = append(elements, fmt.Sprintf("->'%s'", part.field.JSONName)) } - return fmt.Sprintf("%s->>'%s'", strings.Join(elements, ""), end.field.JSONName()) + return fmt.Sprintf("%s->>'%s'", strings.Join(elements, ""), end.field.JSONName) } func (pp *Path) JSONPathQuery() string { elements := make([]string, 1, len(pp.path)+1) elements[0] = "$" // Used sometimes, not always? for _, part := range pp.path { - if part.oneof != nil { - continue // Ignore the node, it isn't in the JSONB tree + if obj, ok := part.field.Schema.(*j5schema.ObjectField); ok { + if obj.Flatten { + continue // Flattened fields are not in the JSONB tree + } } if part.field == nil { panic(fmt.Sprintf("invalid path: %v", pp.DebugName())) } - elements = append(elements, fmt.Sprintf(".%s", part.field.JSONName())) - if part.field.IsList() { + elements = append(elements, fmt.Sprintf(".%s", part.field.JSONName)) + switch part.field.Schema.(type) { + case *j5schema.MapField: + panic("map fields not supported by JSONBArrowPath()") + case *j5schema.ArrayField: elements = append(elements, "[*]") } } @@ -188,20 +169,18 @@ func (pp *Path) JSONPathQuery() string { // WalkPathNodes visits every field in the message tree other than the root // message itself, calling the callback for each. -func WalkPathNodes(rootMessage protoreflect.MessageDescriptor, callback func(Path) error) error { +func WalkPathNodes(rootMessage *j5schema.ObjectSchema, callback func(Path) error) error { root := &Path{ root: rootMessage, } - return root.walk(rootMessage, callback) + return root.walk(rootMessage.Properties, callback) } -func (pp Path) walk(msg protoreflect.MessageDescriptor, callback func(Path) error) error { - fields := msg.Fields() +func (pp Path) walk(props j5schema.PropertySet, callback func(Path) error) error { // walks only fields, not oneofs. - for i := range fields.Len() { - field := fields.Get(i) + for _, field := range props { fieldPath := append(pp.path, pathNode{ - name: field.Name(), + name: field.JSONName, field: field, }) fieldPathSpec := Path{ @@ -214,149 +193,21 @@ func (pp Path) walk(msg protoreflect.MessageDescriptor, callback func(Path) erro return err } - if field.Kind() != protoreflect.MessageKind { - continue - } - if err := fieldPathSpec.walk(field.Message(), callback); err != nil { - return fmt.Errorf("walking %s: %w", field.Name(), err) - } - } - - return nil - -} - -// An element in a path from a root message to a leaf node -// Messages use field name strings -// Repeated uses index numbes as strings (1 = "1") -// Maps use the map keys, which are always strings in J5 land. -// OneOf is not included as it doesn't appear in the proto tree -type ProtoPathSpec []string - -func ParseProtoPathSpec(path string) ProtoPathSpec { - return ProtoPathSpec(strings.Split(path, ".")) -} - -func (pp ProtoPathSpec) String() string { - return strings.Join(pp, ".") -} - -func NewProtoPath(message protoreflect.MessageDescriptor, fieldPath ProtoPathSpec) (*Path, error) { - - if len(fieldPath) == 0 { - return nil, fmt.Errorf("fieldPath must have at least one element") - } - - pathSpec := &Path{ - root: message, - path: make([]pathNode, 0, len(fieldPath)), - } - - walkMessage := message - var pathElem string - walkPath := fieldPath - var inOneof *int - - for { - - pathElem, walkPath = walkPath[0], walkPath[1:] - node := pathNode{ - name: protoreflect.Name(pathElem), - } - field := walkMessage.Fields().ByName(protoreflect.Name(pathElem)) - if field != nil { - node.field = field - // Check that it isn't a oneof. - if inOneof != nil { - if field.ContainingOneof().Index() != *inOneof { - return nil, fmt.Errorf("field %s not in oneof %d", pathElem, *inOneof) - } - inOneof = nil - } - } else { - if inOneof != nil { - return nil, fmt.Errorf("field %s not found in oneof %d", pathElem, *inOneof) - } - // Oneof needn't be specified as it doens't appear in the node tree, - // but if a oneof is named, this adds it as a node in the tree, - // hoping that the next element will be an actual field. - oneof := walkMessage.Oneofs().ByName(protoreflect.Name(pathElem)) - if oneof != nil { - node.oneof = oneof - } else { - return nil, fmt.Errorf("no field named '%s' in message %s", pathElem, walkMessage.FullName()) + switch ft := field.Schema.(type) { + case *j5schema.ObjectField: + if err := fieldPathSpec.walk(ft.ObjectSchema().Properties, callback); err != nil { + return fmt.Errorf("walking %s: %w", field.JSONName, err) } - idx := oneof.Index() - inOneof = &idx - pathSpec.path = append(pathSpec.path, node) - if len(walkPath) == 0 { - pathSpec.leafOneof = node.oneof - break - } - continue - } - - if field.IsMap() { - return nil, fmt.Errorf("unimplemented: map fields in path spec") - } - - pathSpec.path = append(pathSpec.path, node) - - if len(walkPath) == 0 { - pathSpec.leafField = node.field - break - } - - if field.Kind() != protoreflect.MessageKind { - return nil, fmt.Errorf("field %s is not a message, but path elements remain (%v)", pathElem, walkPath) - } - - walkMessage = field.Message() - - } - - return pathSpec, nil -} - -func findField(message protoreflect.MessageDescriptor, fieldPath string) []pathNode { - fields := message.Fields() - if field := fields.ByJSONName(fieldPath); field != nil { - return []pathNode{{ - name: protoreflect.Name(fieldPath), - field: field, - }} - } - - for i := range fields.Len() { - field := fields.Get(i) - // Check for flattened fields - fieldOpts, ok := proto.GetExtension(field.Options().(*descriptorpb.FieldOptions), ext_j5pb.E_Field).(*ext_j5pb.FieldOptions) - if !ok { - continue - } - - if fieldOpts != nil { - // TODO: remove the use of Message once everything has been moved over to Object - if (fieldOpts.GetMessage() != nil && fieldOpts.GetMessage().Flatten) || - (fieldOpts.GetObject() != nil && fieldOpts.GetObject().Flatten) { - if flattenedField := field.Message().Fields().ByJSONName(fieldPath); flattenedField != nil { - return []pathNode{ - { - name: field.Name(), - field: field, - }, - { - name: protoreflect.Name(fieldPath), - field: flattenedField, - }, - } - } + case *j5schema.OneofField: + if err := fieldPathSpec.walk(ft.OneofSchema().Properties, callback); err != nil { + return fmt.Errorf("walking %s: %w", field.JSONName, err) } } } return nil + } // Like ProtoPathSpec but uses JSON field names @@ -370,104 +221,130 @@ func (jp JSONPathSpec) String() string { return strings.Join(jp, ".") } -func NewJSONPath(message protoreflect.MessageDescriptor, fieldPath JSONPathSpec) (*Path, error) { +func NewJSONPath(rootMessage *j5schema.ObjectSchema, fieldPath JSONPathSpec) (*Path, error) { if len(fieldPath) == 0 { return nil, fmt.Errorf("fieldPath must have at least one element") } pathSpec := &Path{ - root: message, + root: rootMessage, path: make([]pathNode, 0, len(fieldPath)), } - walkMessage := message + var walkMessage j5schema.RootSchema + walkMessage = rootMessage var pathElem string walkPath := fieldPath var nodes []pathNode for { pathElem, walkPath = walkPath[0], walkPath[1:] - nodes = findField(walkMessage, pathElem) - if nodes == nil { + var node pathNode + switch walkMessage := walkMessage.(type) { + case *j5schema.ObjectSchema: + field := walkMessage.ClientProperties().ByJSONName(pathElem) + if field == nil { + return nil, fmt.Errorf("field %s not found in object %s", pathElem, walkMessage.FullName()) + } + node = pathNode{ + name: pathElem, + field: field, + } + + case *j5schema.OneofSchema: // Very Special Edge Case: Oneof wrapper types allow the client to // filter based on the type of the oneof. So the oneof can be at the // end of the path, and the field can be the oneof wrapper type. - if len(walkPath) == 0 && pathElem == "type" { - oneof := walkMessage.Oneofs().ByName(protoreflect.Name("type")) - if oneof != nil { - nodes = []pathNode{{ - name: protoreflect.Name(pathElem), - oneof: oneof, - }} - pathSpec.path = append(pathSpec.path, nodes...) - pathSpec.leafOneof = oneof - break - } + pathSpec.path = append(pathSpec.path, nodes...) + pathSpec.leafOneof = walkMessage + break } - return nil, fmt.Errorf("JSON field '%s' not found in message %s", pathElem, walkMessage.FullName()) - } + field := walkMessage.ClientProperties().ByJSONName(pathElem) + if field == nil { - for _, node := range nodes { - if node.field.IsMap() { - return nil, fmt.Errorf("unimplemented: map fields in path spec") + return nil, fmt.Errorf("field %s not found in oneof %s", pathElem, walkMessage.FullName()) + } + node = pathNode{ + name: pathElem, + field: field, } } - pathSpec.path = append(pathSpec.path, nodes...) + pathSpec.path = append(pathSpec.path, node) if len(walkPath) == 0 { - pathSpec.leafField = nodes[len(nodes)-1].field + pathSpec.leafField = node.field break } - if nodes[len(nodes)-1].field.Kind() != protoreflect.MessageKind { + switch nextType := node.field.Schema.(type) { + case *j5schema.ObjectField: + walkMessage = nextType.ObjectSchema() + case *j5schema.OneofField: + walkMessage = nextType.OneofSchema() + case j5schema.RootSchema: + walkMessage = nextType + default: return nil, fmt.Errorf("field %s is not a message, but path elements remain", pathElem) } - - walkMessage = nodes[len(nodes)-1].field.Message() } return pathSpec, nil } -func (pp *Path) GetValue(msg protoreflect.Message) (protoreflect.Value, error) { +func (pp *Path) pathParts() []string { + parts := make([]string, 0, len(pp.path)) + for _, node := range pp.path { + parts = append(parts, node.field.JSONName) + } + return parts +} + +func (pp *Path) GetValue(msg j5reflect.Object) (any, bool, error) { if len(pp.path) == 0 { - return protoreflect.Value{}, fmt.Errorf("empty path") + return nil, false, fmt.Errorf("empty path") } - var val protoreflect.Value - var walkNode pathNode - walkMessage := msg + lastField, ok, err := msg.GetField(pp.pathParts()...) + if err != nil { + return nil, false, fmt.Errorf("getting field %s: %w", pp.pathNodeNames(), err) + } + if !ok { + return nil, false, nil + } - remainingPath := pp.path - for { - walkNode, remainingPath = remainingPath[0], remainingPath[1:] - if walkNode.oneof != nil { - // ignore the oneof - if len(remainingPath) == 0 { - return protoreflect.Value{}, fmt.Errorf("oneof at leaf") - } - continue + if pp.leafOneof != nil { + // this is a type filter. + oneof, ok := lastField.AsOneof() + if !ok { + return nil, false, fmt.Errorf("field %s is not a oneof", pp.pathNodeNames()) } - if walkNode.field == nil { - return protoreflect.Value{}, fmt.Errorf("no field or oneof") + field, ok, err := oneof.GetOne() + if err != nil { + return nil, false, fmt.Errorf("getting oneof field %s: %w", pp.pathNodeNames(), err) } - - // Has vs Get, Has returns false if the field is set to the default value for - // scalar types. We still want the fields if they are set to the default value, - // and can use validation of a field's existence before this point to ensure - // that the field is available. - val = walkMessage.Get(walkNode.field) - if len(remainingPath) == 0 { - return val, nil + if !ok { + return nil, false, nil // No value set in the oneof } - if walkNode.field.Kind() != protoreflect.MessageKind { - return protoreflect.Value{}, fmt.Errorf("field %s is not a message", walkNode.field.Name()) - } - walkMessage = val.Message() + val := field.NameInParent() + return val, true, nil + } + + scalar, ok := lastField.AsScalar() + if !ok { + return nil, false, fmt.Errorf("field %s is not a scalar", pp.pathNodeNames()) + } + + goVal, err := scalar.ToGoValue() + if err != nil { + return nil, false, fmt.Errorf("converting field %s to Go value: %w", pp.pathNodeNames(), err) + } + if goVal == nil { + return nil, false, nil // No value set in the scalar } + return goVal, true, nil } diff --git a/internal/pgstore/path_test.go b/internal/pgstore/path_test.go index 6765f42..6e6ee73 100644 --- a/internal/pgstore/path_test.go +++ b/internal/pgstore/path_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/pentops/flowtest/prototest" + "github.com/pentops/j5/lib/j5schema" "github.com/stretchr/testify/assert" ) @@ -22,97 +23,96 @@ func TestFindFieldSpec(t *testing.T) { message Profile { int64 weight = 1; + ProfileType type = 2; + } + message ProfileType { oneof type { Card card = 2; } } + message Card { int64 size = 1; } `}) fooDesc := descFiles.MessageByName(t, "test.Foo") - - tcs := []struct { - path ProtoPathSpec - jsonPath JSONPathSpec - }{ - {path: ProtoPathSpec{"id"}}, - {path: ProtoPathSpec{"profile", "weight"}}, - {path: ProtoPathSpec{"profile", "card", "size"}}, + fooSchema, err := j5schema.Global.Schema(fooDesc) + if err != nil { + t.Fatal(err) + } + fooObject, ok := fooSchema.(*j5schema.ObjectSchema) + if !ok { + t.Fatal("expected Foo to be an ObjectSchema") } - for _, tc := range tcs { - if tc.jsonPath == nil { - tc.jsonPath = JSONPathSpec(tc.path) // assume it's just lower case wingle word + t.Run("Find Field", func(t *testing.T) { + + tcs := []struct { + path JSONPathSpec + isOneof bool + }{ + {path: JSONPathSpec{"id"}}, + {path: JSONPathSpec{"profile", "weight"}}, + {path: JSONPathSpec{"profile", "type"}}, + {path: JSONPathSpec{"profile", "type", "type"}, isOneof: true}, + {path: JSONPathSpec{"profile", "type", "card", "size"}}, } - t.Run(tc.path.String()+" Proto", func(t *testing.T) { - spec, err := NewProtoPath(fooDesc, tc.path) - if err != nil { - t.Fatal(err) - } - - if spec.leafField == nil { - t.Fatal("expected field") - } - name := tc.path[len(tc.path)-1] - assert.Equal(t, string(spec.leafField.Name()), name) - }) - t.Run(tc.jsonPath.String()+" JSON", func(t *testing.T) { - spec, err := NewJSONPath(fooDesc, tc.jsonPath) - if err != nil { - t.Fatal(err) - } - - if spec.leafField == nil { - t.Fatal("expected field") - } - - name := tc.path[len(tc.path)-1] - assert.Equal(t, string(spec.leafField.Name()), name) - }) - } + for _, tc := range tcs { + t.Run(tc.path.String()+" JSON", func(t *testing.T) { + spec, err := NewJSONPath(fooObject, tc.path) + if err != nil { + t.Fatal(err) + } + + if tc.isOneof { + if spec.leafOneof == nil { + t.Fatal("missing oneof field") + } + } else { + if spec.leafField == nil { + t.Fatal("missing leaf field") + } + name := tc.path[len(tc.path)-1] + assert.Equal(t, string(spec.leafField.JSONName), name) + } + }) + } + }) - tcs = []struct { - path ProtoPathSpec - jsonPath JSONPathSpec - }{ - {path: ProtoPathSpec{"foo"}}, - {path: ProtoPathSpec{"profile", "weight", "size"}}, - } + t.Run("Sad Path", func(t *testing.T) { + tcs := []struct { + path JSONPathSpec + }{ + {path: JSONPathSpec{"foo"}}, + {path: JSONPathSpec{"profile", "weight", "size"}}, + } - for _, tc := range tcs { - if tc.jsonPath == nil { - tc.jsonPath = JSONPathSpec(tc.path) // assume it's just lower case wingle word + for _, tc := range tcs { + t.Run(tc.path.String()+" JSON Sad", func(t *testing.T) { + _, err := NewJSONPath(fooObject, tc.path) + if err == nil { + t.Fatal("expected an error") + } + }) } - t.Run(tc.path.String()+" Proto Sad", func(t *testing.T) { - _, err := NewProtoPath(fooDesc, tc.path) - if err == nil { - t.Fatal("expected an error") - } - }) - t.Run(tc.jsonPath.String()+" JSON Sad", func(t *testing.T) { - _, err := NewJSONPath(fooDesc, tc.jsonPath) - if err == nil { - t.Fatal("expected an error") - } - }) - } + }) t.Run("Walk", func(t *testing.T) { expectedNodes := map[string]struct{}{ - "$.id": {}, - "$.profile": {}, - "$.profile.weight": {}, - "$.profile.card": {}, - "$.profile.card.size": {}, + "$.id": {}, + "$.profile": {}, + "$.profile.weight": {}, + "$.profile.type": {}, + "$.profile.type.card": {}, + "$.profile.type.card.size": {}, } - err := WalkPathNodes(fooDesc, func(node Path) error { + err := WalkPathNodes(fooObject, func(node Path) error { pathQuery := node.JSONPathQuery() t.Log(pathQuery) _, ok := expectedNodes[pathQuery] @@ -120,6 +120,7 @@ func TestFindFieldSpec(t *testing.T) { t.Fatalf("unexpected node %s", pathQuery) } delete(expectedNodes, pathQuery) + return nil }) if err != nil { diff --git a/internal/testproto/gen/test/v1/test_pb/bar.j5s.pb.go b/internal/testproto/gen/test/v1/test_pb/bar.j5s.pb.go deleted file mode 100644 index 8319813..0000000 --- a/internal/testproto/gen/test/v1/test_pb/bar.j5s.pb.go +++ /dev/null @@ -1,868 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.32.0 -// protoc (unknown) -// source: test/v1/bar.j5s.proto - -package test_pb - -import ( - _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" - _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" - _ "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" - date_j5t "github.com/pentops/j5/j5types/date_j5t" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type BarStatus int32 - -const ( - BarStatus_BAR_STATUS_UNSPECIFIED BarStatus = 0 - BarStatus_BAR_STATUS_ACTIVE BarStatus = 1 - BarStatus_BAR_STATUS_DELETED BarStatus = 2 -) - -// Enum value maps for BarStatus. -var ( - BarStatus_name = map[int32]string{ - 0: "BAR_STATUS_UNSPECIFIED", - 1: "BAR_STATUS_ACTIVE", - 2: "BAR_STATUS_DELETED", - } - BarStatus_value = map[string]int32{ - "BAR_STATUS_UNSPECIFIED": 0, - "BAR_STATUS_ACTIVE": 1, - "BAR_STATUS_DELETED": 2, - } -) - -func (x BarStatus) Enum() *BarStatus { - p := new(BarStatus) - *p = x - return p -} - -func (x BarStatus) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (BarStatus) Descriptor() protoreflect.EnumDescriptor { - return file_test_v1_bar_j5s_proto_enumTypes[0].Descriptor() -} - -func (BarStatus) Type() protoreflect.EnumType { - return &file_test_v1_bar_j5s_proto_enumTypes[0] -} - -func (x BarStatus) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use BarStatus.Descriptor instead. -func (BarStatus) EnumDescriptor() ([]byte, []int) { - return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{0} -} - -type BarKeys struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - BarId string `protobuf:"bytes,1,opt,name=bar_id,json=barId,proto3" json:"bar_id,omitempty"` - BarOtherId string `protobuf:"bytes,2,opt,name=bar_other_id,json=barOtherId,proto3" json:"bar_other_id,omitempty"` - DateKey *date_j5t.Date `protobuf:"bytes,3,opt,name=date_key,json=dateKey,proto3" json:"date_key,omitempty"` -} - -func (x *BarKeys) Reset() { - *x = BarKeys{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_bar_j5s_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarKeys) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarKeys) ProtoMessage() {} - -func (x *BarKeys) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_bar_j5s_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarKeys.ProtoReflect.Descriptor instead. -func (*BarKeys) Descriptor() ([]byte, []int) { - return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{0} -} - -func (x *BarKeys) GetBarId() string { - if x != nil { - return x.BarId - } - return "" -} - -func (x *BarKeys) GetBarOtherId() string { - if x != nil { - return x.BarOtherId - } - return "" -} - -func (x *BarKeys) GetDateKey() *date_j5t.Date { - if x != nil { - return x.DateKey - } - return nil -} - -type BarData struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - TenantId *string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3,oneof" json:"tenant_id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - Field string `protobuf:"bytes,3,opt,name=field,proto3" json:"field,omitempty"` -} - -func (x *BarData) Reset() { - *x = BarData{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_bar_j5s_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarData) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarData) ProtoMessage() {} - -func (x *BarData) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_bar_j5s_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarData.ProtoReflect.Descriptor instead. -func (*BarData) Descriptor() ([]byte, []int) { - return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{1} -} - -func (x *BarData) GetTenantId() string { - if x != nil && x.TenantId != nil { - return *x.TenantId - } - return "" -} - -func (x *BarData) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *BarData) GetField() string { - if x != nil { - return x.Field - } - return "" -} - -type BarState struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Metadata *psm_j5pb.StateMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - Keys *BarKeys `protobuf:"bytes,2,opt,name=keys,proto3" json:"keys,omitempty"` - Data *BarData `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - Status BarStatus `protobuf:"varint,4,opt,name=status,proto3,enum=test.v1.BarStatus" json:"status,omitempty"` -} - -func (x *BarState) Reset() { - *x = BarState{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_bar_j5s_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarState) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarState) ProtoMessage() {} - -func (x *BarState) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_bar_j5s_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarState.ProtoReflect.Descriptor instead. -func (*BarState) Descriptor() ([]byte, []int) { - return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{2} -} - -func (x *BarState) GetMetadata() *psm_j5pb.StateMetadata { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *BarState) GetKeys() *BarKeys { - if x != nil { - return x.Keys - } - return nil -} - -func (x *BarState) GetData() *BarData { - if x != nil { - return x.Data - } - return nil -} - -func (x *BarState) GetStatus() BarStatus { - if x != nil { - return x.Status - } - return BarStatus_BAR_STATUS_UNSPECIFIED -} - -type BarEventType struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to Type: - // - // *BarEventType_Created_ - // *BarEventType_Updated_ - // *BarEventType_Deleted_ - Type isBarEventType_Type `protobuf_oneof:"type"` -} - -func (x *BarEventType) Reset() { - *x = BarEventType{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_bar_j5s_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarEventType) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarEventType) ProtoMessage() {} - -func (x *BarEventType) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_bar_j5s_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarEventType.ProtoReflect.Descriptor instead. -func (*BarEventType) Descriptor() ([]byte, []int) { - return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{3} -} - -func (m *BarEventType) GetType() isBarEventType_Type { - if m != nil { - return m.Type - } - return nil -} - -func (x *BarEventType) GetCreated() *BarEventType_Created { - if x, ok := x.GetType().(*BarEventType_Created_); ok { - return x.Created - } - return nil -} - -func (x *BarEventType) GetUpdated() *BarEventType_Updated { - if x, ok := x.GetType().(*BarEventType_Updated_); ok { - return x.Updated - } - return nil -} - -func (x *BarEventType) GetDeleted() *BarEventType_Deleted { - if x, ok := x.GetType().(*BarEventType_Deleted_); ok { - return x.Deleted - } - return nil -} - -type isBarEventType_Type interface { - isBarEventType_Type() -} - -type BarEventType_Created_ struct { - Created *BarEventType_Created `protobuf:"bytes,1,opt,name=created,proto3,oneof"` -} - -type BarEventType_Updated_ struct { - Updated *BarEventType_Updated `protobuf:"bytes,2,opt,name=updated,proto3,oneof"` -} - -type BarEventType_Deleted_ struct { - Deleted *BarEventType_Deleted `protobuf:"bytes,3,opt,name=deleted,proto3,oneof"` -} - -func (*BarEventType_Created_) isBarEventType_Type() {} - -func (*BarEventType_Updated_) isBarEventType_Type() {} - -func (*BarEventType_Deleted_) isBarEventType_Type() {} - -type BarEvent struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Metadata *psm_j5pb.EventMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - Keys *BarKeys `protobuf:"bytes,2,opt,name=keys,proto3" json:"keys,omitempty"` - Event *BarEventType `protobuf:"bytes,3,opt,name=event,proto3" json:"event,omitempty"` -} - -func (x *BarEvent) Reset() { - *x = BarEvent{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_bar_j5s_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarEvent) ProtoMessage() {} - -func (x *BarEvent) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_bar_j5s_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarEvent.ProtoReflect.Descriptor instead. -func (*BarEvent) Descriptor() ([]byte, []int) { - return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{4} -} - -func (x *BarEvent) GetMetadata() *psm_j5pb.EventMetadata { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *BarEvent) GetKeys() *BarKeys { - if x != nil { - return x.Keys - } - return nil -} - -func (x *BarEvent) GetEvent() *BarEventType { - if x != nil { - return x.Event - } - return nil -} - -type BarEventType_Created struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Field string `protobuf:"bytes,2,opt,name=field,proto3" json:"field,omitempty"` -} - -func (x *BarEventType_Created) Reset() { - *x = BarEventType_Created{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_bar_j5s_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarEventType_Created) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarEventType_Created) ProtoMessage() {} - -func (x *BarEventType_Created) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_bar_j5s_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarEventType_Created.ProtoReflect.Descriptor instead. -func (*BarEventType_Created) Descriptor() ([]byte, []int) { - return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{3, 0} -} - -func (x *BarEventType_Created) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *BarEventType_Created) GetField() string { - if x != nil { - return x.Field - } - return "" -} - -type BarEventType_Updated struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Field string `protobuf:"bytes,2,opt,name=field,proto3" json:"field,omitempty"` -} - -func (x *BarEventType_Updated) Reset() { - *x = BarEventType_Updated{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_bar_j5s_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarEventType_Updated) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarEventType_Updated) ProtoMessage() {} - -func (x *BarEventType_Updated) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_bar_j5s_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarEventType_Updated.ProtoReflect.Descriptor instead. -func (*BarEventType_Updated) Descriptor() ([]byte, []int) { - return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{3, 1} -} - -func (x *BarEventType_Updated) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *BarEventType_Updated) GetField() string { - if x != nil { - return x.Field - } - return "" -} - -type BarEventType_Deleted struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *BarEventType_Deleted) Reset() { - *x = BarEventType_Deleted{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_bar_j5s_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarEventType_Deleted) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarEventType_Deleted) ProtoMessage() {} - -func (x *BarEventType_Deleted) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_bar_j5s_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarEventType_Deleted.ProtoReflect.Descriptor instead. -func (*BarEventType_Deleted) Descriptor() ([]byte, []int) { - return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{3, 2} -} - -var File_test_v1_bar_j5s_proto protoreflect.FileDescriptor - -var file_test_v1_bar_j5s_proto_rawDesc = []byte{ - 0x0a, 0x15, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x61, 0x72, 0x2e, 0x6a, 0x35, - 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, - 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, - 0x35, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6a, 0x35, 0x2f, 0x6c, - 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x6a, 0x35, 0x2f, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, 0x35, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x64, - 0x61, 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0xd5, 0x01, 0x0a, 0x07, 0x42, 0x61, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x33, 0x0a, - 0x06, 0x62, 0x61, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1c, 0xba, - 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, - 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x05, 0x62, 0x61, 0x72, - 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0c, 0x62, 0x61, 0x72, 0x5f, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1c, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, - 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, - 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x0a, 0x62, 0x61, 0x72, 0x4f, 0x74, 0x68, 0x65, 0x72, - 0x49, 0x64, 0x12, 0x40, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6a, 0x35, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, - 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x65, 0x42, 0x0d, 0xba, 0x48, - 0x03, 0xc8, 0x01, 0x01, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x07, 0x64, 0x61, 0x74, - 0x65, 0x4b, 0x65, 0x79, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, - 0x02, 0x07, 0x0a, 0x03, 0x62, 0x61, 0x72, 0x10, 0x01, 0x22, 0xd1, 0x01, 0x0a, 0x07, 0x42, 0x61, - 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x34, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x12, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, - 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0x48, 0x00, 0x52, 0x08, - 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x34, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x20, 0xc2, 0xff, 0x8e, 0x02, 0x03, - 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x12, 0x72, 0x10, 0x0a, 0x0e, 0x52, 0x0c, 0x08, - 0x01, 0x12, 0x08, 0x74, 0x73, 0x76, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x37, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x21, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x13, - 0x72, 0x11, 0x0a, 0x0f, 0x52, 0x0d, 0x08, 0x01, 0x12, 0x09, 0x74, 0x73, 0x76, 0x5f, 0x66, 0x69, - 0x65, 0x6c, 0x64, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, - 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x62, 0x61, 0x72, 0x10, 0x04, 0x42, - 0x0c, 0x0a, 0x0a, 0x5f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, 0x9f, 0x02, - 0x0a, 0x08, 0x42, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6a, - 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, - 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x12, 0x35, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x4b, 0x65, 0x79, - 0x73, 0x42, 0x0f, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, - 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, - 0x2e, 0x42, 0x61, 0x72, 0x44, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, - 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4b, 0x0a, - 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, - 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x42, 0x1f, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, 0x01, 0x02, 0x10, 0x01, 0xc2, 0xff, - 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x07, 0xa2, 0x01, 0x04, 0x52, 0x02, - 0x08, 0x01, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, - 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x62, 0x61, 0x72, 0x10, 0x02, 0x22, - 0xa3, 0x03, 0x0a, 0x0c, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x12, 0x42, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, - 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x07, 0x63, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, - 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x64, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, - 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, - 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, - 0x00, 0x48, 0x00, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x1a, 0x50, 0x0a, 0x07, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, - 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x05, - 0x66, 0x69, 0x65, 0x6c, 0x64, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x1a, 0x50, - 0x0a, 0x07, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, - 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, - 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, - 0x1a, 0x12, 0x0a, 0x07, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x3a, 0x07, 0xc2, 0xff, 0x8e, - 0x02, 0x02, 0x52, 0x00, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x42, 0x06, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xe6, 0x01, 0x0a, 0x08, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x35, 0x0a, 0x04, 0x6b, 0x65, 0x79, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, - 0x31, 0x2e, 0x42, 0x61, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0f, 0xba, 0x48, 0x03, 0xc8, 0x01, - 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, - 0x12, 0x47, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x42, 0x1a, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, - 0x8e, 0x02, 0x02, 0x62, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x07, 0xaa, 0x01, 0x04, 0x52, 0x02, - 0x08, 0x01, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, - 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x62, 0x61, 0x72, 0x10, 0x03, 0x2a, 0x56, - 0x0a, 0x09, 0x42, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x42, - 0x41, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x42, 0x41, 0x52, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x16, - 0x0a, 0x12, 0x42, 0x41, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x44, 0x45, 0x4c, - 0x45, 0x54, 0x45, 0x44, 0x10, 0x02, 0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, - 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x74, - 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x62, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_test_v1_bar_j5s_proto_rawDescOnce sync.Once - file_test_v1_bar_j5s_proto_rawDescData = file_test_v1_bar_j5s_proto_rawDesc -) - -func file_test_v1_bar_j5s_proto_rawDescGZIP() []byte { - file_test_v1_bar_j5s_proto_rawDescOnce.Do(func() { - file_test_v1_bar_j5s_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_v1_bar_j5s_proto_rawDescData) - }) - return file_test_v1_bar_j5s_proto_rawDescData -} - -var file_test_v1_bar_j5s_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_test_v1_bar_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 8) -var file_test_v1_bar_j5s_proto_goTypes = []interface{}{ - (BarStatus)(0), // 0: test.v1.BarStatus - (*BarKeys)(nil), // 1: test.v1.BarKeys - (*BarData)(nil), // 2: test.v1.BarData - (*BarState)(nil), // 3: test.v1.BarState - (*BarEventType)(nil), // 4: test.v1.BarEventType - (*BarEvent)(nil), // 5: test.v1.BarEvent - (*BarEventType_Created)(nil), // 6: test.v1.BarEventType.Created - (*BarEventType_Updated)(nil), // 7: test.v1.BarEventType.Updated - (*BarEventType_Deleted)(nil), // 8: test.v1.BarEventType.Deleted - (*date_j5t.Date)(nil), // 9: j5.types.date.v1.Date - (*psm_j5pb.StateMetadata)(nil), // 10: j5.state.v1.StateMetadata - (*psm_j5pb.EventMetadata)(nil), // 11: j5.state.v1.EventMetadata -} -var file_test_v1_bar_j5s_proto_depIdxs = []int32{ - 9, // 0: test.v1.BarKeys.date_key:type_name -> j5.types.date.v1.Date - 10, // 1: test.v1.BarState.metadata:type_name -> j5.state.v1.StateMetadata - 1, // 2: test.v1.BarState.keys:type_name -> test.v1.BarKeys - 2, // 3: test.v1.BarState.data:type_name -> test.v1.BarData - 0, // 4: test.v1.BarState.status:type_name -> test.v1.BarStatus - 6, // 5: test.v1.BarEventType.created:type_name -> test.v1.BarEventType.Created - 7, // 6: test.v1.BarEventType.updated:type_name -> test.v1.BarEventType.Updated - 8, // 7: test.v1.BarEventType.deleted:type_name -> test.v1.BarEventType.Deleted - 11, // 8: test.v1.BarEvent.metadata:type_name -> j5.state.v1.EventMetadata - 1, // 9: test.v1.BarEvent.keys:type_name -> test.v1.BarKeys - 4, // 10: test.v1.BarEvent.event:type_name -> test.v1.BarEventType - 11, // [11:11] is the sub-list for method output_type - 11, // [11:11] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name -} - -func init() { file_test_v1_bar_j5s_proto_init() } -func file_test_v1_bar_j5s_proto_init() { - if File_test_v1_bar_j5s_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_test_v1_bar_j5s_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarKeys); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_bar_j5s_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarData); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_bar_j5s_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarState); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_bar_j5s_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarEventType); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_bar_j5s_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarEvent); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_bar_j5s_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarEventType_Created); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_bar_j5s_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarEventType_Updated); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_bar_j5s_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarEventType_Deleted); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_test_v1_bar_j5s_proto_msgTypes[1].OneofWrappers = []interface{}{} - file_test_v1_bar_j5s_proto_msgTypes[3].OneofWrappers = []interface{}{ - (*BarEventType_Created_)(nil), - (*BarEventType_Updated_)(nil), - (*BarEventType_Deleted_)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_test_v1_bar_j5s_proto_rawDesc, - NumEnums: 1, - NumMessages: 8, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_test_v1_bar_j5s_proto_goTypes, - DependencyIndexes: file_test_v1_bar_j5s_proto_depIdxs, - EnumInfos: file_test_v1_bar_j5s_proto_enumTypes, - MessageInfos: file_test_v1_bar_j5s_proto_msgTypes, - }.Build() - File_test_v1_bar_j5s_proto = out.File - file_test_v1_bar_j5s_proto_rawDesc = nil - file_test_v1_bar_j5s_proto_goTypes = nil - file_test_v1_bar_j5s_proto_depIdxs = nil -} diff --git a/internal/testproto/gen/test/v1/test_pb/bar.j5s_j5.pb.go b/internal/testproto/gen/test/v1/test_pb/bar.j5s_j5.pb.go new file mode 100644 index 0000000..0ebe24c --- /dev/null +++ b/internal/testproto/gen/test/v1/test_pb/bar.j5s_j5.pb.go @@ -0,0 +1,23 @@ +// Code generated by protoc-gen-go-j5. DO NOT EDIT. + +package test_pb + +import ( + j5reflect "github.com/pentops/j5/lib/j5reflect" +) + +func (msg *BarKeys) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *BarData) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *BarState) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *BarEventType) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *BarEvent) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} diff --git a/internal/testproto/gen/test/v1/test_pb/bar.j5s_psm.pb.go b/internal/testproto/gen/test/v1/test_pb/bar.j5s_psm.pb.go deleted file mode 100644 index f43abcb..0000000 --- a/internal/testproto/gen/test/v1/test_pb/bar.j5s_psm.pb.go +++ /dev/null @@ -1,618 +0,0 @@ -// Code generated by protoc-gen-go-psm. DO NOT EDIT. - -package test_pb - -import ( - context "context" - fmt "fmt" - psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" - psm "github.com/pentops/protostate/psm" - sqrlx "github.com/pentops/sqrlx.go/sqrlx" -) - -// PSM BarPSM - -type BarPSM = psm.StateMachine[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] - -type BarPSMDB = psm.DBStateMachine[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] - -type BarPSMEventSpec = psm.EventSpec[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] - -type BarPSMHookBaton = psm.HookBaton[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] - -type BarPSMFullBaton = psm.CallbackBaton[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] - -type BarPSMEventKey = string - -const ( - BarPSMEventNil BarPSMEventKey = "" - BarPSMEventCreated BarPSMEventKey = "created" - BarPSMEventUpdated BarPSMEventKey = "updated" - BarPSMEventDeleted BarPSMEventKey = "deleted" -) - -// EXTEND BarKeys with the psm.IKeyset interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *BarKeys) PSMIsSet() bool { - return msg != nil -} - -// PSMFullName returns the full name of state machine with package prefix -func (msg *BarKeys) PSMFullName() string { - return "test.v1.bar" -} -func (msg *BarKeys) PSMKeyValues() (map[string]any, error) { - keyset := map[string]any{ - "bar_id": msg.BarId, - "bar_other_id": msg.BarOtherId, - "date_key": msg.DateKey, - } - return keyset, nil -} - -// EXTEND BarState with the psm.IState interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *BarState) PSMIsSet() bool { - return msg != nil -} - -func (msg *BarState) PSMMetadata() *psm_j5pb.StateMetadata { - if msg.Metadata == nil { - msg.Metadata = &psm_j5pb.StateMetadata{} - } - return msg.Metadata -} - -func (msg *BarState) PSMKeys() *BarKeys { - return msg.Keys -} - -func (msg *BarState) SetStatus(status BarStatus) { - msg.Status = status -} - -func (msg *BarState) SetPSMKeys(inner *BarKeys) { - msg.Keys = inner -} - -func (msg *BarState) PSMData() *BarData { - if msg.Data == nil { - msg.Data = &BarData{} - } - return msg.Data -} - -// EXTEND BarData with the psm.IStateData interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *BarData) PSMIsSet() bool { - return msg != nil -} - -// EXTEND BarEvent with the psm.IEvent interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *BarEvent) PSMIsSet() bool { - return msg != nil -} - -func (msg *BarEvent) PSMMetadata() *psm_j5pb.EventMetadata { - if msg.Metadata == nil { - msg.Metadata = &psm_j5pb.EventMetadata{} - } - return msg.Metadata -} - -func (msg *BarEvent) PSMKeys() *BarKeys { - return msg.Keys -} - -func (msg *BarEvent) SetPSMKeys(inner *BarKeys) { - msg.Keys = inner -} - -// PSMEventKey returns the BarPSMEventPSMEventKey for the event, implementing psm.IEvent -func (msg *BarEvent) PSMEventKey() BarPSMEventKey { - tt := msg.UnwrapPSMEvent() - if tt == nil { - return BarPSMEventNil - } - return tt.PSMEventKey() -} - -// UnwrapPSMEvent implements psm.IEvent, returning the inner event message -func (msg *BarEvent) UnwrapPSMEvent() BarPSMEvent { - if msg == nil { - return nil - } - if msg.Event == nil { - return nil - } - switch v := msg.Event.Type.(type) { - case *BarEventType_Created_: - return v.Created - case *BarEventType_Updated_: - return v.Updated - case *BarEventType_Deleted_: - return v.Deleted - default: - return nil - } -} - -// SetPSMEvent sets the inner event message from a concrete type, implementing psm.IEvent -func (msg *BarEvent) SetPSMEvent(inner BarPSMEvent) error { - if msg.Event == nil { - msg.Event = &BarEventType{} - } - switch v := inner.(type) { - case *BarEventType_Created: - msg.Event.Type = &BarEventType_Created_{Created: v} - case *BarEventType_Updated: - msg.Event.Type = &BarEventType_Updated_{Updated: v} - case *BarEventType_Deleted: - msg.Event.Type = &BarEventType_Deleted_{Deleted: v} - default: - return fmt.Errorf("invalid type %T for BarEventType", v) - } - return nil -} - -type BarPSMEvent interface { - psm.IInnerEvent - PSMEventKey() BarPSMEventKey -} - -// EXTEND BarEventType_Created with the BarPSMEvent interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *BarEventType_Created) PSMIsSet() bool { - return msg != nil -} - -func (*BarEventType_Created) PSMEventKey() BarPSMEventKey { - return BarPSMEventCreated -} - -// EXTEND BarEventType_Updated with the BarPSMEvent interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *BarEventType_Updated) PSMIsSet() bool { - return msg != nil -} - -func (*BarEventType_Updated) PSMEventKey() BarPSMEventKey { - return BarPSMEventUpdated -} - -// EXTEND BarEventType_Deleted with the BarPSMEvent interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *BarEventType_Deleted) PSMIsSet() bool { - return msg != nil -} - -func (*BarEventType_Deleted) PSMEventKey() BarPSMEventKey { - return BarPSMEventDeleted -} - -func BarPSMBuilder() *psm.StateMachineConfig[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] { - return &psm.StateMachineConfig[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent - ]{} -} - -// BarPSMMutation runs at the start of a transition to merge the event information into the state data object. The state object is mutable in this context. -func BarPSMMutation[SE BarPSMEvent](cb func(*BarData, SE) error) psm.TransitionMutation[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent - SE, // Specific event type for the transition -] { - return psm.TransitionMutation[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent - SE, // Specific event type for the transition - ](cb) -} - -// BarPSMLogicHook runs after the mutation is complete. This hook can trigger side effects, including chained events, which are additional events processed by the state machine. Use this for Business Logic which determines the 'next step' in processing. -func BarPSMLogicHook[ - SE BarPSMEvent, -]( - cb func( - context.Context, - BarPSMHookBaton, - *BarState, - SE, - ) error) psm.TransitionHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] { - eventType := (*new(SE)).PSMEventKey() - return psm.TransitionHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func(ctx context.Context, tx sqrlx.Transaction, baton BarPSMFullBaton, state *BarState, event *BarEvent) error { - asType, ok := any(event.UnwrapPSMEvent()).(SE) - if !ok { - name := event.ProtoReflect().Descriptor().FullName() - return fmt.Errorf("unexpected event type in transition: %s [IE] does not match [SE] (%T)", name, new(SE)) - } - return cb(ctx, baton, state, asType) - }, - EventType: eventType, - RunOnFollow: false, - } -} - -// BarPSMDataHook runs after the mutations, and can be used to update data in tables which are not controlled as the state machine, e.g. for pre-calculating fields for performance reasons. Use of this hook prevents (future) transaction optimizations, as the transaction state when the function is called must needs to match the processing state, but only for this single transition, unlike the GeneralEventDataHook. -func BarPSMDataHook[ - SE BarPSMEvent, -]( - cb func( - context.Context, - sqrlx.Transaction, - *BarState, - SE, - ) error) psm.TransitionHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] { - eventType := (*new(SE)).PSMEventKey() - return psm.TransitionHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func(ctx context.Context, tx sqrlx.Transaction, baton BarPSMFullBaton, state *BarState, event *BarEvent) error { - asType, ok := any(event.UnwrapPSMEvent()).(SE) - if !ok { - name := event.ProtoReflect().Descriptor().FullName() - return fmt.Errorf("unexpected event type in transition: %s [IE] does not match [SE] (%T)", name, new(SE)) - } - return cb(ctx, tx, state, asType) - }, - EventType: eventType, - RunOnFollow: true, - } -} - -// BarPSMLinkHook runs after the mutation and logic hook, and can be used to link the state machine to other state machines in the same database transaction -func BarPSMLinkHook[ - SE BarPSMEvent, - DK psm.IKeyset, - DIE psm.IInnerEvent, -]( - linkDestination psm.LinkDestination[DK, DIE], - cb func( - context.Context, - *BarState, - SE, - func(DK, DIE), - ) error) psm.TransitionHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] { - eventType := (*new(SE)).PSMEventKey() - wrapped := func(ctx context.Context, tx sqrlx.Transaction, state *BarState, event SE, add func(DK, DIE)) error { - return cb(ctx, state, event, add) - } - return psm.TransitionHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func(ctx context.Context, tx sqrlx.Transaction, baton BarPSMFullBaton, state *BarState, event *BarEvent) error { - return psm.RunLinkHook(ctx, linkDestination, wrapped, tx, state, event) - }, - EventType: eventType, - RunOnFollow: false, - } -} - -// BarPSMLinkDBHook like LinkHook, but has access to the current transaction for reads only (not enforced), use in place of controller logic to look up existing state. -func BarPSMLinkDBHook[ - SE BarPSMEvent, - DK psm.IKeyset, - DIE psm.IInnerEvent, -]( - linkDestination psm.LinkDestination[DK, DIE], - cb func( - context.Context, - sqrlx.Transaction, - *BarState, - SE, - func(DK, DIE), - ) error) psm.TransitionHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] { - eventType := (*new(SE)).PSMEventKey() - return psm.TransitionHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func(ctx context.Context, tx sqrlx.Transaction, baton BarPSMFullBaton, state *BarState, event *BarEvent) error { - return psm.RunLinkHook(ctx, linkDestination, cb, tx, state, event) - }, - EventType: eventType, - RunOnFollow: false, - } -} - -// BarPSMGeneralLogicHook runs once per transition at the state-machine level regardless of which transition / event is being processed. It runs exactly once per transition, with the state object in the final state after the transition but prior to processing any further events. Chained events are added to the *end* of the event queue for the transaction, and side effects are published (as always) when the transaction is committed. The function MUST be pure, i.e. It MUST NOT produce any side-effects outside of the HookBaton, and MUST NOT modify the state. -func BarPSMGeneralLogicHook( - cb func( - context.Context, - BarPSMHookBaton, - *BarState, - *BarEvent, - ) error) psm.GeneralEventHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] { - return psm.GeneralEventHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func( - ctx context.Context, - tx sqrlx.Transaction, - baton BarPSMFullBaton, - state *BarState, - event *BarEvent, - ) error { - return cb(ctx, baton, state, event) - }, - RunOnFollow: false, - } -} - -// BarPSMGeneralStateDataHook runs at the state-machine level regardless of which transition / event is being processed. It runs at-least once before committing a database transaction after multiple transitions are complete. This hook has access only to the final state after the transitions and is used to update other tables based on the resulting state. It MUST be idempotent, it may be called after injecting externally-held state data. -func BarPSMGeneralStateDataHook( - cb func( - context.Context, - sqrlx.Transaction, - *BarState, - ) error) psm.GeneralStateHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] { - return psm.GeneralStateHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func( - ctx context.Context, - tx sqrlx.Transaction, - baton BarPSMFullBaton, - state *BarState, - ) error { - return cb(ctx, tx, state) - }, - RunOnFollow: true, - } -} - -// BarPSMGeneralEventDataHook runs after each transition at the state-machine level regardless of which transition / event is being processed. It runs exactly once per transition, before any other events are processed. The presence of this hook type prevents (future) transaction optimizations, so should be used sparingly. -func BarPSMGeneralEventDataHook( - cb func( - context.Context, - sqrlx.Transaction, - *BarState, - *BarEvent, - ) error) psm.GeneralEventHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] { - return psm.GeneralEventHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func( - ctx context.Context, - tx sqrlx.Transaction, - baton BarPSMFullBaton, - state *BarState, - event *BarEvent, - ) error { - return cb(ctx, tx, state, event) - }, - RunOnFollow: true, - } -} - -// BarPSMEventPublishHook EventPublishHook runs for each transition, at least once before committing a database transaction after multiple transitions are complete. It should publish a derived version of the event using the publisher. -func BarPSMEventPublishHook( - cb func( - context.Context, - psm.Publisher, - *BarState, - *BarEvent, - ) error) psm.GeneralEventHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] { - return psm.GeneralEventHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func( - ctx context.Context, - tx sqrlx.Transaction, - baton BarPSMFullBaton, - state *BarState, - event *BarEvent, - ) error { - return cb(ctx, baton, state, event) - }, - RunOnFollow: false, - } -} - -// BarPSMUpsertPublishHook runs for each transition, at least once before committing a database transaction after multiple transitions are complete. It should publish a derived version of the event using the publisher. -func BarPSMUpsertPublishHook( - cb func( - context.Context, - psm.Publisher, - *BarState, - ) error) psm.GeneralStateHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent -] { - return psm.GeneralStateHook[ - *BarKeys, // implements psm.IKeyset - *BarState, // implements psm.IState - BarStatus, // implements psm.IStatusEnum - *BarData, // implements psm.IStateData - *BarEvent, // implements psm.IEvent - BarPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func( - ctx context.Context, - tx sqrlx.Transaction, - baton BarPSMFullBaton, - state *BarState, - ) error { - return cb(ctx, baton, state) - }, - RunOnFollow: false, - } -} - -func (event *BarEvent) EventPublishMetadata() *psm_j5pb.EventPublishMetadata { - tenantKeys := make([]*psm_j5pb.EventTenant, 0) - return &psm_j5pb.EventPublishMetadata{ - EventId: event.Metadata.EventId, - Sequence: event.Metadata.Sequence, - Timestamp: event.Metadata.Timestamp, - Cause: event.Metadata.Cause, - Auth: &psm_j5pb.PublishAuth{ - TenantKeys: tenantKeys, - }, - } -} diff --git a/internal/testproto/gen/test/v1/test_pb/bar.j5s_sugar.pb.go b/internal/testproto/gen/test/v1/test_pb/bar.j5s_sugar.pb.go deleted file mode 100644 index 7397083..0000000 --- a/internal/testproto/gen/test/v1/test_pb/bar.j5s_sugar.pb.go +++ /dev/null @@ -1,120 +0,0 @@ -// Code generated by protoc-gen-go-sugar. DO NOT EDIT. - -package test_pb - -import ( - driver "database/sql/driver" - fmt "fmt" - proto "google.golang.org/protobuf/proto" -) - -// BarEventType is a oneof wrapper -type BarEventTypeKey string - -const ( - BarEvent_Created BarEventTypeKey = "created" - BarEvent_Updated BarEventTypeKey = "updated" - BarEvent_Deleted BarEventTypeKey = "deleted" -) - -func (x *BarEventType) TypeKey() (BarEventTypeKey, bool) { - switch x.Type.(type) { - case *BarEventType_Created_: - return BarEvent_Created, true - case *BarEventType_Updated_: - return BarEvent_Updated, true - case *BarEventType_Deleted_: - return BarEvent_Deleted, true - default: - return "", false - } -} - -type IsBarEventTypeWrappedType interface { - TypeKey() BarEventTypeKey - proto.Message -} - -func (x *BarEventType) Set(val IsBarEventTypeWrappedType) { - switch v := val.(type) { - case *BarEventType_Created: - x.Type = &BarEventType_Created_{Created: v} - case *BarEventType_Updated: - x.Type = &BarEventType_Updated_{Updated: v} - case *BarEventType_Deleted: - x.Type = &BarEventType_Deleted_{Deleted: v} - } -} -func (x *BarEventType) Get() IsBarEventTypeWrappedType { - switch v := x.Type.(type) { - case *BarEventType_Created_: - return v.Created - case *BarEventType_Updated_: - return v.Updated - case *BarEventType_Deleted_: - return v.Deleted - default: - return nil - } -} -func (x *BarEventType_Created) TypeKey() BarEventTypeKey { - return BarEvent_Created -} -func (x *BarEventType_Updated) TypeKey() BarEventTypeKey { - return BarEvent_Updated -} -func (x *BarEventType_Deleted) TypeKey() BarEventTypeKey { - return BarEvent_Deleted -} - -type IsBarEventType_Type = isBarEventType_Type - -// BarStatus -const ( - BarStatus_UNSPECIFIED BarStatus = 0 - BarStatus_ACTIVE BarStatus = 1 - BarStatus_DELETED BarStatus = 2 -) - -var ( - BarStatus_name_short = map[int32]string{ - 0: "UNSPECIFIED", - 1: "ACTIVE", - 2: "DELETED", - } - BarStatus_value_short = map[string]int32{ - "UNSPECIFIED": 0, - "ACTIVE": 1, - "DELETED": 2, - } - BarStatus_value_either = map[string]int32{ - "UNSPECIFIED": 0, - "BAR_STATUS_UNSPECIFIED": 0, - "ACTIVE": 1, - "BAR_STATUS_ACTIVE": 1, - "DELETED": 2, - "BAR_STATUS_DELETED": 2, - } -) - -// ShortString returns the un-prefixed string representation of the enum value -func (x BarStatus) ShortString() string { - return BarStatus_name_short[int32(x)] -} -func (x BarStatus) Value() (driver.Value, error) { - return []uint8(x.ShortString()), nil -} -func (x *BarStatus) Scan(value interface{}) error { - var strVal string - switch vt := value.(type) { - case []uint8: - strVal = string(vt) - case string: - strVal = vt - default: - return fmt.Errorf("invalid type %T", value) - } - val := BarStatus_value_either[strVal] - *x = BarStatus(val) - return nil -} diff --git a/internal/testproto/gen/test/v1/test_pb/foo.j5s.pb.go b/internal/testproto/gen/test/v1/test_pb/foo.j5s.pb.go deleted file mode 100644 index 47cbb18..0000000 --- a/internal/testproto/gen/test/v1/test_pb/foo.j5s.pb.go +++ /dev/null @@ -1,1201 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.32.0 -// protoc (unknown) -// source: test/v1/foo.j5s.proto - -package test_pb - -import ( - _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" - _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" - _ "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type FooStatus int32 - -const ( - FooStatus_FOO_STATUS_UNSPECIFIED FooStatus = 0 - FooStatus_FOO_STATUS_ACTIVE FooStatus = 1 - FooStatus_FOO_STATUS_DELETED FooStatus = 2 -) - -// Enum value maps for FooStatus. -var ( - FooStatus_name = map[int32]string{ - 0: "FOO_STATUS_UNSPECIFIED", - 1: "FOO_STATUS_ACTIVE", - 2: "FOO_STATUS_DELETED", - } - FooStatus_value = map[string]int32{ - "FOO_STATUS_UNSPECIFIED": 0, - "FOO_STATUS_ACTIVE": 1, - "FOO_STATUS_DELETED": 2, - } -) - -func (x FooStatus) Enum() *FooStatus { - p := new(FooStatus) - *p = x - return p -} - -func (x FooStatus) String() string { - return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) -} - -func (FooStatus) Descriptor() protoreflect.EnumDescriptor { - return file_test_v1_foo_j5s_proto_enumTypes[0].Descriptor() -} - -func (FooStatus) Type() protoreflect.EnumType { - return &file_test_v1_foo_j5s_proto_enumTypes[0] -} - -func (x FooStatus) Number() protoreflect.EnumNumber { - return protoreflect.EnumNumber(x) -} - -// Deprecated: Use FooStatus.Descriptor instead. -func (FooStatus) EnumDescriptor() ([]byte, []int) { - return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{0} -} - -type FooKeys struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - FooId string `protobuf:"bytes,1,opt,name=foo_id,json=fooId,proto3" json:"foo_id,omitempty"` - TenantId *string `protobuf:"bytes,2,opt,name=tenant_id,json=tenantId,proto3,oneof" json:"tenant_id,omitempty"` - MetaTenantId string `protobuf:"bytes,3,opt,name=meta_tenant_id,json=metaTenantId,proto3" json:"meta_tenant_id,omitempty"` -} - -func (x *FooKeys) Reset() { - *x = FooKeys{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_foo_j5s_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooKeys) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooKeys) ProtoMessage() {} - -func (x *FooKeys) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_foo_j5s_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooKeys.ProtoReflect.Descriptor instead. -func (*FooKeys) Descriptor() ([]byte, []int) { - return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{0} -} - -func (x *FooKeys) GetFooId() string { - if x != nil { - return x.FooId - } - return "" -} - -func (x *FooKeys) GetTenantId() string { - if x != nil && x.TenantId != nil { - return *x.TenantId - } - return "" -} - -func (x *FooKeys) GetMetaTenantId() string { - if x != nil { - return x.MetaTenantId - } - return "" -} - -type FooData struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Field string `protobuf:"bytes,2,opt,name=field,proto3" json:"field,omitempty"` - Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` - Characteristics *FooCharacteristics `protobuf:"bytes,4,opt,name=characteristics,proto3" json:"characteristics,omitempty"` - Profiles []*FooProfile `protobuf:"bytes,5,rep,name=profiles,proto3" json:"profiles,omitempty"` -} - -func (x *FooData) Reset() { - *x = FooData{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_foo_j5s_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooData) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooData) ProtoMessage() {} - -func (x *FooData) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_foo_j5s_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooData.ProtoReflect.Descriptor instead. -func (*FooData) Descriptor() ([]byte, []int) { - return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{1} -} - -func (x *FooData) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *FooData) GetField() string { - if x != nil { - return x.Field - } - return "" -} - -func (x *FooData) GetDescription() string { - if x != nil && x.Description != nil { - return *x.Description - } - return "" -} - -func (x *FooData) GetCharacteristics() *FooCharacteristics { - if x != nil { - return x.Characteristics - } - return nil -} - -func (x *FooData) GetProfiles() []*FooProfile { - if x != nil { - return x.Profiles - } - return nil -} - -type FooState struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Metadata *psm_j5pb.StateMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - Keys *FooKeys `protobuf:"bytes,2,opt,name=keys,proto3" json:"keys,omitempty"` - Data *FooData `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - Status FooStatus `protobuf:"varint,4,opt,name=status,proto3,enum=test.v1.FooStatus" json:"status,omitempty"` -} - -func (x *FooState) Reset() { - *x = FooState{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_foo_j5s_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooState) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooState) ProtoMessage() {} - -func (x *FooState) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_foo_j5s_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooState.ProtoReflect.Descriptor instead. -func (*FooState) Descriptor() ([]byte, []int) { - return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{2} -} - -func (x *FooState) GetMetadata() *psm_j5pb.StateMetadata { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *FooState) GetKeys() *FooKeys { - if x != nil { - return x.Keys - } - return nil -} - -func (x *FooState) GetData() *FooData { - if x != nil { - return x.Data - } - return nil -} - -func (x *FooState) GetStatus() FooStatus { - if x != nil { - return x.Status - } - return FooStatus_FOO_STATUS_UNSPECIFIED -} - -type FooEventType struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to Type: - // - // *FooEventType_Created_ - // *FooEventType_Updated_ - // *FooEventType_Deleted_ - Type isFooEventType_Type `protobuf_oneof:"type"` -} - -func (x *FooEventType) Reset() { - *x = FooEventType{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_foo_j5s_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooEventType) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooEventType) ProtoMessage() {} - -func (x *FooEventType) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_foo_j5s_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooEventType.ProtoReflect.Descriptor instead. -func (*FooEventType) Descriptor() ([]byte, []int) { - return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{3} -} - -func (m *FooEventType) GetType() isFooEventType_Type { - if m != nil { - return m.Type - } - return nil -} - -func (x *FooEventType) GetCreated() *FooEventType_Created { - if x, ok := x.GetType().(*FooEventType_Created_); ok { - return x.Created - } - return nil -} - -func (x *FooEventType) GetUpdated() *FooEventType_Updated { - if x, ok := x.GetType().(*FooEventType_Updated_); ok { - return x.Updated - } - return nil -} - -func (x *FooEventType) GetDeleted() *FooEventType_Deleted { - if x, ok := x.GetType().(*FooEventType_Deleted_); ok { - return x.Deleted - } - return nil -} - -type isFooEventType_Type interface { - isFooEventType_Type() -} - -type FooEventType_Created_ struct { - Created *FooEventType_Created `protobuf:"bytes,1,opt,name=created,proto3,oneof"` -} - -type FooEventType_Updated_ struct { - Updated *FooEventType_Updated `protobuf:"bytes,2,opt,name=updated,proto3,oneof"` -} - -type FooEventType_Deleted_ struct { - Deleted *FooEventType_Deleted `protobuf:"bytes,3,opt,name=deleted,proto3,oneof"` -} - -func (*FooEventType_Created_) isFooEventType_Type() {} - -func (*FooEventType_Updated_) isFooEventType_Type() {} - -func (*FooEventType_Deleted_) isFooEventType_Type() {} - -type FooEvent struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Metadata *psm_j5pb.EventMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - Keys *FooKeys `protobuf:"bytes,2,opt,name=keys,proto3" json:"keys,omitempty"` - Event *FooEventType `protobuf:"bytes,3,opt,name=event,proto3" json:"event,omitempty"` -} - -func (x *FooEvent) Reset() { - *x = FooEvent{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_foo_j5s_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooEvent) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooEvent) ProtoMessage() {} - -func (x *FooEvent) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_foo_j5s_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooEvent.ProtoReflect.Descriptor instead. -func (*FooEvent) Descriptor() ([]byte, []int) { - return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{4} -} - -func (x *FooEvent) GetMetadata() *psm_j5pb.EventMetadata { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *FooEvent) GetKeys() *FooKeys { - if x != nil { - return x.Keys - } - return nil -} - -func (x *FooEvent) GetEvent() *FooEventType { - if x != nil { - return x.Event - } - return nil -} - -type FooCharacteristics struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Weight int64 `protobuf:"varint,1,opt,name=weight,proto3" json:"weight,omitempty"` - Height int64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` - Length int64 `protobuf:"varint,3,opt,name=length,proto3" json:"length,omitempty"` -} - -func (x *FooCharacteristics) Reset() { - *x = FooCharacteristics{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_foo_j5s_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooCharacteristics) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooCharacteristics) ProtoMessage() {} - -func (x *FooCharacteristics) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_foo_j5s_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooCharacteristics.ProtoReflect.Descriptor instead. -func (*FooCharacteristics) Descriptor() ([]byte, []int) { - return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{5} -} - -func (x *FooCharacteristics) GetWeight() int64 { - if x != nil { - return x.Weight - } - return 0 -} - -func (x *FooCharacteristics) GetHeight() int64 { - if x != nil { - return x.Height - } - return 0 -} - -func (x *FooCharacteristics) GetLength() int64 { - if x != nil { - return x.Length - } - return 0 -} - -type FooProfile struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Place int64 `protobuf:"varint,1,opt,name=place,proto3" json:"place,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` -} - -func (x *FooProfile) Reset() { - *x = FooProfile{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_foo_j5s_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooProfile) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooProfile) ProtoMessage() {} - -func (x *FooProfile) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_foo_j5s_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooProfile.ProtoReflect.Descriptor instead. -func (*FooProfile) Descriptor() ([]byte, []int) { - return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{6} -} - -func (x *FooProfile) GetPlace() int64 { - if x != nil { - return x.Place - } - return 0 -} - -func (x *FooProfile) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -type FooEventType_Created struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Field string `protobuf:"bytes,2,opt,name=field,proto3" json:"field,omitempty"` - Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` - Weight *int64 `protobuf:"varint,4,opt,name=weight,proto3,oneof" json:"weight,omitempty"` - Height *int64 `protobuf:"varint,5,opt,name=height,proto3,oneof" json:"height,omitempty"` - Length *int64 `protobuf:"varint,6,opt,name=length,proto3,oneof" json:"length,omitempty"` - Profiles []*FooProfile `protobuf:"bytes,7,rep,name=profiles,proto3" json:"profiles,omitempty"` -} - -func (x *FooEventType_Created) Reset() { - *x = FooEventType_Created{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_foo_j5s_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooEventType_Created) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooEventType_Created) ProtoMessage() {} - -func (x *FooEventType_Created) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_foo_j5s_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooEventType_Created.ProtoReflect.Descriptor instead. -func (*FooEventType_Created) Descriptor() ([]byte, []int) { - return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{3, 0} -} - -func (x *FooEventType_Created) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *FooEventType_Created) GetField() string { - if x != nil { - return x.Field - } - return "" -} - -func (x *FooEventType_Created) GetDescription() string { - if x != nil && x.Description != nil { - return *x.Description - } - return "" -} - -func (x *FooEventType_Created) GetWeight() int64 { - if x != nil && x.Weight != nil { - return *x.Weight - } - return 0 -} - -func (x *FooEventType_Created) GetHeight() int64 { - if x != nil && x.Height != nil { - return *x.Height - } - return 0 -} - -func (x *FooEventType_Created) GetLength() int64 { - if x != nil && x.Length != nil { - return *x.Length - } - return 0 -} - -func (x *FooEventType_Created) GetProfiles() []*FooProfile { - if x != nil { - return x.Profiles - } - return nil -} - -type FooEventType_Updated struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - Field string `protobuf:"bytes,2,opt,name=field,proto3" json:"field,omitempty"` - Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` - Weight *int64 `protobuf:"varint,4,opt,name=weight,proto3,oneof" json:"weight,omitempty"` - Height *int64 `protobuf:"varint,5,opt,name=height,proto3,oneof" json:"height,omitempty"` - Length *int64 `protobuf:"varint,6,opt,name=length,proto3,oneof" json:"length,omitempty"` - Profiles []*FooProfile `protobuf:"bytes,7,rep,name=profiles,proto3" json:"profiles,omitempty"` - Delete bool `protobuf:"varint,8,opt,name=delete,proto3" json:"delete,omitempty"` -} - -func (x *FooEventType_Updated) Reset() { - *x = FooEventType_Updated{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_foo_j5s_proto_msgTypes[8] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooEventType_Updated) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooEventType_Updated) ProtoMessage() {} - -func (x *FooEventType_Updated) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_foo_j5s_proto_msgTypes[8] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooEventType_Updated.ProtoReflect.Descriptor instead. -func (*FooEventType_Updated) Descriptor() ([]byte, []int) { - return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{3, 1} -} - -func (x *FooEventType_Updated) GetName() string { - if x != nil { - return x.Name - } - return "" -} - -func (x *FooEventType_Updated) GetField() string { - if x != nil { - return x.Field - } - return "" -} - -func (x *FooEventType_Updated) GetDescription() string { - if x != nil && x.Description != nil { - return *x.Description - } - return "" -} - -func (x *FooEventType_Updated) GetWeight() int64 { - if x != nil && x.Weight != nil { - return *x.Weight - } - return 0 -} - -func (x *FooEventType_Updated) GetHeight() int64 { - if x != nil && x.Height != nil { - return *x.Height - } - return 0 -} - -func (x *FooEventType_Updated) GetLength() int64 { - if x != nil && x.Length != nil { - return *x.Length - } - return 0 -} - -func (x *FooEventType_Updated) GetProfiles() []*FooProfile { - if x != nil { - return x.Profiles - } - return nil -} - -func (x *FooEventType_Updated) GetDelete() bool { - if x != nil { - return x.Delete - } - return false -} - -type FooEventType_Deleted struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Delete bool `protobuf:"varint,1,opt,name=delete,proto3" json:"delete,omitempty"` -} - -func (x *FooEventType_Deleted) Reset() { - *x = FooEventType_Deleted{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_foo_j5s_proto_msgTypes[9] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooEventType_Deleted) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooEventType_Deleted) ProtoMessage() {} - -func (x *FooEventType_Deleted) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_foo_j5s_proto_msgTypes[9] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooEventType_Deleted.ProtoReflect.Descriptor instead. -func (*FooEventType_Deleted) Descriptor() ([]byte, []int) { - return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{3, 2} -} - -func (x *FooEventType_Deleted) GetDelete() bool { - if x != nil { - return x.Delete - } - return false -} - -var File_test_v1_foo_j5s_proto protoreflect.FileDescriptor - -var file_test_v1_foo_j5s_proto_rawDesc = []byte{ - 0x0a, 0x15, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x6f, 0x6f, 0x2e, 0x6a, 0x35, - 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, - 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, - 0x35, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6a, 0x35, 0x2f, 0x6c, - 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x6a, 0x35, 0x2f, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x93, 0x02, 0x0a, 0x07, 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, - 0x12, 0x43, 0x0a, 0x06, 0x66, 0x6f, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x2c, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, - 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x8a, 0xf7, - 0x98, 0xc6, 0x02, 0x0a, 0x72, 0x08, 0x1a, 0x06, 0x12, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x05, - 0x66, 0x6f, 0x6f, 0x49, 0x64, 0x12, 0x51, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2f, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, - 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, - 0x08, 0x2a, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x72, - 0x08, 0x1a, 0x06, 0x12, 0x04, 0x52, 0x02, 0x08, 0x01, 0x48, 0x00, 0x52, 0x08, 0x74, 0x65, 0x6e, - 0x61, 0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x6d, 0x65, 0x74, 0x61, - 0x5f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x27, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, - 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x72, - 0x08, 0x1a, 0x06, 0x12, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x54, - 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, - 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x10, 0x01, 0x42, 0x0c, 0x0a, 0x0a, - 0x5f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, 0xf8, 0x02, 0x0a, 0x07, 0x46, - 0x6f, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x12, 0x34, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x20, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, - 0x98, 0xc6, 0x02, 0x12, 0x72, 0x10, 0x0a, 0x0e, 0x52, 0x0c, 0x08, 0x01, 0x12, 0x08, 0x74, 0x73, - 0x76, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x05, - 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x21, 0xc2, 0xff, 0x8e, - 0x02, 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x13, 0x72, 0x11, 0x0a, 0x0f, 0x52, - 0x0d, 0x08, 0x01, 0x12, 0x09, 0x74, 0x73, 0x76, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, - 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x4e, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xc2, 0xff, 0x8e, 0x02, - 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x19, 0x72, 0x17, 0x0a, 0x15, 0x52, 0x13, - 0x08, 0x01, 0x12, 0x0f, 0x74, 0x73, 0x76, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, - 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x4e, 0x0a, 0x0f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, - 0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x43, 0x68, 0x61, 0x72, - 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x42, 0x07, 0xc2, 0xff, 0x8e, - 0x02, 0x02, 0x52, 0x00, 0x52, 0x0f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, - 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, - 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, - 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x42, 0x08, 0xc2, 0xff, - 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, - 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, - 0x66, 0x6f, 0x6f, 0x10, 0x04, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb2, 0x02, 0x0a, 0x08, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x35, 0x0a, 0x04, 0x6b, 0x65, 0x79, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, - 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0f, 0xba, 0x48, 0x03, 0xc8, 0x01, - 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, - 0x12, 0x33, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, - 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x44, 0x61, 0x74, 0x61, - 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x5e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, - 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x32, 0xba, 0x48, 0x08, 0xc8, 0x01, - 0x01, 0x82, 0x01, 0x02, 0x10, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x8a, 0xf7, 0x98, - 0xc6, 0x02, 0x1a, 0xa2, 0x01, 0x17, 0x52, 0x15, 0x08, 0x01, 0x12, 0x11, 0x46, 0x4f, 0x4f, 0x5f, - 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, - 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x10, 0x02, 0x22, 0x8d, 0x08, 0x0a, 0x0c, 0x46, - 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x42, 0x0a, 0x07, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x07, 0xc2, 0xff, 0x8e, - 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, - 0x42, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x42, - 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, - 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x64, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x07, - 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x1a, 0xe2, 0x02, 0x0a, 0x07, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x12, 0x2f, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, - 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, - 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, 0x01, 0x52, 0x06, - 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x68, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, - 0xfa, 0x01, 0x00, 0x48, 0x02, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, - 0x12, 0x25, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, - 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, 0x03, 0x52, 0x06, 0x6c, 0x65, - 0x6e, 0x67, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, - 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x42, 0x08, - 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, - 0x65, 0x73, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, - 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, - 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x1a, 0x84, 0x03, 0x0a, - 0x07, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, - 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2f, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, - 0x02, 0x03, 0xf2, 0x01, 0x00, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, - 0x00, 0x48, 0x01, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, - 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, - 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, 0x02, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, - 0x03, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x08, - 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x50, 0x72, 0x6f, 0x66, - 0x69, 0x6c, 0x65, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x08, 0x70, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0x8a, 0x02, - 0x00, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, - 0x52, 0x00, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x09, 0x0a, - 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, 0x65, 0x6e, - 0x67, 0x74, 0x68, 0x1a, 0x34, 0x0a, 0x07, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x20, - 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x08, - 0xc2, 0xff, 0x8e, 0x02, 0x03, 0x8a, 0x02, 0x00, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, - 0x5a, 0x00, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xe6, 0x01, 0x0a, 0x08, 0x46, - 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6a, 0x35, 0x2e, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, - 0x02, 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x35, - 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0f, - 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, - 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x47, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, - 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x42, 0x1a, 0xba, 0x48, 0x03, - 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x62, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x07, - 0xaa, 0x01, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x13, - 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, - 0x6f, 0x10, 0x03, 0x22, 0xb3, 0x01, 0x0a, 0x12, 0x46, 0x6f, 0x6f, 0x43, 0x68, 0x61, 0x72, 0x61, - 0x63, 0x74, 0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x30, 0x0a, 0x06, 0x77, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x18, 0xc2, 0xff, 0x8e, 0x02, - 0x03, 0xfa, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x32, 0x08, 0x52, 0x02, 0x08, 0x01, - 0x5a, 0x02, 0x08, 0x01, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x30, 0x0a, 0x06, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, 0x18, 0xc2, 0xff, - 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x32, 0x08, 0x52, 0x02, - 0x08, 0x01, 0x5a, 0x02, 0x08, 0x01, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x30, - 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x18, - 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x32, 0x08, - 0x52, 0x02, 0x08, 0x01, 0x5a, 0x02, 0x08, 0x01, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, - 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0x7f, 0x0a, 0x0a, 0x46, 0x6f, 0x6f, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x70, 0x6c, 0x61, 0x63, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x14, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, - 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x06, 0x32, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x05, 0x70, 0x6c, - 0x61, 0x63, 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x28, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, - 0x1a, 0x72, 0x18, 0x0a, 0x16, 0x52, 0x14, 0x08, 0x01, 0x12, 0x10, 0x74, 0x73, 0x76, 0x5f, 0x70, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x2a, 0x56, 0x0a, 0x09, 0x46, 0x6f, - 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x4f, 0x4f, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x4f, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, - 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x4f, - 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, - 0x10, 0x02, 0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, - 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, - 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, -} - -var ( - file_test_v1_foo_j5s_proto_rawDescOnce sync.Once - file_test_v1_foo_j5s_proto_rawDescData = file_test_v1_foo_j5s_proto_rawDesc -) - -func file_test_v1_foo_j5s_proto_rawDescGZIP() []byte { - file_test_v1_foo_j5s_proto_rawDescOnce.Do(func() { - file_test_v1_foo_j5s_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_v1_foo_j5s_proto_rawDescData) - }) - return file_test_v1_foo_j5s_proto_rawDescData -} - -var file_test_v1_foo_j5s_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_test_v1_foo_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 10) -var file_test_v1_foo_j5s_proto_goTypes = []interface{}{ - (FooStatus)(0), // 0: test.v1.FooStatus - (*FooKeys)(nil), // 1: test.v1.FooKeys - (*FooData)(nil), // 2: test.v1.FooData - (*FooState)(nil), // 3: test.v1.FooState - (*FooEventType)(nil), // 4: test.v1.FooEventType - (*FooEvent)(nil), // 5: test.v1.FooEvent - (*FooCharacteristics)(nil), // 6: test.v1.FooCharacteristics - (*FooProfile)(nil), // 7: test.v1.FooProfile - (*FooEventType_Created)(nil), // 8: test.v1.FooEventType.Created - (*FooEventType_Updated)(nil), // 9: test.v1.FooEventType.Updated - (*FooEventType_Deleted)(nil), // 10: test.v1.FooEventType.Deleted - (*psm_j5pb.StateMetadata)(nil), // 11: j5.state.v1.StateMetadata - (*psm_j5pb.EventMetadata)(nil), // 12: j5.state.v1.EventMetadata -} -var file_test_v1_foo_j5s_proto_depIdxs = []int32{ - 6, // 0: test.v1.FooData.characteristics:type_name -> test.v1.FooCharacteristics - 7, // 1: test.v1.FooData.profiles:type_name -> test.v1.FooProfile - 11, // 2: test.v1.FooState.metadata:type_name -> j5.state.v1.StateMetadata - 1, // 3: test.v1.FooState.keys:type_name -> test.v1.FooKeys - 2, // 4: test.v1.FooState.data:type_name -> test.v1.FooData - 0, // 5: test.v1.FooState.status:type_name -> test.v1.FooStatus - 8, // 6: test.v1.FooEventType.created:type_name -> test.v1.FooEventType.Created - 9, // 7: test.v1.FooEventType.updated:type_name -> test.v1.FooEventType.Updated - 10, // 8: test.v1.FooEventType.deleted:type_name -> test.v1.FooEventType.Deleted - 12, // 9: test.v1.FooEvent.metadata:type_name -> j5.state.v1.EventMetadata - 1, // 10: test.v1.FooEvent.keys:type_name -> test.v1.FooKeys - 4, // 11: test.v1.FooEvent.event:type_name -> test.v1.FooEventType - 7, // 12: test.v1.FooEventType.Created.profiles:type_name -> test.v1.FooProfile - 7, // 13: test.v1.FooEventType.Updated.profiles:type_name -> test.v1.FooProfile - 14, // [14:14] is the sub-list for method output_type - 14, // [14:14] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name -} - -func init() { file_test_v1_foo_j5s_proto_init() } -func file_test_v1_foo_j5s_proto_init() { - if File_test_v1_foo_j5s_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_test_v1_foo_j5s_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooKeys); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_foo_j5s_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooData); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_foo_j5s_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooState); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_foo_j5s_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooEventType); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_foo_j5s_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooEvent); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_foo_j5s_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooCharacteristics); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_foo_j5s_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooProfile); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_foo_j5s_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooEventType_Created); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_foo_j5s_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooEventType_Updated); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_foo_j5s_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooEventType_Deleted); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_test_v1_foo_j5s_proto_msgTypes[0].OneofWrappers = []interface{}{} - file_test_v1_foo_j5s_proto_msgTypes[1].OneofWrappers = []interface{}{} - file_test_v1_foo_j5s_proto_msgTypes[3].OneofWrappers = []interface{}{ - (*FooEventType_Created_)(nil), - (*FooEventType_Updated_)(nil), - (*FooEventType_Deleted_)(nil), - } - file_test_v1_foo_j5s_proto_msgTypes[7].OneofWrappers = []interface{}{} - file_test_v1_foo_j5s_proto_msgTypes[8].OneofWrappers = []interface{}{} - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_test_v1_foo_j5s_proto_rawDesc, - NumEnums: 1, - NumMessages: 10, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_test_v1_foo_j5s_proto_goTypes, - DependencyIndexes: file_test_v1_foo_j5s_proto_depIdxs, - EnumInfos: file_test_v1_foo_j5s_proto_enumTypes, - MessageInfos: file_test_v1_foo_j5s_proto_msgTypes, - }.Build() - File_test_v1_foo_j5s_proto = out.File - file_test_v1_foo_j5s_proto_rawDesc = nil - file_test_v1_foo_j5s_proto_goTypes = nil - file_test_v1_foo_j5s_proto_depIdxs = nil -} diff --git a/internal/testproto/gen/test/v1/test_pb/foo.j5s_j5.pb.go b/internal/testproto/gen/test/v1/test_pb/foo.j5s_j5.pb.go new file mode 100644 index 0000000..f221194 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_pb/foo.j5s_j5.pb.go @@ -0,0 +1,29 @@ +// Code generated by protoc-gen-go-j5. DO NOT EDIT. + +package test_pb + +import ( + j5reflect "github.com/pentops/j5/lib/j5reflect" +) + +func (msg *FooKeys) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *FooData) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *FooState) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *FooEventType) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *FooEvent) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *FooCharacteristics) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *FooProfile) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} diff --git a/internal/testproto/gen/test/v1/test_pb/foo.j5s_psm.pb.go b/internal/testproto/gen/test/v1/test_pb/foo.j5s_psm.pb.go deleted file mode 100644 index cf1c325..0000000 --- a/internal/testproto/gen/test/v1/test_pb/foo.j5s_psm.pb.go +++ /dev/null @@ -1,628 +0,0 @@ -// Code generated by protoc-gen-go-psm. DO NOT EDIT. - -package test_pb - -import ( - context "context" - fmt "fmt" - psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" - psm "github.com/pentops/protostate/psm" - sqrlx "github.com/pentops/sqrlx.go/sqrlx" -) - -// PSM FooPSM - -type FooPSM = psm.StateMachine[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] - -type FooPSMDB = psm.DBStateMachine[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] - -type FooPSMEventSpec = psm.EventSpec[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] - -type FooPSMHookBaton = psm.HookBaton[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] - -type FooPSMFullBaton = psm.CallbackBaton[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] - -type FooPSMEventKey = string - -const ( - FooPSMEventNil FooPSMEventKey = "" - FooPSMEventCreated FooPSMEventKey = "created" - FooPSMEventUpdated FooPSMEventKey = "updated" - FooPSMEventDeleted FooPSMEventKey = "deleted" -) - -// EXTEND FooKeys with the psm.IKeyset interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *FooKeys) PSMIsSet() bool { - return msg != nil -} - -// PSMFullName returns the full name of state machine with package prefix -func (msg *FooKeys) PSMFullName() string { - return "test.v1.foo" -} -func (msg *FooKeys) PSMKeyValues() (map[string]any, error) { - keyset := map[string]any{ - "foo_id": msg.FooId, - } - if msg.TenantId != nil { - keyset["tenant_id"] = *msg.TenantId - } - if msg.MetaTenantId != "" { - keyset["meta_tenant_id"] = msg.MetaTenantId - } - return keyset, nil -} - -// EXTEND FooState with the psm.IState interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *FooState) PSMIsSet() bool { - return msg != nil -} - -func (msg *FooState) PSMMetadata() *psm_j5pb.StateMetadata { - if msg.Metadata == nil { - msg.Metadata = &psm_j5pb.StateMetadata{} - } - return msg.Metadata -} - -func (msg *FooState) PSMKeys() *FooKeys { - return msg.Keys -} - -func (msg *FooState) SetStatus(status FooStatus) { - msg.Status = status -} - -func (msg *FooState) SetPSMKeys(inner *FooKeys) { - msg.Keys = inner -} - -func (msg *FooState) PSMData() *FooData { - if msg.Data == nil { - msg.Data = &FooData{} - } - return msg.Data -} - -// EXTEND FooData with the psm.IStateData interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *FooData) PSMIsSet() bool { - return msg != nil -} - -// EXTEND FooEvent with the psm.IEvent interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *FooEvent) PSMIsSet() bool { - return msg != nil -} - -func (msg *FooEvent) PSMMetadata() *psm_j5pb.EventMetadata { - if msg.Metadata == nil { - msg.Metadata = &psm_j5pb.EventMetadata{} - } - return msg.Metadata -} - -func (msg *FooEvent) PSMKeys() *FooKeys { - return msg.Keys -} - -func (msg *FooEvent) SetPSMKeys(inner *FooKeys) { - msg.Keys = inner -} - -// PSMEventKey returns the FooPSMEventPSMEventKey for the event, implementing psm.IEvent -func (msg *FooEvent) PSMEventKey() FooPSMEventKey { - tt := msg.UnwrapPSMEvent() - if tt == nil { - return FooPSMEventNil - } - return tt.PSMEventKey() -} - -// UnwrapPSMEvent implements psm.IEvent, returning the inner event message -func (msg *FooEvent) UnwrapPSMEvent() FooPSMEvent { - if msg == nil { - return nil - } - if msg.Event == nil { - return nil - } - switch v := msg.Event.Type.(type) { - case *FooEventType_Created_: - return v.Created - case *FooEventType_Updated_: - return v.Updated - case *FooEventType_Deleted_: - return v.Deleted - default: - return nil - } -} - -// SetPSMEvent sets the inner event message from a concrete type, implementing psm.IEvent -func (msg *FooEvent) SetPSMEvent(inner FooPSMEvent) error { - if msg.Event == nil { - msg.Event = &FooEventType{} - } - switch v := inner.(type) { - case *FooEventType_Created: - msg.Event.Type = &FooEventType_Created_{Created: v} - case *FooEventType_Updated: - msg.Event.Type = &FooEventType_Updated_{Updated: v} - case *FooEventType_Deleted: - msg.Event.Type = &FooEventType_Deleted_{Deleted: v} - default: - return fmt.Errorf("invalid type %T for FooEventType", v) - } - return nil -} - -type FooPSMEvent interface { - psm.IInnerEvent - PSMEventKey() FooPSMEventKey -} - -// EXTEND FooEventType_Created with the FooPSMEvent interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *FooEventType_Created) PSMIsSet() bool { - return msg != nil -} - -func (*FooEventType_Created) PSMEventKey() FooPSMEventKey { - return FooPSMEventCreated -} - -// EXTEND FooEventType_Updated with the FooPSMEvent interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *FooEventType_Updated) PSMIsSet() bool { - return msg != nil -} - -func (*FooEventType_Updated) PSMEventKey() FooPSMEventKey { - return FooPSMEventUpdated -} - -// EXTEND FooEventType_Deleted with the FooPSMEvent interface - -// PSMIsSet is a helper for != nil, which does not work with generic parameters -func (msg *FooEventType_Deleted) PSMIsSet() bool { - return msg != nil -} - -func (*FooEventType_Deleted) PSMEventKey() FooPSMEventKey { - return FooPSMEventDeleted -} - -func FooPSMBuilder() *psm.StateMachineConfig[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] { - return &psm.StateMachineConfig[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent - ]{} -} - -// FooPSMMutation runs at the start of a transition to merge the event information into the state data object. The state object is mutable in this context. -func FooPSMMutation[SE FooPSMEvent](cb func(*FooData, SE) error) psm.TransitionMutation[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent - SE, // Specific event type for the transition -] { - return psm.TransitionMutation[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent - SE, // Specific event type for the transition - ](cb) -} - -// FooPSMLogicHook runs after the mutation is complete. This hook can trigger side effects, including chained events, which are additional events processed by the state machine. Use this for Business Logic which determines the 'next step' in processing. -func FooPSMLogicHook[ - SE FooPSMEvent, -]( - cb func( - context.Context, - FooPSMHookBaton, - *FooState, - SE, - ) error) psm.TransitionHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] { - eventType := (*new(SE)).PSMEventKey() - return psm.TransitionHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func(ctx context.Context, tx sqrlx.Transaction, baton FooPSMFullBaton, state *FooState, event *FooEvent) error { - asType, ok := any(event.UnwrapPSMEvent()).(SE) - if !ok { - name := event.ProtoReflect().Descriptor().FullName() - return fmt.Errorf("unexpected event type in transition: %s [IE] does not match [SE] (%T)", name, new(SE)) - } - return cb(ctx, baton, state, asType) - }, - EventType: eventType, - RunOnFollow: false, - } -} - -// FooPSMDataHook runs after the mutations, and can be used to update data in tables which are not controlled as the state machine, e.g. for pre-calculating fields for performance reasons. Use of this hook prevents (future) transaction optimizations, as the transaction state when the function is called must needs to match the processing state, but only for this single transition, unlike the GeneralEventDataHook. -func FooPSMDataHook[ - SE FooPSMEvent, -]( - cb func( - context.Context, - sqrlx.Transaction, - *FooState, - SE, - ) error) psm.TransitionHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] { - eventType := (*new(SE)).PSMEventKey() - return psm.TransitionHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func(ctx context.Context, tx sqrlx.Transaction, baton FooPSMFullBaton, state *FooState, event *FooEvent) error { - asType, ok := any(event.UnwrapPSMEvent()).(SE) - if !ok { - name := event.ProtoReflect().Descriptor().FullName() - return fmt.Errorf("unexpected event type in transition: %s [IE] does not match [SE] (%T)", name, new(SE)) - } - return cb(ctx, tx, state, asType) - }, - EventType: eventType, - RunOnFollow: true, - } -} - -// FooPSMLinkHook runs after the mutation and logic hook, and can be used to link the state machine to other state machines in the same database transaction -func FooPSMLinkHook[ - SE FooPSMEvent, - DK psm.IKeyset, - DIE psm.IInnerEvent, -]( - linkDestination psm.LinkDestination[DK, DIE], - cb func( - context.Context, - *FooState, - SE, - func(DK, DIE), - ) error) psm.TransitionHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] { - eventType := (*new(SE)).PSMEventKey() - wrapped := func(ctx context.Context, tx sqrlx.Transaction, state *FooState, event SE, add func(DK, DIE)) error { - return cb(ctx, state, event, add) - } - return psm.TransitionHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func(ctx context.Context, tx sqrlx.Transaction, baton FooPSMFullBaton, state *FooState, event *FooEvent) error { - return psm.RunLinkHook(ctx, linkDestination, wrapped, tx, state, event) - }, - EventType: eventType, - RunOnFollow: false, - } -} - -// FooPSMLinkDBHook like LinkHook, but has access to the current transaction for reads only (not enforced), use in place of controller logic to look up existing state. -func FooPSMLinkDBHook[ - SE FooPSMEvent, - DK psm.IKeyset, - DIE psm.IInnerEvent, -]( - linkDestination psm.LinkDestination[DK, DIE], - cb func( - context.Context, - sqrlx.Transaction, - *FooState, - SE, - func(DK, DIE), - ) error) psm.TransitionHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] { - eventType := (*new(SE)).PSMEventKey() - return psm.TransitionHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func(ctx context.Context, tx sqrlx.Transaction, baton FooPSMFullBaton, state *FooState, event *FooEvent) error { - return psm.RunLinkHook(ctx, linkDestination, cb, tx, state, event) - }, - EventType: eventType, - RunOnFollow: false, - } -} - -// FooPSMGeneralLogicHook runs once per transition at the state-machine level regardless of which transition / event is being processed. It runs exactly once per transition, with the state object in the final state after the transition but prior to processing any further events. Chained events are added to the *end* of the event queue for the transaction, and side effects are published (as always) when the transaction is committed. The function MUST be pure, i.e. It MUST NOT produce any side-effects outside of the HookBaton, and MUST NOT modify the state. -func FooPSMGeneralLogicHook( - cb func( - context.Context, - FooPSMHookBaton, - *FooState, - *FooEvent, - ) error) psm.GeneralEventHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] { - return psm.GeneralEventHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func( - ctx context.Context, - tx sqrlx.Transaction, - baton FooPSMFullBaton, - state *FooState, - event *FooEvent, - ) error { - return cb(ctx, baton, state, event) - }, - RunOnFollow: false, - } -} - -// FooPSMGeneralStateDataHook runs at the state-machine level regardless of which transition / event is being processed. It runs at-least once before committing a database transaction after multiple transitions are complete. This hook has access only to the final state after the transitions and is used to update other tables based on the resulting state. It MUST be idempotent, it may be called after injecting externally-held state data. -func FooPSMGeneralStateDataHook( - cb func( - context.Context, - sqrlx.Transaction, - *FooState, - ) error) psm.GeneralStateHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] { - return psm.GeneralStateHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func( - ctx context.Context, - tx sqrlx.Transaction, - baton FooPSMFullBaton, - state *FooState, - ) error { - return cb(ctx, tx, state) - }, - RunOnFollow: true, - } -} - -// FooPSMGeneralEventDataHook runs after each transition at the state-machine level regardless of which transition / event is being processed. It runs exactly once per transition, before any other events are processed. The presence of this hook type prevents (future) transaction optimizations, so should be used sparingly. -func FooPSMGeneralEventDataHook( - cb func( - context.Context, - sqrlx.Transaction, - *FooState, - *FooEvent, - ) error) psm.GeneralEventHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] { - return psm.GeneralEventHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func( - ctx context.Context, - tx sqrlx.Transaction, - baton FooPSMFullBaton, - state *FooState, - event *FooEvent, - ) error { - return cb(ctx, tx, state, event) - }, - RunOnFollow: true, - } -} - -// FooPSMEventPublishHook EventPublishHook runs for each transition, at least once before committing a database transaction after multiple transitions are complete. It should publish a derived version of the event using the publisher. -func FooPSMEventPublishHook( - cb func( - context.Context, - psm.Publisher, - *FooState, - *FooEvent, - ) error) psm.GeneralEventHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] { - return psm.GeneralEventHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func( - ctx context.Context, - tx sqrlx.Transaction, - baton FooPSMFullBaton, - state *FooState, - event *FooEvent, - ) error { - return cb(ctx, baton, state, event) - }, - RunOnFollow: false, - } -} - -// FooPSMUpsertPublishHook runs for each transition, at least once before committing a database transaction after multiple transitions are complete. It should publish a derived version of the event using the publisher. -func FooPSMUpsertPublishHook( - cb func( - context.Context, - psm.Publisher, - *FooState, - ) error) psm.GeneralStateHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent -] { - return psm.GeneralStateHook[ - *FooKeys, // implements psm.IKeyset - *FooState, // implements psm.IState - FooStatus, // implements psm.IStatusEnum - *FooData, // implements psm.IStateData - *FooEvent, // implements psm.IEvent - FooPSMEvent, // implements psm.IInnerEvent - ]{ - Callback: func( - ctx context.Context, - tx sqrlx.Transaction, - baton FooPSMFullBaton, - state *FooState, - ) error { - return cb(ctx, baton, state) - }, - RunOnFollow: false, - } -} - -func (event *FooEvent) EventPublishMetadata() *psm_j5pb.EventPublishMetadata { - tenantKeys := make([]*psm_j5pb.EventTenant, 0) - if event.Keys.TenantId != nil { - tenantKeys = append(tenantKeys, &psm_j5pb.EventTenant{ - TenantType: "tenant", - TenantId: *event.Keys.TenantId, - }) - } - return &psm_j5pb.EventPublishMetadata{ - EventId: event.Metadata.EventId, - Sequence: event.Metadata.Sequence, - Timestamp: event.Metadata.Timestamp, - Cause: event.Metadata.Cause, - Auth: &psm_j5pb.PublishAuth{ - TenantKeys: tenantKeys, - }, - } -} diff --git a/internal/testproto/gen/test/v1/test_pb/foo.j5s_sugar.pb.go b/internal/testproto/gen/test/v1/test_pb/foo.j5s_sugar.pb.go deleted file mode 100644 index 59173e5..0000000 --- a/internal/testproto/gen/test/v1/test_pb/foo.j5s_sugar.pb.go +++ /dev/null @@ -1,120 +0,0 @@ -// Code generated by protoc-gen-go-sugar. DO NOT EDIT. - -package test_pb - -import ( - driver "database/sql/driver" - fmt "fmt" - proto "google.golang.org/protobuf/proto" -) - -// FooEventType is a oneof wrapper -type FooEventTypeKey string - -const ( - FooEvent_Created FooEventTypeKey = "created" - FooEvent_Updated FooEventTypeKey = "updated" - FooEvent_Deleted FooEventTypeKey = "deleted" -) - -func (x *FooEventType) TypeKey() (FooEventTypeKey, bool) { - switch x.Type.(type) { - case *FooEventType_Created_: - return FooEvent_Created, true - case *FooEventType_Updated_: - return FooEvent_Updated, true - case *FooEventType_Deleted_: - return FooEvent_Deleted, true - default: - return "", false - } -} - -type IsFooEventTypeWrappedType interface { - TypeKey() FooEventTypeKey - proto.Message -} - -func (x *FooEventType) Set(val IsFooEventTypeWrappedType) { - switch v := val.(type) { - case *FooEventType_Created: - x.Type = &FooEventType_Created_{Created: v} - case *FooEventType_Updated: - x.Type = &FooEventType_Updated_{Updated: v} - case *FooEventType_Deleted: - x.Type = &FooEventType_Deleted_{Deleted: v} - } -} -func (x *FooEventType) Get() IsFooEventTypeWrappedType { - switch v := x.Type.(type) { - case *FooEventType_Created_: - return v.Created - case *FooEventType_Updated_: - return v.Updated - case *FooEventType_Deleted_: - return v.Deleted - default: - return nil - } -} -func (x *FooEventType_Created) TypeKey() FooEventTypeKey { - return FooEvent_Created -} -func (x *FooEventType_Updated) TypeKey() FooEventTypeKey { - return FooEvent_Updated -} -func (x *FooEventType_Deleted) TypeKey() FooEventTypeKey { - return FooEvent_Deleted -} - -type IsFooEventType_Type = isFooEventType_Type - -// FooStatus -const ( - FooStatus_UNSPECIFIED FooStatus = 0 - FooStatus_ACTIVE FooStatus = 1 - FooStatus_DELETED FooStatus = 2 -) - -var ( - FooStatus_name_short = map[int32]string{ - 0: "UNSPECIFIED", - 1: "ACTIVE", - 2: "DELETED", - } - FooStatus_value_short = map[string]int32{ - "UNSPECIFIED": 0, - "ACTIVE": 1, - "DELETED": 2, - } - FooStatus_value_either = map[string]int32{ - "UNSPECIFIED": 0, - "FOO_STATUS_UNSPECIFIED": 0, - "ACTIVE": 1, - "FOO_STATUS_ACTIVE": 1, - "DELETED": 2, - "FOO_STATUS_DELETED": 2, - } -) - -// ShortString returns the un-prefixed string representation of the enum value -func (x FooStatus) ShortString() string { - return FooStatus_name_short[int32(x)] -} -func (x FooStatus) Value() (driver.Value, error) { - return []uint8(x.ShortString()), nil -} -func (x *FooStatus) Scan(value interface{}) error { - var strVal string - switch vt := value.(type) { - case []uint8: - strVal = string(vt) - case string: - strVal = vt - default: - return fmt.Errorf("invalid type %T", value) - } - val := FooStatus_value_either[strVal] - *x = FooStatus(val) - return nil -} diff --git a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s.pb.go b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s.pb.go deleted file mode 100644 index 4db2aa6..0000000 --- a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s.pb.go +++ /dev/null @@ -1,649 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.32.0 -// protoc (unknown) -// source: test/v1/service/bar.p.j5s.proto - -package test_spb - -import ( - _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" - _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" - list_j5pb "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - date_j5t "github.com/pentops/j5/j5types/date_j5t" - test_pb "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type BarGetRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - BarId string `protobuf:"bytes,1,opt,name=bar_id,json=barId,proto3" json:"bar_id,omitempty"` - BarOtherId string `protobuf:"bytes,2,opt,name=bar_other_id,json=barOtherId,proto3" json:"bar_other_id,omitempty"` - DateKey *date_j5t.Date `protobuf:"bytes,3,opt,name=date_key,json=dateKey,proto3" json:"date_key,omitempty"` -} - -func (x *BarGetRequest) Reset() { - *x = BarGetRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarGetRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarGetRequest) ProtoMessage() {} - -func (x *BarGetRequest) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarGetRequest.ProtoReflect.Descriptor instead. -func (*BarGetRequest) Descriptor() ([]byte, []int) { - return file_test_v1_service_bar_p_j5s_proto_rawDescGZIP(), []int{0} -} - -func (x *BarGetRequest) GetBarId() string { - if x != nil { - return x.BarId - } - return "" -} - -func (x *BarGetRequest) GetBarOtherId() string { - if x != nil { - return x.BarOtherId - } - return "" -} - -func (x *BarGetRequest) GetDateKey() *date_j5t.Date { - if x != nil { - return x.DateKey - } - return nil -} - -type BarGetResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Bar *test_pb.BarState `protobuf:"bytes,1,opt,name=bar,proto3" json:"bar,omitempty"` -} - -func (x *BarGetResponse) Reset() { - *x = BarGetResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarGetResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarGetResponse) ProtoMessage() {} - -func (x *BarGetResponse) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarGetResponse.ProtoReflect.Descriptor instead. -func (*BarGetResponse) Descriptor() ([]byte, []int) { - return file_test_v1_service_bar_p_j5s_proto_rawDescGZIP(), []int{1} -} - -func (x *BarGetResponse) GetBar() *test_pb.BarState { - if x != nil { - return x.Bar - } - return nil -} - -type BarListRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Page *list_j5pb.PageRequest `protobuf:"bytes,1,opt,name=page,proto3" json:"page,omitempty"` - Query *list_j5pb.QueryRequest `protobuf:"bytes,2,opt,name=query,proto3" json:"query,omitempty"` -} - -func (x *BarListRequest) Reset() { - *x = BarListRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarListRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarListRequest) ProtoMessage() {} - -func (x *BarListRequest) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarListRequest.ProtoReflect.Descriptor instead. -func (*BarListRequest) Descriptor() ([]byte, []int) { - return file_test_v1_service_bar_p_j5s_proto_rawDescGZIP(), []int{2} -} - -func (x *BarListRequest) GetPage() *list_j5pb.PageRequest { - if x != nil { - return x.Page - } - return nil -} - -func (x *BarListRequest) GetQuery() *list_j5pb.QueryRequest { - if x != nil { - return x.Query - } - return nil -} - -type BarListResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Bar []*test_pb.BarState `protobuf:"bytes,1,rep,name=bar,proto3" json:"bar,omitempty"` - Page *list_j5pb.PageResponse `protobuf:"bytes,2,opt,name=page,proto3" json:"page,omitempty"` -} - -func (x *BarListResponse) Reset() { - *x = BarListResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarListResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarListResponse) ProtoMessage() {} - -func (x *BarListResponse) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarListResponse.ProtoReflect.Descriptor instead. -func (*BarListResponse) Descriptor() ([]byte, []int) { - return file_test_v1_service_bar_p_j5s_proto_rawDescGZIP(), []int{3} -} - -func (x *BarListResponse) GetBar() []*test_pb.BarState { - if x != nil { - return x.Bar - } - return nil -} - -func (x *BarListResponse) GetPage() *list_j5pb.PageResponse { - if x != nil { - return x.Page - } - return nil -} - -type BarEventsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - BarId string `protobuf:"bytes,1,opt,name=bar_id,json=barId,proto3" json:"bar_id,omitempty"` - BarOtherId string `protobuf:"bytes,2,opt,name=bar_other_id,json=barOtherId,proto3" json:"bar_other_id,omitempty"` - DateKey *date_j5t.Date `protobuf:"bytes,3,opt,name=date_key,json=dateKey,proto3" json:"date_key,omitempty"` - Page *list_j5pb.PageRequest `protobuf:"bytes,4,opt,name=page,proto3" json:"page,omitempty"` - Query *list_j5pb.QueryRequest `protobuf:"bytes,5,opt,name=query,proto3" json:"query,omitempty"` -} - -func (x *BarEventsRequest) Reset() { - *x = BarEventsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarEventsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarEventsRequest) ProtoMessage() {} - -func (x *BarEventsRequest) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarEventsRequest.ProtoReflect.Descriptor instead. -func (*BarEventsRequest) Descriptor() ([]byte, []int) { - return file_test_v1_service_bar_p_j5s_proto_rawDescGZIP(), []int{4} -} - -func (x *BarEventsRequest) GetBarId() string { - if x != nil { - return x.BarId - } - return "" -} - -func (x *BarEventsRequest) GetBarOtherId() string { - if x != nil { - return x.BarOtherId - } - return "" -} - -func (x *BarEventsRequest) GetDateKey() *date_j5t.Date { - if x != nil { - return x.DateKey - } - return nil -} - -func (x *BarEventsRequest) GetPage() *list_j5pb.PageRequest { - if x != nil { - return x.Page - } - return nil -} - -func (x *BarEventsRequest) GetQuery() *list_j5pb.QueryRequest { - if x != nil { - return x.Query - } - return nil -} - -type BarEventsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Events []*test_pb.BarEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` - Page *list_j5pb.PageResponse `protobuf:"bytes,2,opt,name=page,proto3" json:"page,omitempty"` -} - -func (x *BarEventsResponse) Reset() { - *x = BarEventsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarEventsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarEventsResponse) ProtoMessage() {} - -func (x *BarEventsResponse) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarEventsResponse.ProtoReflect.Descriptor instead. -func (*BarEventsResponse) Descriptor() ([]byte, []int) { - return file_test_v1_service_bar_p_j5s_proto_rawDescGZIP(), []int{5} -} - -func (x *BarEventsResponse) GetEvents() []*test_pb.BarEvent { - if x != nil { - return x.Events - } - return nil -} - -func (x *BarEventsResponse) GetPage() *list_j5pb.PageResponse { - if x != nil { - return x.Page - } - return nil -} - -var File_test_v1_service_bar_p_j5s_proto protoreflect.FileDescriptor - -var file_test_v1_service_bar_p_j5s_proto_rawDesc = []byte{ - 0x0a, 0x1f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2f, 0x62, 0x61, 0x72, 0x2e, 0x70, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x0f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, - 0x35, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x6a, 0x35, 0x2f, 0x6c, - 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x1a, 0x16, 0x6a, 0x35, 0x2f, 0x6c, 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x71, 0x75, - 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, 0x35, 0x2f, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, - 0x62, 0x61, 0x72, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcf, 0x01, - 0x0a, 0x0d, 0x42, 0x61, 0x72, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x33, 0x0a, 0x06, 0x62, 0x61, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x1c, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, - 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x05, 0x62, - 0x61, 0x72, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0c, 0x62, 0x61, 0x72, 0x5f, 0x6f, 0x74, 0x68, 0x65, - 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1c, 0xba, 0x48, 0x08, 0xc8, - 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, - 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x0a, 0x62, 0x61, 0x72, 0x4f, 0x74, 0x68, - 0x65, 0x72, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6a, 0x35, 0x2e, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2e, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x65, 0x42, 0x0d, - 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x07, 0x64, - 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, - 0x4d, 0x0a, 0x0e, 0x42, 0x61, 0x72, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x32, 0x0a, 0x03, 0x62, 0x61, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, - 0x52, 0x03, 0x62, 0x61, 0x72, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0x88, - 0x01, 0x0a, 0x0e, 0x42, 0x61, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x17, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, - 0x00, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, - 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, - 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0x80, 0x01, 0x0a, 0x0f, 0x42, 0x61, - 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, - 0x03, 0x62, 0x61, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x65, 0x73, - 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, 0x08, 0xc2, - 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x03, 0x62, 0x61, 0x72, 0x12, 0x35, 0x0a, 0x04, - 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6a, 0x35, 0x2e, - 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x70, - 0x61, 0x67, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0xc1, 0x02, 0x0a, - 0x10, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x33, 0x0a, 0x06, 0x62, 0x61, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x1c, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, - 0x8e, 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, - 0x05, 0x62, 0x61, 0x72, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0c, 0x62, 0x61, 0x72, 0x5f, 0x6f, 0x74, - 0x68, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1c, 0xba, 0x48, - 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, 0x02, - 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x0a, 0x62, 0x61, 0x72, 0x4f, - 0x74, 0x68, 0x65, 0x72, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6b, - 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6a, 0x35, 0x2e, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x2e, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x65, - 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, - 0x07, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, - 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, - 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x37, - 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, - 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, - 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, - 0x22, 0x88, 0x01, 0x0a, 0x11, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, - 0x2e, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, - 0xaa, 0x01, 0x00, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x35, 0x0a, 0x04, 0x70, - 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, - 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x70, 0x61, - 0x67, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x32, 0xbe, 0x03, 0x0a, 0x0f, - 0x42, 0x61, 0x72, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x8d, 0x01, 0x0a, 0x06, 0x42, 0x61, 0x72, 0x47, 0x65, 0x74, 0x12, 0x1e, 0x2e, 0x74, 0x65, 0x73, - 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, 0x61, 0x72, - 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x74, 0x65, 0x73, - 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, 0x61, 0x72, - 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0xc2, 0xff, 0x8e, - 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x33, 0x12, 0x31, 0x2f, 0x74, - 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x61, 0x72, 0x2f, 0x71, 0x2f, 0x7b, 0x62, 0x61, - 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x7b, 0x62, 0x61, 0x72, 0x5f, 0x6f, 0x74, 0x68, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x7d, 0x12, - 0x6d, 0x0a, 0x07, 0x42, 0x61, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1f, 0x2e, 0x74, 0x65, 0x73, - 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, 0x61, 0x72, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x74, 0x65, - 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, 0x61, - 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0xc2, - 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x10, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, - 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x61, 0x72, 0x2f, 0x71, 0x12, 0x9d, - 0x01, 0x0a, 0x09, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, - 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x22, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2e, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x49, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x18, 0x01, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x3a, 0x12, 0x38, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x62, - 0x61, 0x72, 0x2f, 0x71, 0x2f, 0x7b, 0x62, 0x61, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x7b, 0x62, - 0x61, 0x72, 0x5f, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x7b, 0x64, 0x61, - 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x7d, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x0c, - 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x05, 0x0a, 0x03, 0x62, 0x61, 0x72, 0x42, 0x47, 0x5a, 0x45, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, - 0x70, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, - 0x74, 0x5f, 0x73, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_test_v1_service_bar_p_j5s_proto_rawDescOnce sync.Once - file_test_v1_service_bar_p_j5s_proto_rawDescData = file_test_v1_service_bar_p_j5s_proto_rawDesc -) - -func file_test_v1_service_bar_p_j5s_proto_rawDescGZIP() []byte { - file_test_v1_service_bar_p_j5s_proto_rawDescOnce.Do(func() { - file_test_v1_service_bar_p_j5s_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_v1_service_bar_p_j5s_proto_rawDescData) - }) - return file_test_v1_service_bar_p_j5s_proto_rawDescData -} - -var file_test_v1_service_bar_p_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 6) -var file_test_v1_service_bar_p_j5s_proto_goTypes = []interface{}{ - (*BarGetRequest)(nil), // 0: test.v1.service.BarGetRequest - (*BarGetResponse)(nil), // 1: test.v1.service.BarGetResponse - (*BarListRequest)(nil), // 2: test.v1.service.BarListRequest - (*BarListResponse)(nil), // 3: test.v1.service.BarListResponse - (*BarEventsRequest)(nil), // 4: test.v1.service.BarEventsRequest - (*BarEventsResponse)(nil), // 5: test.v1.service.BarEventsResponse - (*date_j5t.Date)(nil), // 6: j5.types.date.v1.Date - (*test_pb.BarState)(nil), // 7: test.v1.BarState - (*list_j5pb.PageRequest)(nil), // 8: j5.list.v1.PageRequest - (*list_j5pb.QueryRequest)(nil), // 9: j5.list.v1.QueryRequest - (*list_j5pb.PageResponse)(nil), // 10: j5.list.v1.PageResponse - (*test_pb.BarEvent)(nil), // 11: test.v1.BarEvent -} -var file_test_v1_service_bar_p_j5s_proto_depIdxs = []int32{ - 6, // 0: test.v1.service.BarGetRequest.date_key:type_name -> j5.types.date.v1.Date - 7, // 1: test.v1.service.BarGetResponse.bar:type_name -> test.v1.BarState - 8, // 2: test.v1.service.BarListRequest.page:type_name -> j5.list.v1.PageRequest - 9, // 3: test.v1.service.BarListRequest.query:type_name -> j5.list.v1.QueryRequest - 7, // 4: test.v1.service.BarListResponse.bar:type_name -> test.v1.BarState - 10, // 5: test.v1.service.BarListResponse.page:type_name -> j5.list.v1.PageResponse - 6, // 6: test.v1.service.BarEventsRequest.date_key:type_name -> j5.types.date.v1.Date - 8, // 7: test.v1.service.BarEventsRequest.page:type_name -> j5.list.v1.PageRequest - 9, // 8: test.v1.service.BarEventsRequest.query:type_name -> j5.list.v1.QueryRequest - 11, // 9: test.v1.service.BarEventsResponse.events:type_name -> test.v1.BarEvent - 10, // 10: test.v1.service.BarEventsResponse.page:type_name -> j5.list.v1.PageResponse - 0, // 11: test.v1.service.BarQueryService.BarGet:input_type -> test.v1.service.BarGetRequest - 2, // 12: test.v1.service.BarQueryService.BarList:input_type -> test.v1.service.BarListRequest - 4, // 13: test.v1.service.BarQueryService.BarEvents:input_type -> test.v1.service.BarEventsRequest - 1, // 14: test.v1.service.BarQueryService.BarGet:output_type -> test.v1.service.BarGetResponse - 3, // 15: test.v1.service.BarQueryService.BarList:output_type -> test.v1.service.BarListResponse - 5, // 16: test.v1.service.BarQueryService.BarEvents:output_type -> test.v1.service.BarEventsResponse - 14, // [14:17] is the sub-list for method output_type - 11, // [11:14] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name -} - -func init() { file_test_v1_service_bar_p_j5s_proto_init() } -func file_test_v1_service_bar_p_j5s_proto_init() { - if File_test_v1_service_bar_p_j5s_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_test_v1_service_bar_p_j5s_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarGetRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_service_bar_p_j5s_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarGetResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_service_bar_p_j5s_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarListRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_service_bar_p_j5s_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarListResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_service_bar_p_j5s_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarEventsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_service_bar_p_j5s_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarEventsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_test_v1_service_bar_p_j5s_proto_rawDesc, - NumEnums: 0, - NumMessages: 6, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_test_v1_service_bar_p_j5s_proto_goTypes, - DependencyIndexes: file_test_v1_service_bar_p_j5s_proto_depIdxs, - MessageInfos: file_test_v1_service_bar_p_j5s_proto_msgTypes, - }.Build() - File_test_v1_service_bar_p_j5s_proto = out.File - file_test_v1_service_bar_p_j5s_proto_rawDesc = nil - file_test_v1_service_bar_p_j5s_proto_goTypes = nil - file_test_v1_service_bar_p_j5s_proto_depIdxs = nil -} diff --git a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_grpc.pb.go b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_grpc.pb.go deleted file mode 100644 index c21a6a7..0000000 --- a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_grpc.pb.go +++ /dev/null @@ -1,186 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.4.0 -// - protoc (unknown) -// source: test/v1/service/bar.p.j5s.proto - -package test_spb - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 - -const ( - BarQueryService_BarGet_FullMethodName = "/test.v1.service.BarQueryService/BarGet" - BarQueryService_BarList_FullMethodName = "/test.v1.service.BarQueryService/BarList" - BarQueryService_BarEvents_FullMethodName = "/test.v1.service.BarQueryService/BarEvents" -) - -// BarQueryServiceClient is the client API for BarQueryService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type BarQueryServiceClient interface { - BarGet(ctx context.Context, in *BarGetRequest, opts ...grpc.CallOption) (*BarGetResponse, error) - BarList(ctx context.Context, in *BarListRequest, opts ...grpc.CallOption) (*BarListResponse, error) - BarEvents(ctx context.Context, in *BarEventsRequest, opts ...grpc.CallOption) (*BarEventsResponse, error) -} - -type barQueryServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewBarQueryServiceClient(cc grpc.ClientConnInterface) BarQueryServiceClient { - return &barQueryServiceClient{cc} -} - -func (c *barQueryServiceClient) BarGet(ctx context.Context, in *BarGetRequest, opts ...grpc.CallOption) (*BarGetResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(BarGetResponse) - err := c.cc.Invoke(ctx, BarQueryService_BarGet_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *barQueryServiceClient) BarList(ctx context.Context, in *BarListRequest, opts ...grpc.CallOption) (*BarListResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(BarListResponse) - err := c.cc.Invoke(ctx, BarQueryService_BarList_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *barQueryServiceClient) BarEvents(ctx context.Context, in *BarEventsRequest, opts ...grpc.CallOption) (*BarEventsResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(BarEventsResponse) - err := c.cc.Invoke(ctx, BarQueryService_BarEvents_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -// BarQueryServiceServer is the server API for BarQueryService service. -// All implementations must embed UnimplementedBarQueryServiceServer -// for forward compatibility -type BarQueryServiceServer interface { - BarGet(context.Context, *BarGetRequest) (*BarGetResponse, error) - BarList(context.Context, *BarListRequest) (*BarListResponse, error) - BarEvents(context.Context, *BarEventsRequest) (*BarEventsResponse, error) - mustEmbedUnimplementedBarQueryServiceServer() -} - -// UnimplementedBarQueryServiceServer must be embedded to have forward compatible implementations. -type UnimplementedBarQueryServiceServer struct { -} - -func (UnimplementedBarQueryServiceServer) BarGet(context.Context, *BarGetRequest) (*BarGetResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method BarGet not implemented") -} -func (UnimplementedBarQueryServiceServer) BarList(context.Context, *BarListRequest) (*BarListResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method BarList not implemented") -} -func (UnimplementedBarQueryServiceServer) BarEvents(context.Context, *BarEventsRequest) (*BarEventsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method BarEvents not implemented") -} -func (UnimplementedBarQueryServiceServer) mustEmbedUnimplementedBarQueryServiceServer() {} - -// UnsafeBarQueryServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to BarQueryServiceServer will -// result in compilation errors. -type UnsafeBarQueryServiceServer interface { - mustEmbedUnimplementedBarQueryServiceServer() -} - -func RegisterBarQueryServiceServer(s grpc.ServiceRegistrar, srv BarQueryServiceServer) { - s.RegisterService(&BarQueryService_ServiceDesc, srv) -} - -func _BarQueryService_BarGet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(BarGetRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(BarQueryServiceServer).BarGet(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: BarQueryService_BarGet_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BarQueryServiceServer).BarGet(ctx, req.(*BarGetRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _BarQueryService_BarList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(BarListRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(BarQueryServiceServer).BarList(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: BarQueryService_BarList_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BarQueryServiceServer).BarList(ctx, req.(*BarListRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _BarQueryService_BarEvents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(BarEventsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(BarQueryServiceServer).BarEvents(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: BarQueryService_BarEvents_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BarQueryServiceServer).BarEvents(ctx, req.(*BarEventsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// BarQueryService_ServiceDesc is the grpc.ServiceDesc for BarQueryService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var BarQueryService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "test.v1.service.BarQueryService", - HandlerType: (*BarQueryServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "BarGet", - Handler: _BarQueryService_BarGet_Handler, - }, - { - MethodName: "BarList", - Handler: _BarQueryService_BarList_Handler, - }, - { - MethodName: "BarEvents", - Handler: _BarQueryService_BarEvents_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "test/v1/service/bar.p.j5s.proto", -} diff --git a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_j5.pb.go b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_j5.pb.go new file mode 100644 index 0000000..f9e6ba3 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_j5.pb.go @@ -0,0 +1,26 @@ +// Code generated by protoc-gen-go-j5. DO NOT EDIT. + +package test_spb + +import ( + j5reflect "github.com/pentops/j5/lib/j5reflect" +) + +func (msg *BarGetRequest) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *BarGetResponse) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *BarListRequest) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *BarListResponse) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *BarEventsRequest) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *BarEventsResponse) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} diff --git a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_psm_query.pb.go b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_psm_query.pb.go deleted file mode 100644 index d0b3d8e..0000000 --- a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_psm_query.pb.go +++ /dev/null @@ -1,117 +0,0 @@ -// Code generated by protoc-gen-go-psm. DO NOT EDIT. - -package test_spb - -import ( - context "context" - psm "github.com/pentops/protostate/psm" - sqrlx "github.com/pentops/sqrlx.go/sqrlx" -) - -// State Query Service for %sBar -// QuerySet is the query set for the Bar service. - -type BarPSMQuerySet = psm.StateQuerySet[ - *BarGetRequest, - *BarGetResponse, - *BarListRequest, - *BarListResponse, - *BarEventsRequest, - *BarEventsResponse, -] - -func NewBarPSMQuerySet( - smSpec psm.QuerySpec[ - *BarGetRequest, - *BarGetResponse, - *BarListRequest, - *BarListResponse, - *BarEventsRequest, - *BarEventsResponse, - ], - options psm.StateQueryOptions, -) (*BarPSMQuerySet, error) { - return psm.BuildStateQuerySet[ - *BarGetRequest, - *BarGetResponse, - *BarListRequest, - *BarListResponse, - *BarEventsRequest, - *BarEventsResponse, - ](smSpec, options) -} - -type BarPSMQuerySpec = psm.QuerySpec[ - *BarGetRequest, - *BarGetResponse, - *BarListRequest, - *BarListResponse, - *BarEventsRequest, - *BarEventsResponse, -] - -func DefaultBarPSMQuerySpec(tableSpec psm.QueryTableSpec) BarPSMQuerySpec { - return psm.QuerySpec[ - *BarGetRequest, - *BarGetResponse, - *BarListRequest, - *BarListResponse, - *BarEventsRequest, - *BarEventsResponse, - ]{ - QueryTableSpec: tableSpec, - ListRequestFilter: func(req *BarListRequest) (map[string]interface{}, error) { - filter := map[string]interface{}{} - return filter, nil - }, - ListEventsRequestFilter: func(req *BarEventsRequest) (map[string]interface{}, error) { - filter := map[string]interface{}{} - filter["bar_id"] = req.BarId - filter["bar_other_id"] = req.BarOtherId - filter["date_key"] = req.DateKey - return filter, nil - }, - } -} - -type BarQueryServiceImpl struct { - db sqrlx.Transactor - querySet *BarPSMQuerySet - UnsafeBarQueryServiceServer -} - -var _ BarQueryServiceServer = &BarQueryServiceImpl{} - -func NewBarQueryServiceImpl(db sqrlx.Transactor, querySet *BarPSMQuerySet) *BarQueryServiceImpl { - return &BarQueryServiceImpl{ - db: db, - querySet: querySet, - } -} - -func (s *BarQueryServiceImpl) BarGet(ctx context.Context, req *BarGetRequest) (*BarGetResponse, error) { - resObject := &BarGetResponse{} - err := s.querySet.Get(ctx, s.db, req, resObject) - if err != nil { - return nil, err - } - return resObject, nil -} - -func (s *BarQueryServiceImpl) BarList(ctx context.Context, req *BarListRequest) (*BarListResponse, error) { - resObject := &BarListResponse{} - err := s.querySet.List(ctx, s.db, req, resObject) - if err != nil { - return nil, err - } - return resObject, nil -} - -func (s *BarQueryServiceImpl) BarEvents(ctx context.Context, req *BarEventsRequest) (*BarEventsResponse, error) { - resObject := &BarEventsResponse{} - err := s.querySet.ListEvents(ctx, s.db, req, resObject) - if err != nil { - return nil, err - } - return resObject, nil -} diff --git a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_sugar.pb.go b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_sugar.pb.go deleted file mode 100644 index 2bfa669..0000000 --- a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_sugar.pb.go +++ /dev/null @@ -1,3 +0,0 @@ -// Code generated by protoc-gen-go-sugar. DO NOT EDIT. - -package test_spb diff --git a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s.pb.go b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s.pb.go deleted file mode 100644 index 9737caa..0000000 --- a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s.pb.go +++ /dev/null @@ -1,768 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.32.0 -// protoc (unknown) -// source: test/v1/service/foo.p.j5s.proto - -package test_spb - -import ( - _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" - _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" - list_j5pb "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - test_pb "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type FooGetRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - FooId string `protobuf:"bytes,1,opt,name=foo_id,json=fooId,proto3" json:"foo_id,omitempty"` -} - -func (x *FooGetRequest) Reset() { - *x = FooGetRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooGetRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooGetRequest) ProtoMessage() {} - -func (x *FooGetRequest) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooGetRequest.ProtoReflect.Descriptor instead. -func (*FooGetRequest) Descriptor() ([]byte, []int) { - return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{0} -} - -func (x *FooGetRequest) GetFooId() string { - if x != nil { - return x.FooId - } - return "" -} - -type FooGetResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Foo *test_pb.FooState `protobuf:"bytes,1,opt,name=foo,proto3" json:"foo,omitempty"` - Events []*test_pb.FooEvent `protobuf:"bytes,2,rep,name=events,proto3" json:"events,omitempty"` -} - -func (x *FooGetResponse) Reset() { - *x = FooGetResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooGetResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooGetResponse) ProtoMessage() {} - -func (x *FooGetResponse) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[1] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooGetResponse.ProtoReflect.Descriptor instead. -func (*FooGetResponse) Descriptor() ([]byte, []int) { - return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{1} -} - -func (x *FooGetResponse) GetFoo() *test_pb.FooState { - if x != nil { - return x.Foo - } - return nil -} - -func (x *FooGetResponse) GetEvents() []*test_pb.FooEvent { - if x != nil { - return x.Events - } - return nil -} - -type FooListRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Page *list_j5pb.PageRequest `protobuf:"bytes,1,opt,name=page,proto3" json:"page,omitempty"` - Query *list_j5pb.QueryRequest `protobuf:"bytes,2,opt,name=query,proto3" json:"query,omitempty"` -} - -func (x *FooListRequest) Reset() { - *x = FooListRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooListRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooListRequest) ProtoMessage() {} - -func (x *FooListRequest) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[2] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooListRequest.ProtoReflect.Descriptor instead. -func (*FooListRequest) Descriptor() ([]byte, []int) { - return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{2} -} - -func (x *FooListRequest) GetPage() *list_j5pb.PageRequest { - if x != nil { - return x.Page - } - return nil -} - -func (x *FooListRequest) GetQuery() *list_j5pb.QueryRequest { - if x != nil { - return x.Query - } - return nil -} - -type FooListResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Foo []*test_pb.FooState `protobuf:"bytes,1,rep,name=foo,proto3" json:"foo,omitempty"` - Page *list_j5pb.PageResponse `protobuf:"bytes,2,opt,name=page,proto3" json:"page,omitempty"` -} - -func (x *FooListResponse) Reset() { - *x = FooListResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooListResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooListResponse) ProtoMessage() {} - -func (x *FooListResponse) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[3] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooListResponse.ProtoReflect.Descriptor instead. -func (*FooListResponse) Descriptor() ([]byte, []int) { - return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{3} -} - -func (x *FooListResponse) GetFoo() []*test_pb.FooState { - if x != nil { - return x.Foo - } - return nil -} - -func (x *FooListResponse) GetPage() *list_j5pb.PageResponse { - if x != nil { - return x.Page - } - return nil -} - -type FooEventsRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - FooId string `protobuf:"bytes,1,opt,name=foo_id,json=fooId,proto3" json:"foo_id,omitempty"` - Page *list_j5pb.PageRequest `protobuf:"bytes,2,opt,name=page,proto3" json:"page,omitempty"` - Query *list_j5pb.QueryRequest `protobuf:"bytes,3,opt,name=query,proto3" json:"query,omitempty"` -} - -func (x *FooEventsRequest) Reset() { - *x = FooEventsRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooEventsRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooEventsRequest) ProtoMessage() {} - -func (x *FooEventsRequest) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[4] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooEventsRequest.ProtoReflect.Descriptor instead. -func (*FooEventsRequest) Descriptor() ([]byte, []int) { - return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{4} -} - -func (x *FooEventsRequest) GetFooId() string { - if x != nil { - return x.FooId - } - return "" -} - -func (x *FooEventsRequest) GetPage() *list_j5pb.PageRequest { - if x != nil { - return x.Page - } - return nil -} - -func (x *FooEventsRequest) GetQuery() *list_j5pb.QueryRequest { - if x != nil { - return x.Query - } - return nil -} - -type FooEventsResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Events []*test_pb.FooEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` - Page *list_j5pb.PageResponse `protobuf:"bytes,2,opt,name=page,proto3" json:"page,omitempty"` -} - -func (x *FooEventsResponse) Reset() { - *x = FooEventsResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooEventsResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooEventsResponse) ProtoMessage() {} - -func (x *FooEventsResponse) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[5] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooEventsResponse.ProtoReflect.Descriptor instead. -func (*FooEventsResponse) Descriptor() ([]byte, []int) { - return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{5} -} - -func (x *FooEventsResponse) GetEvents() []*test_pb.FooEvent { - if x != nil { - return x.Events - } - return nil -} - -func (x *FooEventsResponse) GetPage() *list_j5pb.PageResponse { - if x != nil { - return x.Page - } - return nil -} - -type FooSummaryRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields -} - -func (x *FooSummaryRequest) Reset() { - *x = FooSummaryRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooSummaryRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooSummaryRequest) ProtoMessage() {} - -func (x *FooSummaryRequest) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[6] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooSummaryRequest.ProtoReflect.Descriptor instead. -func (*FooSummaryRequest) Descriptor() ([]byte, []int) { - return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{6} -} - -type FooSummaryResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - CountFoos int64 `protobuf:"varint,1,opt,name=count_foos,json=countFoos,proto3" json:"count_foos,omitempty"` - TotalWeight int64 `protobuf:"varint,2,opt,name=total_weight,json=totalWeight,proto3" json:"total_weight,omitempty"` - TotalHeight int64 `protobuf:"varint,3,opt,name=total_height,json=totalHeight,proto3" json:"total_height,omitempty"` - TotalLength int64 `protobuf:"varint,4,opt,name=total_length,json=totalLength,proto3" json:"total_length,omitempty"` -} - -func (x *FooSummaryResponse) Reset() { - *x = FooSummaryResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[7] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooSummaryResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooSummaryResponse) ProtoMessage() {} - -func (x *FooSummaryResponse) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[7] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooSummaryResponse.ProtoReflect.Descriptor instead. -func (*FooSummaryResponse) Descriptor() ([]byte, []int) { - return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{7} -} - -func (x *FooSummaryResponse) GetCountFoos() int64 { - if x != nil { - return x.CountFoos - } - return 0 -} - -func (x *FooSummaryResponse) GetTotalWeight() int64 { - if x != nil { - return x.TotalWeight - } - return 0 -} - -func (x *FooSummaryResponse) GetTotalHeight() int64 { - if x != nil { - return x.TotalHeight - } - return 0 -} - -func (x *FooSummaryResponse) GetTotalLength() int64 { - if x != nil { - return x.TotalLength - } - return 0 -} - -var File_test_v1_service_foo_p_j5s_proto protoreflect.FileDescriptor - -var file_test_v1_service_foo_p_j5s_proto_rawDesc = []byte{ - 0x0a, 0x1f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2f, 0x66, 0x6f, 0x6f, 0x2e, 0x70, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x0f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, - 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, - 0x35, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6a, 0x35, 0x2f, 0x6c, - 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x6a, 0x35, 0x2f, 0x6c, 0x69, 0x73, - 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, - 0x16, 0x6a, 0x35, 0x2f, 0x6c, 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x71, 0x75, 0x65, 0x72, - 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, - 0x2f, 0x66, 0x6f, 0x6f, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5d, - 0x0a, 0x0d, 0x46, 0x6f, 0x6f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x43, 0x0a, 0x06, 0x66, 0x6f, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x2c, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, - 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x8a, 0xf7, 0x98, - 0xc6, 0x02, 0x0a, 0x72, 0x08, 0x1a, 0x06, 0x12, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x05, 0x66, - 0x6f, 0x6f, 0x49, 0x64, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0x82, 0x01, - 0x0a, 0x0e, 0x46, 0x6f, 0x6f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x32, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, - 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, - 0x03, 0x66, 0x6f, 0x6f, 0x12, 0x33, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, - 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, - 0x00, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, - 0x52, 0x00, 0x22, 0x88, 0x01, 0x0a, 0x0e, 0x46, 0x6f, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x07, 0xc2, 0xff, - 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6a, 0x35, 0x2e, - 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x05, 0x71, - 0x75, 0x65, 0x72, 0x79, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0x80, 0x01, - 0x0a, 0x0f, 0x46, 0x6f, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x2d, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, - 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x03, 0x66, 0x6f, 0x6f, - 0x12, 0x35, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, - 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, - 0x00, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, - 0x22, 0xcf, 0x01, 0x0a, 0x10, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x06, 0x66, 0x6f, 0x6f, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2c, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, - 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, - 0x02, 0x08, 0x01, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x72, 0x08, 0x1a, 0x06, 0x12, 0x04, 0x52, - 0x02, 0x08, 0x01, 0x52, 0x05, 0x66, 0x6f, 0x6f, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x61, - 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, - 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, - 0x12, 0x37, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, - 0x52, 0x00, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, - 0x52, 0x00, 0x22, 0x88, 0x01, 0x0a, 0x11, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, - 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x08, 0xc2, 0xff, 0x8e, - 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x35, 0x0a, - 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6a, 0x35, - 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, - 0x70, 0x61, 0x67, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0x1c, 0x0a, - 0x11, 0x46, 0x6f, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0xcd, 0x01, 0x0a, 0x12, - 0x46, 0x6f, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0a, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x66, 0x6f, 0x6f, 0x73, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, - 0x52, 0x09, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x46, 0x6f, 0x6f, 0x73, 0x12, 0x2b, 0x0a, 0x0c, 0x74, - 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x52, 0x0b, 0x74, 0x6f, 0x74, - 0x61, 0x6c, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2b, 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, - 0x6c, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, - 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x48, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2b, 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6c, - 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, - 0x02, 0x03, 0xfa, 0x01, 0x00, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4c, 0x65, 0x6e, 0x67, - 0x74, 0x68, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x32, 0x89, 0x03, 0x0a, 0x0f, - 0x46, 0x6f, 0x6f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x73, 0x0a, 0x06, 0x46, 0x6f, 0x6f, 0x47, 0x65, 0x74, 0x12, 0x1e, 0x2e, 0x74, 0x65, 0x73, 0x74, - 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x74, 0x65, 0x73, 0x74, - 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0xc2, 0xff, 0x8e, 0x02, - 0x04, 0x52, 0x02, 0x08, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x74, 0x65, - 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x6f, 0x6f, 0x2f, 0x71, 0x2f, 0x7b, 0x66, 0x6f, 0x6f, - 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x6d, 0x0a, 0x07, 0x46, 0x6f, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x12, - 0x1f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, - 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x20, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, - 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x22, 0x1f, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x10, 0x01, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x6f, - 0x6f, 0x2f, 0x71, 0x12, 0x83, 0x01, 0x0a, 0x09, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x73, 0x12, 0x21, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, - 0x69, 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, - 0x02, 0x18, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x74, 0x65, 0x73, 0x74, - 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x6f, 0x6f, 0x2f, 0x71, 0x2f, 0x7b, 0x66, 0x6f, 0x6f, 0x5f, 0x69, - 0x64, 0x7d, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x0c, 0xea, 0x85, 0x8f, 0x02, 0x07, - 0x0a, 0x05, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x32, 0x81, 0x01, 0x0a, 0x0a, 0x46, 0x6f, 0x6f, 0x53, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x73, 0x0a, 0x0a, 0x46, 0x6f, 0x6f, 0x53, 0x75, 0x6d, - 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, - 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, - 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x75, - 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, - 0x66, 0x6f, 0x6f, 0x2f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x42, 0x47, 0x5a, 0x45, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, - 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x67, 0x65, 0x6e, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, 0x74, - 0x5f, 0x73, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_test_v1_service_foo_p_j5s_proto_rawDescOnce sync.Once - file_test_v1_service_foo_p_j5s_proto_rawDescData = file_test_v1_service_foo_p_j5s_proto_rawDesc -) - -func file_test_v1_service_foo_p_j5s_proto_rawDescGZIP() []byte { - file_test_v1_service_foo_p_j5s_proto_rawDescOnce.Do(func() { - file_test_v1_service_foo_p_j5s_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_v1_service_foo_p_j5s_proto_rawDescData) - }) - return file_test_v1_service_foo_p_j5s_proto_rawDescData -} - -var file_test_v1_service_foo_p_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 8) -var file_test_v1_service_foo_p_j5s_proto_goTypes = []interface{}{ - (*FooGetRequest)(nil), // 0: test.v1.service.FooGetRequest - (*FooGetResponse)(nil), // 1: test.v1.service.FooGetResponse - (*FooListRequest)(nil), // 2: test.v1.service.FooListRequest - (*FooListResponse)(nil), // 3: test.v1.service.FooListResponse - (*FooEventsRequest)(nil), // 4: test.v1.service.FooEventsRequest - (*FooEventsResponse)(nil), // 5: test.v1.service.FooEventsResponse - (*FooSummaryRequest)(nil), // 6: test.v1.service.FooSummaryRequest - (*FooSummaryResponse)(nil), // 7: test.v1.service.FooSummaryResponse - (*test_pb.FooState)(nil), // 8: test.v1.FooState - (*test_pb.FooEvent)(nil), // 9: test.v1.FooEvent - (*list_j5pb.PageRequest)(nil), // 10: j5.list.v1.PageRequest - (*list_j5pb.QueryRequest)(nil), // 11: j5.list.v1.QueryRequest - (*list_j5pb.PageResponse)(nil), // 12: j5.list.v1.PageResponse -} -var file_test_v1_service_foo_p_j5s_proto_depIdxs = []int32{ - 8, // 0: test.v1.service.FooGetResponse.foo:type_name -> test.v1.FooState - 9, // 1: test.v1.service.FooGetResponse.events:type_name -> test.v1.FooEvent - 10, // 2: test.v1.service.FooListRequest.page:type_name -> j5.list.v1.PageRequest - 11, // 3: test.v1.service.FooListRequest.query:type_name -> j5.list.v1.QueryRequest - 8, // 4: test.v1.service.FooListResponse.foo:type_name -> test.v1.FooState - 12, // 5: test.v1.service.FooListResponse.page:type_name -> j5.list.v1.PageResponse - 10, // 6: test.v1.service.FooEventsRequest.page:type_name -> j5.list.v1.PageRequest - 11, // 7: test.v1.service.FooEventsRequest.query:type_name -> j5.list.v1.QueryRequest - 9, // 8: test.v1.service.FooEventsResponse.events:type_name -> test.v1.FooEvent - 12, // 9: test.v1.service.FooEventsResponse.page:type_name -> j5.list.v1.PageResponse - 0, // 10: test.v1.service.FooQueryService.FooGet:input_type -> test.v1.service.FooGetRequest - 2, // 11: test.v1.service.FooQueryService.FooList:input_type -> test.v1.service.FooListRequest - 4, // 12: test.v1.service.FooQueryService.FooEvents:input_type -> test.v1.service.FooEventsRequest - 6, // 13: test.v1.service.FooService.FooSummary:input_type -> test.v1.service.FooSummaryRequest - 1, // 14: test.v1.service.FooQueryService.FooGet:output_type -> test.v1.service.FooGetResponse - 3, // 15: test.v1.service.FooQueryService.FooList:output_type -> test.v1.service.FooListResponse - 5, // 16: test.v1.service.FooQueryService.FooEvents:output_type -> test.v1.service.FooEventsResponse - 7, // 17: test.v1.service.FooService.FooSummary:output_type -> test.v1.service.FooSummaryResponse - 14, // [14:18] is the sub-list for method output_type - 10, // [10:14] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name -} - -func init() { file_test_v1_service_foo_p_j5s_proto_init() } -func file_test_v1_service_foo_p_j5s_proto_init() { - if File_test_v1_service_foo_p_j5s_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_test_v1_service_foo_p_j5s_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooGetRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_service_foo_p_j5s_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooGetResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_service_foo_p_j5s_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooListRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_service_foo_p_j5s_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooListResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_service_foo_p_j5s_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooEventsRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_service_foo_p_j5s_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooEventsResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_service_foo_p_j5s_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooSummaryRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_test_v1_service_foo_p_j5s_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooSummaryResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_test_v1_service_foo_p_j5s_proto_rawDesc, - NumEnums: 0, - NumMessages: 8, - NumExtensions: 0, - NumServices: 2, - }, - GoTypes: file_test_v1_service_foo_p_j5s_proto_goTypes, - DependencyIndexes: file_test_v1_service_foo_p_j5s_proto_depIdxs, - MessageInfos: file_test_v1_service_foo_p_j5s_proto_msgTypes, - }.Build() - File_test_v1_service_foo_p_j5s_proto = out.File - file_test_v1_service_foo_p_j5s_proto_rawDesc = nil - file_test_v1_service_foo_p_j5s_proto_goTypes = nil - file_test_v1_service_foo_p_j5s_proto_depIdxs = nil -} diff --git a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_grpc.pb.go b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_grpc.pb.go deleted file mode 100644 index 12ef3a5..0000000 --- a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_grpc.pb.go +++ /dev/null @@ -1,277 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.4.0 -// - protoc (unknown) -// source: test/v1/service/foo.p.j5s.proto - -package test_spb - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 - -const ( - FooQueryService_FooGet_FullMethodName = "/test.v1.service.FooQueryService/FooGet" - FooQueryService_FooList_FullMethodName = "/test.v1.service.FooQueryService/FooList" - FooQueryService_FooEvents_FullMethodName = "/test.v1.service.FooQueryService/FooEvents" -) - -// FooQueryServiceClient is the client API for FooQueryService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type FooQueryServiceClient interface { - FooGet(ctx context.Context, in *FooGetRequest, opts ...grpc.CallOption) (*FooGetResponse, error) - FooList(ctx context.Context, in *FooListRequest, opts ...grpc.CallOption) (*FooListResponse, error) - FooEvents(ctx context.Context, in *FooEventsRequest, opts ...grpc.CallOption) (*FooEventsResponse, error) -} - -type fooQueryServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewFooQueryServiceClient(cc grpc.ClientConnInterface) FooQueryServiceClient { - return &fooQueryServiceClient{cc} -} - -func (c *fooQueryServiceClient) FooGet(ctx context.Context, in *FooGetRequest, opts ...grpc.CallOption) (*FooGetResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(FooGetResponse) - err := c.cc.Invoke(ctx, FooQueryService_FooGet_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fooQueryServiceClient) FooList(ctx context.Context, in *FooListRequest, opts ...grpc.CallOption) (*FooListResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(FooListResponse) - err := c.cc.Invoke(ctx, FooQueryService_FooList_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *fooQueryServiceClient) FooEvents(ctx context.Context, in *FooEventsRequest, opts ...grpc.CallOption) (*FooEventsResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(FooEventsResponse) - err := c.cc.Invoke(ctx, FooQueryService_FooEvents_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -// FooQueryServiceServer is the server API for FooQueryService service. -// All implementations must embed UnimplementedFooQueryServiceServer -// for forward compatibility -type FooQueryServiceServer interface { - FooGet(context.Context, *FooGetRequest) (*FooGetResponse, error) - FooList(context.Context, *FooListRequest) (*FooListResponse, error) - FooEvents(context.Context, *FooEventsRequest) (*FooEventsResponse, error) - mustEmbedUnimplementedFooQueryServiceServer() -} - -// UnimplementedFooQueryServiceServer must be embedded to have forward compatible implementations. -type UnimplementedFooQueryServiceServer struct { -} - -func (UnimplementedFooQueryServiceServer) FooGet(context.Context, *FooGetRequest) (*FooGetResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method FooGet not implemented") -} -func (UnimplementedFooQueryServiceServer) FooList(context.Context, *FooListRequest) (*FooListResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method FooList not implemented") -} -func (UnimplementedFooQueryServiceServer) FooEvents(context.Context, *FooEventsRequest) (*FooEventsResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method FooEvents not implemented") -} -func (UnimplementedFooQueryServiceServer) mustEmbedUnimplementedFooQueryServiceServer() {} - -// UnsafeFooQueryServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to FooQueryServiceServer will -// result in compilation errors. -type UnsafeFooQueryServiceServer interface { - mustEmbedUnimplementedFooQueryServiceServer() -} - -func RegisterFooQueryServiceServer(s grpc.ServiceRegistrar, srv FooQueryServiceServer) { - s.RegisterService(&FooQueryService_ServiceDesc, srv) -} - -func _FooQueryService_FooGet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(FooGetRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FooQueryServiceServer).FooGet(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: FooQueryService_FooGet_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FooQueryServiceServer).FooGet(ctx, req.(*FooGetRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _FooQueryService_FooList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(FooListRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FooQueryServiceServer).FooList(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: FooQueryService_FooList_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FooQueryServiceServer).FooList(ctx, req.(*FooListRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _FooQueryService_FooEvents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(FooEventsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FooQueryServiceServer).FooEvents(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: FooQueryService_FooEvents_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FooQueryServiceServer).FooEvents(ctx, req.(*FooEventsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// FooQueryService_ServiceDesc is the grpc.ServiceDesc for FooQueryService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var FooQueryService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "test.v1.service.FooQueryService", - HandlerType: (*FooQueryServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "FooGet", - Handler: _FooQueryService_FooGet_Handler, - }, - { - MethodName: "FooList", - Handler: _FooQueryService_FooList_Handler, - }, - { - MethodName: "FooEvents", - Handler: _FooQueryService_FooEvents_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "test/v1/service/foo.p.j5s.proto", -} - -const ( - FooService_FooSummary_FullMethodName = "/test.v1.service.FooService/FooSummary" -) - -// FooServiceClient is the client API for FooService service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type FooServiceClient interface { - FooSummary(ctx context.Context, in *FooSummaryRequest, opts ...grpc.CallOption) (*FooSummaryResponse, error) -} - -type fooServiceClient struct { - cc grpc.ClientConnInterface -} - -func NewFooServiceClient(cc grpc.ClientConnInterface) FooServiceClient { - return &fooServiceClient{cc} -} - -func (c *fooServiceClient) FooSummary(ctx context.Context, in *FooSummaryRequest, opts ...grpc.CallOption) (*FooSummaryResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(FooSummaryResponse) - err := c.cc.Invoke(ctx, FooService_FooSummary_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -// FooServiceServer is the server API for FooService service. -// All implementations must embed UnimplementedFooServiceServer -// for forward compatibility -type FooServiceServer interface { - FooSummary(context.Context, *FooSummaryRequest) (*FooSummaryResponse, error) - mustEmbedUnimplementedFooServiceServer() -} - -// UnimplementedFooServiceServer must be embedded to have forward compatible implementations. -type UnimplementedFooServiceServer struct { -} - -func (UnimplementedFooServiceServer) FooSummary(context.Context, *FooSummaryRequest) (*FooSummaryResponse, error) { - return nil, status.Errorf(codes.Unimplemented, "method FooSummary not implemented") -} -func (UnimplementedFooServiceServer) mustEmbedUnimplementedFooServiceServer() {} - -// UnsafeFooServiceServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to FooServiceServer will -// result in compilation errors. -type UnsafeFooServiceServer interface { - mustEmbedUnimplementedFooServiceServer() -} - -func RegisterFooServiceServer(s grpc.ServiceRegistrar, srv FooServiceServer) { - s.RegisterService(&FooService_ServiceDesc, srv) -} - -func _FooService_FooSummary_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(FooSummaryRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FooServiceServer).FooSummary(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: FooService_FooSummary_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FooServiceServer).FooSummary(ctx, req.(*FooSummaryRequest)) - } - return interceptor(ctx, in, info, handler) -} - -// FooService_ServiceDesc is the grpc.ServiceDesc for FooService service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var FooService_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "test.v1.service.FooService", - HandlerType: (*FooServiceServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "FooSummary", - Handler: _FooService_FooSummary_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "test/v1/service/foo.p.j5s.proto", -} diff --git a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_j5.pb.go b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_j5.pb.go new file mode 100644 index 0000000..d03702c --- /dev/null +++ b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_j5.pb.go @@ -0,0 +1,32 @@ +// Code generated by protoc-gen-go-j5. DO NOT EDIT. + +package test_spb + +import ( + j5reflect "github.com/pentops/j5/lib/j5reflect" +) + +func (msg *FooGetRequest) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *FooGetResponse) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *FooListRequest) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *FooListResponse) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *FooEventsRequest) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *FooEventsResponse) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *FooSummaryRequest) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} +func (msg *FooSummaryResponse) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} diff --git a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_psm_query.pb.go b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_psm_query.pb.go deleted file mode 100644 index 2daa236..0000000 --- a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_psm_query.pb.go +++ /dev/null @@ -1,115 +0,0 @@ -// Code generated by protoc-gen-go-psm. DO NOT EDIT. - -package test_spb - -import ( - context "context" - psm "github.com/pentops/protostate/psm" - sqrlx "github.com/pentops/sqrlx.go/sqrlx" -) - -// State Query Service for %sFoo -// QuerySet is the query set for the Foo service. - -type FooPSMQuerySet = psm.StateQuerySet[ - *FooGetRequest, - *FooGetResponse, - *FooListRequest, - *FooListResponse, - *FooEventsRequest, - *FooEventsResponse, -] - -func NewFooPSMQuerySet( - smSpec psm.QuerySpec[ - *FooGetRequest, - *FooGetResponse, - *FooListRequest, - *FooListResponse, - *FooEventsRequest, - *FooEventsResponse, - ], - options psm.StateQueryOptions, -) (*FooPSMQuerySet, error) { - return psm.BuildStateQuerySet[ - *FooGetRequest, - *FooGetResponse, - *FooListRequest, - *FooListResponse, - *FooEventsRequest, - *FooEventsResponse, - ](smSpec, options) -} - -type FooPSMQuerySpec = psm.QuerySpec[ - *FooGetRequest, - *FooGetResponse, - *FooListRequest, - *FooListResponse, - *FooEventsRequest, - *FooEventsResponse, -] - -func DefaultFooPSMQuerySpec(tableSpec psm.QueryTableSpec) FooPSMQuerySpec { - return psm.QuerySpec[ - *FooGetRequest, - *FooGetResponse, - *FooListRequest, - *FooListResponse, - *FooEventsRequest, - *FooEventsResponse, - ]{ - QueryTableSpec: tableSpec, - ListRequestFilter: func(req *FooListRequest) (map[string]interface{}, error) { - filter := map[string]interface{}{} - return filter, nil - }, - ListEventsRequestFilter: func(req *FooEventsRequest) (map[string]interface{}, error) { - filter := map[string]interface{}{} - filter["foo_id"] = req.FooId - return filter, nil - }, - } -} - -type FooQueryServiceImpl struct { - db sqrlx.Transactor - querySet *FooPSMQuerySet - UnsafeFooQueryServiceServer -} - -var _ FooQueryServiceServer = &FooQueryServiceImpl{} - -func NewFooQueryServiceImpl(db sqrlx.Transactor, querySet *FooPSMQuerySet) *FooQueryServiceImpl { - return &FooQueryServiceImpl{ - db: db, - querySet: querySet, - } -} - -func (s *FooQueryServiceImpl) FooGet(ctx context.Context, req *FooGetRequest) (*FooGetResponse, error) { - resObject := &FooGetResponse{} - err := s.querySet.Get(ctx, s.db, req, resObject) - if err != nil { - return nil, err - } - return resObject, nil -} - -func (s *FooQueryServiceImpl) FooList(ctx context.Context, req *FooListRequest) (*FooListResponse, error) { - resObject := &FooListResponse{} - err := s.querySet.List(ctx, s.db, req, resObject) - if err != nil { - return nil, err - } - return resObject, nil -} - -func (s *FooQueryServiceImpl) FooEvents(ctx context.Context, req *FooEventsRequest) (*FooEventsResponse, error) { - resObject := &FooEventsResponse{} - err := s.querySet.ListEvents(ctx, s.db, req, resObject) - if err != nil { - return nil, err - } - return resObject, nil -} diff --git a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_sugar.pb.go b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_sugar.pb.go deleted file mode 100644 index 2bfa669..0000000 --- a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_sugar.pb.go +++ /dev/null @@ -1,3 +0,0 @@ -// Code generated by protoc-gen-go-sugar. DO NOT EDIT. - -package test_spb diff --git a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s.pb.go b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s.pb.go deleted file mode 100644 index 8c05cf5..0000000 --- a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s.pb.go +++ /dev/null @@ -1,235 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.32.0 -// protoc (unknown) -// source: test/v1/topic/bar.p.j5s.proto - -package test_tpb - -import ( - _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" - _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" - _ "github.com/pentops/j5/gen/j5/messaging/v1/messaging_j5pb" - psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" - test_pb "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - emptypb "google.golang.org/protobuf/types/known/emptypb" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type BarEventMessage struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Metadata *psm_j5pb.EventPublishMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - Keys *test_pb.BarKeys `protobuf:"bytes,2,opt,name=keys,proto3" json:"keys,omitempty"` - Event *test_pb.BarEventType `protobuf:"bytes,3,opt,name=event,proto3" json:"event,omitempty"` - Data *test_pb.BarData `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` - Status test_pb.BarStatus `protobuf:"varint,5,opt,name=status,proto3,enum=test.v1.BarStatus" json:"status,omitempty"` -} - -func (x *BarEventMessage) Reset() { - *x = BarEventMessage{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_topic_bar_p_j5s_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BarEventMessage) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BarEventMessage) ProtoMessage() {} - -func (x *BarEventMessage) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_topic_bar_p_j5s_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use BarEventMessage.ProtoReflect.Descriptor instead. -func (*BarEventMessage) Descriptor() ([]byte, []int) { - return file_test_v1_topic_bar_p_j5s_proto_rawDescGZIP(), []int{0} -} - -func (x *BarEventMessage) GetMetadata() *psm_j5pb.EventPublishMetadata { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *BarEventMessage) GetKeys() *test_pb.BarKeys { - if x != nil { - return x.Keys - } - return nil -} - -func (x *BarEventMessage) GetEvent() *test_pb.BarEventType { - if x != nil { - return x.Event - } - return nil -} - -func (x *BarEventMessage) GetData() *test_pb.BarData { - if x != nil { - return x.Data - } - return nil -} - -func (x *BarEventMessage) GetStatus() test_pb.BarStatus { - if x != nil { - return x.Status - } - return test_pb.BarStatus(0) -} - -var File_test_v1_topic_bar_p_j5s_proto protoreflect.FileDescriptor - -var file_test_v1_topic_bar_p_j5s_proto_rawDesc = []byte{ - 0x0a, 0x1d, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x2f, - 0x62, 0x61, 0x72, 0x2e, 0x70, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, - 0x0d, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x1a, 0x1b, - 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, - 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, 0x35, 0x2f, 0x65, 0x78, 0x74, - 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x6a, 0x35, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x69, 0x6e, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x6a, 0x35, 0x2f, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x61, - 0x72, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xce, 0x02, 0x0a, 0x0f, - 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x4c, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x21, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, - 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x33, 0x0a, - 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, - 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0d, 0xba, - 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x6b, 0x65, - 0x79, 0x73, 0x12, 0x3a, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, - 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x62, 0x00, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x33, - 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x44, 0x61, 0x74, 0x61, 0x42, 0x0d, - 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x3e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, - 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x12, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, - 0x01, 0x02, 0x10, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x32, 0x79, 0x0a, 0x0f, - 0x42, 0x61, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, - 0x42, 0x0a, 0x08, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x74, 0x65, - 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x42, 0x61, 0x72, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x1a, 0x22, 0xda, 0xa2, 0xf5, 0xe4, 0x02, 0x1c, 0x0a, 0x0b, 0x62, 0x61, 0x72, - 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x6a, 0x0d, 0x0a, 0x0b, 0x74, 0x65, 0x73, 0x74, - 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x42, 0x47, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, - 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x70, 0x62, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_test_v1_topic_bar_p_j5s_proto_rawDescOnce sync.Once - file_test_v1_topic_bar_p_j5s_proto_rawDescData = file_test_v1_topic_bar_p_j5s_proto_rawDesc -) - -func file_test_v1_topic_bar_p_j5s_proto_rawDescGZIP() []byte { - file_test_v1_topic_bar_p_j5s_proto_rawDescOnce.Do(func() { - file_test_v1_topic_bar_p_j5s_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_v1_topic_bar_p_j5s_proto_rawDescData) - }) - return file_test_v1_topic_bar_p_j5s_proto_rawDescData -} - -var file_test_v1_topic_bar_p_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_test_v1_topic_bar_p_j5s_proto_goTypes = []interface{}{ - (*BarEventMessage)(nil), // 0: test.v1.topic.BarEventMessage - (*psm_j5pb.EventPublishMetadata)(nil), // 1: j5.state.v1.EventPublishMetadata - (*test_pb.BarKeys)(nil), // 2: test.v1.BarKeys - (*test_pb.BarEventType)(nil), // 3: test.v1.BarEventType - (*test_pb.BarData)(nil), // 4: test.v1.BarData - (test_pb.BarStatus)(0), // 5: test.v1.BarStatus - (*emptypb.Empty)(nil), // 6: google.protobuf.Empty -} -var file_test_v1_topic_bar_p_j5s_proto_depIdxs = []int32{ - 1, // 0: test.v1.topic.BarEventMessage.metadata:type_name -> j5.state.v1.EventPublishMetadata - 2, // 1: test.v1.topic.BarEventMessage.keys:type_name -> test.v1.BarKeys - 3, // 2: test.v1.topic.BarEventMessage.event:type_name -> test.v1.BarEventType - 4, // 3: test.v1.topic.BarEventMessage.data:type_name -> test.v1.BarData - 5, // 4: test.v1.topic.BarEventMessage.status:type_name -> test.v1.BarStatus - 0, // 5: test.v1.topic.BarPublishTopic.BarEvent:input_type -> test.v1.topic.BarEventMessage - 6, // 6: test.v1.topic.BarPublishTopic.BarEvent:output_type -> google.protobuf.Empty - 6, // [6:7] is the sub-list for method output_type - 5, // [5:6] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name -} - -func init() { file_test_v1_topic_bar_p_j5s_proto_init() } -func file_test_v1_topic_bar_p_j5s_proto_init() { - if File_test_v1_topic_bar_p_j5s_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_test_v1_topic_bar_p_j5s_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BarEventMessage); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_test_v1_topic_bar_p_j5s_proto_rawDesc, - NumEnums: 0, - NumMessages: 1, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_test_v1_topic_bar_p_j5s_proto_goTypes, - DependencyIndexes: file_test_v1_topic_bar_p_j5s_proto_depIdxs, - MessageInfos: file_test_v1_topic_bar_p_j5s_proto_msgTypes, - }.Build() - File_test_v1_topic_bar_p_j5s_proto = out.File - file_test_v1_topic_bar_p_j5s_proto_rawDesc = nil - file_test_v1_topic_bar_p_j5s_proto_goTypes = nil - file_test_v1_topic_bar_p_j5s_proto_depIdxs = nil -} diff --git a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_grpc.pb.go b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_grpc.pb.go deleted file mode 100644 index c375433..0000000 --- a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_grpc.pb.go +++ /dev/null @@ -1,111 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.4.0 -// - protoc (unknown) -// source: test/v1/topic/bar.p.j5s.proto - -package test_tpb - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - emptypb "google.golang.org/protobuf/types/known/emptypb" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 - -const ( - BarPublishTopic_BarEvent_FullMethodName = "/test.v1.topic.BarPublishTopic/BarEvent" -) - -// BarPublishTopicClient is the client API for BarPublishTopic service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type BarPublishTopicClient interface { - BarEvent(ctx context.Context, in *BarEventMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) -} - -type barPublishTopicClient struct { - cc grpc.ClientConnInterface -} - -func NewBarPublishTopicClient(cc grpc.ClientConnInterface) BarPublishTopicClient { - return &barPublishTopicClient{cc} -} - -func (c *barPublishTopicClient) BarEvent(ctx context.Context, in *BarEventMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, BarPublishTopic_BarEvent_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -// BarPublishTopicServer is the server API for BarPublishTopic service. -// All implementations must embed UnimplementedBarPublishTopicServer -// for forward compatibility -type BarPublishTopicServer interface { - BarEvent(context.Context, *BarEventMessage) (*emptypb.Empty, error) - mustEmbedUnimplementedBarPublishTopicServer() -} - -// UnimplementedBarPublishTopicServer must be embedded to have forward compatible implementations. -type UnimplementedBarPublishTopicServer struct { -} - -func (UnimplementedBarPublishTopicServer) BarEvent(context.Context, *BarEventMessage) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method BarEvent not implemented") -} -func (UnimplementedBarPublishTopicServer) mustEmbedUnimplementedBarPublishTopicServer() {} - -// UnsafeBarPublishTopicServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to BarPublishTopicServer will -// result in compilation errors. -type UnsafeBarPublishTopicServer interface { - mustEmbedUnimplementedBarPublishTopicServer() -} - -func RegisterBarPublishTopicServer(s grpc.ServiceRegistrar, srv BarPublishTopicServer) { - s.RegisterService(&BarPublishTopic_ServiceDesc, srv) -} - -func _BarPublishTopic_BarEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(BarEventMessage) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(BarPublishTopicServer).BarEvent(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: BarPublishTopic_BarEvent_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(BarPublishTopicServer).BarEvent(ctx, req.(*BarEventMessage)) - } - return interceptor(ctx, in, info, handler) -} - -// BarPublishTopic_ServiceDesc is the grpc.ServiceDesc for BarPublishTopic service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var BarPublishTopic_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "test.v1.topic.BarPublishTopic", - HandlerType: (*BarPublishTopicServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "BarEvent", - Handler: _BarPublishTopic_BarEvent_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "test/v1/topic/bar.p.j5s.proto", -} diff --git a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_j5.pb.go b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_j5.pb.go new file mode 100644 index 0000000..d1aaad2 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_j5.pb.go @@ -0,0 +1,11 @@ +// Code generated by protoc-gen-go-j5. DO NOT EDIT. + +package test_tpb + +import ( + j5reflect "github.com/pentops/j5/lib/j5reflect" +) + +func (msg *BarEventMessage) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} diff --git a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_o5_messaging.pb.go b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_o5_messaging.pb.go deleted file mode 100644 index bdc4966..0000000 --- a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_o5_messaging.pb.go +++ /dev/null @@ -1,95 +0,0 @@ -// Code generated by protoc-gen-go-o5-messaging. DO NOT EDIT. -// versions: -// - protoc-gen-go-o5-messaging 0.0.0 -// source: test/v1/topic/bar.p.j5s.proto - -package test_tpb - -import ( - context "context" - messaging_pb "github.com/pentops/o5-messaging/gen/o5/messaging/v1/messaging_pb" - o5msg "github.com/pentops/o5-messaging/o5msg" -) - -// Service: BarPublishTopic -// Method: BarEvent - -func (msg *BarEventMessage) O5MessageHeader() o5msg.Header { - header := o5msg.Header{ - GrpcService: "test.v1.topic.BarPublishTopic", - GrpcMethod: "BarEvent", - Headers: map[string]string{}, - DestinationTopic: "bar_publish", - } - header.Extension = &messaging_pb.Message_Event_{ - Event: &messaging_pb.Message_Event{ - EntityName: "test.v1.Bar", - }, - } - return header -} - -type BarPublishTopicTxSender[C any] struct { - sender o5msg.TxSender[C] -} - -func NewBarPublishTopicTxSender[C any](sender o5msg.TxSender[C]) *BarPublishTopicTxSender[C] { - sender.Register(o5msg.TopicDescriptor{ - Service: "test.v1.topic.BarPublishTopic", - Methods: []o5msg.MethodDescriptor{ - { - Name: "BarEvent", - Message: (*BarEventMessage).ProtoReflect(nil).Descriptor(), - }, - }, - }) - return &BarPublishTopicTxSender[C]{sender: sender} -} - -type BarPublishTopicCollector[C any] struct { - collector o5msg.Collector[C] -} - -func NewBarPublishTopicCollector[C any](collector o5msg.Collector[C]) *BarPublishTopicCollector[C] { - collector.Register(o5msg.TopicDescriptor{ - Service: "test.v1.topic.BarPublishTopic", - Methods: []o5msg.MethodDescriptor{ - { - Name: "BarEvent", - Message: (*BarEventMessage).ProtoReflect(nil).Descriptor(), - }, - }, - }) - return &BarPublishTopicCollector[C]{collector: collector} -} - -type BarPublishTopicPublisher struct { - publisher o5msg.Publisher -} - -func NewBarPublishTopicPublisher(publisher o5msg.Publisher) *BarPublishTopicPublisher { - publisher.Register(o5msg.TopicDescriptor{ - Service: "test.v1.topic.BarPublishTopic", - Methods: []o5msg.MethodDescriptor{ - { - Name: "BarEvent", - Message: (*BarEventMessage).ProtoReflect(nil).Descriptor(), - }, - }, - }) - return &BarPublishTopicPublisher{publisher: publisher} -} - -// Method: BarEvent - -func (send BarPublishTopicTxSender[C]) BarEvent(ctx context.Context, sendContext C, msg *BarEventMessage) error { - return send.sender.Send(ctx, sendContext, msg) -} - -func (collect BarPublishTopicCollector[C]) BarEvent(sendContext C, msg *BarEventMessage) { - collect.collector.Collect(sendContext, msg) -} - -func (publish BarPublishTopicPublisher) BarEvent(ctx context.Context, msg *BarEventMessage) error { - return publish.publisher.Publish(ctx, msg) -} diff --git a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_psm_publish.pb.go b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_psm_publish.pb.go deleted file mode 100644 index 282581d..0000000 --- a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_psm_publish.pb.go +++ /dev/null @@ -1,35 +0,0 @@ -// Code generated by protoc-gen-go-psm. DO NOT EDIT. - -package test_tpb - -import ( - context "context" - test_pb "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" - psm "github.com/pentops/protostate/psm" -) - -// Publish Toipc for test.v1.Bar -func PublishBar() psm.GeneralEventHook[ - *test_pb.BarKeys, // implements psm.IKeyset - *test_pb.BarState, // implements psm.IState - test_pb.BarStatus, // implements psm.IStatusEnum - *test_pb.BarData, // implements psm.IStateData - *test_pb.BarEvent, // implements psm.IEvent - test_pb.BarPSMEvent, // implements psm.IInnerEvent -] { - return test_pb.BarPSMEventPublishHook(func( - ctx context.Context, - publisher psm.Publisher, - state *test_pb.BarState, - event *test_pb.BarEvent, - ) error { - publisher.Publish(&BarEventMessage{ - Metadata: event.EventPublishMetadata(), - Keys: event.Keys, - Event: event.Event, - Data: state.Data, - Status: state.Status, - }) - return nil - }) -} diff --git a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_sugar.pb.go b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_sugar.pb.go deleted file mode 100644 index 5c3223e..0000000 --- a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_sugar.pb.go +++ /dev/null @@ -1,3 +0,0 @@ -// Code generated by protoc-gen-go-sugar. DO NOT EDIT. - -package test_tpb diff --git a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s.pb.go b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s.pb.go deleted file mode 100644 index b9db8e0..0000000 --- a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s.pb.go +++ /dev/null @@ -1,235 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.32.0 -// protoc (unknown) -// source: test/v1/topic/foo.p.j5s.proto - -package test_tpb - -import ( - _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" - _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" - _ "github.com/pentops/j5/gen/j5/messaging/v1/messaging_j5pb" - psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" - test_pb "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - emptypb "google.golang.org/protobuf/types/known/emptypb" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type FooEventMessage struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Metadata *psm_j5pb.EventPublishMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` - Keys *test_pb.FooKeys `protobuf:"bytes,2,opt,name=keys,proto3" json:"keys,omitempty"` - Event *test_pb.FooEventType `protobuf:"bytes,3,opt,name=event,proto3" json:"event,omitempty"` - Data *test_pb.FooData `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` - Status test_pb.FooStatus `protobuf:"varint,5,opt,name=status,proto3,enum=test.v1.FooStatus" json:"status,omitempty"` -} - -func (x *FooEventMessage) Reset() { - *x = FooEventMessage{} - if protoimpl.UnsafeEnabled { - mi := &file_test_v1_topic_foo_p_j5s_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *FooEventMessage) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*FooEventMessage) ProtoMessage() {} - -func (x *FooEventMessage) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_topic_foo_p_j5s_proto_msgTypes[0] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use FooEventMessage.ProtoReflect.Descriptor instead. -func (*FooEventMessage) Descriptor() ([]byte, []int) { - return file_test_v1_topic_foo_p_j5s_proto_rawDescGZIP(), []int{0} -} - -func (x *FooEventMessage) GetMetadata() *psm_j5pb.EventPublishMetadata { - if x != nil { - return x.Metadata - } - return nil -} - -func (x *FooEventMessage) GetKeys() *test_pb.FooKeys { - if x != nil { - return x.Keys - } - return nil -} - -func (x *FooEventMessage) GetEvent() *test_pb.FooEventType { - if x != nil { - return x.Event - } - return nil -} - -func (x *FooEventMessage) GetData() *test_pb.FooData { - if x != nil { - return x.Data - } - return nil -} - -func (x *FooEventMessage) GetStatus() test_pb.FooStatus { - if x != nil { - return x.Status - } - return test_pb.FooStatus(0) -} - -var File_test_v1_topic_foo_p_j5s_proto protoreflect.FileDescriptor - -var file_test_v1_topic_foo_p_j5s_proto_rawDesc = []byte{ - 0x0a, 0x1d, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x2f, - 0x66, 0x6f, 0x6f, 0x2e, 0x70, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, - 0x0d, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x1a, 0x1b, - 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, - 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, 0x35, 0x2f, 0x65, 0x78, 0x74, - 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x6a, 0x35, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x69, 0x6e, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x6a, 0x35, 0x2f, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x6f, - 0x6f, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xce, 0x02, 0x0a, 0x0f, - 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x4c, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x21, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, - 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x33, 0x0a, - 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, - 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0d, 0xba, - 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x6b, 0x65, - 0x79, 0x73, 0x12, 0x3a, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, - 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x62, 0x00, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x33, - 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x42, 0x0d, - 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x64, - 0x61, 0x74, 0x61, 0x12, 0x3e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, - 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x12, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, - 0x01, 0x02, 0x10, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x32, 0x79, 0x0a, 0x0f, - 0x46, 0x6f, 0x6f, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, - 0x42, 0x0a, 0x08, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x74, 0x65, - 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x46, 0x6f, 0x6f, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, - 0x70, 0x74, 0x79, 0x1a, 0x22, 0xda, 0xa2, 0xf5, 0xe4, 0x02, 0x1c, 0x0a, 0x0b, 0x66, 0x6f, 0x6f, - 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x6a, 0x0d, 0x0a, 0x0b, 0x74, 0x65, 0x73, 0x74, - 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x42, 0x47, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, - 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, - 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x70, 0x62, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_test_v1_topic_foo_p_j5s_proto_rawDescOnce sync.Once - file_test_v1_topic_foo_p_j5s_proto_rawDescData = file_test_v1_topic_foo_p_j5s_proto_rawDesc -) - -func file_test_v1_topic_foo_p_j5s_proto_rawDescGZIP() []byte { - file_test_v1_topic_foo_p_j5s_proto_rawDescOnce.Do(func() { - file_test_v1_topic_foo_p_j5s_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_v1_topic_foo_p_j5s_proto_rawDescData) - }) - return file_test_v1_topic_foo_p_j5s_proto_rawDescData -} - -var file_test_v1_topic_foo_p_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_test_v1_topic_foo_p_j5s_proto_goTypes = []interface{}{ - (*FooEventMessage)(nil), // 0: test.v1.topic.FooEventMessage - (*psm_j5pb.EventPublishMetadata)(nil), // 1: j5.state.v1.EventPublishMetadata - (*test_pb.FooKeys)(nil), // 2: test.v1.FooKeys - (*test_pb.FooEventType)(nil), // 3: test.v1.FooEventType - (*test_pb.FooData)(nil), // 4: test.v1.FooData - (test_pb.FooStatus)(0), // 5: test.v1.FooStatus - (*emptypb.Empty)(nil), // 6: google.protobuf.Empty -} -var file_test_v1_topic_foo_p_j5s_proto_depIdxs = []int32{ - 1, // 0: test.v1.topic.FooEventMessage.metadata:type_name -> j5.state.v1.EventPublishMetadata - 2, // 1: test.v1.topic.FooEventMessage.keys:type_name -> test.v1.FooKeys - 3, // 2: test.v1.topic.FooEventMessage.event:type_name -> test.v1.FooEventType - 4, // 3: test.v1.topic.FooEventMessage.data:type_name -> test.v1.FooData - 5, // 4: test.v1.topic.FooEventMessage.status:type_name -> test.v1.FooStatus - 0, // 5: test.v1.topic.FooPublishTopic.FooEvent:input_type -> test.v1.topic.FooEventMessage - 6, // 6: test.v1.topic.FooPublishTopic.FooEvent:output_type -> google.protobuf.Empty - 6, // [6:7] is the sub-list for method output_type - 5, // [5:6] is the sub-list for method input_type - 5, // [5:5] is the sub-list for extension type_name - 5, // [5:5] is the sub-list for extension extendee - 0, // [0:5] is the sub-list for field type_name -} - -func init() { file_test_v1_topic_foo_p_j5s_proto_init() } -func file_test_v1_topic_foo_p_j5s_proto_init() { - if File_test_v1_topic_foo_p_j5s_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_test_v1_topic_foo_p_j5s_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooEventMessage); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_test_v1_topic_foo_p_j5s_proto_rawDesc, - NumEnums: 0, - NumMessages: 1, - NumExtensions: 0, - NumServices: 1, - }, - GoTypes: file_test_v1_topic_foo_p_j5s_proto_goTypes, - DependencyIndexes: file_test_v1_topic_foo_p_j5s_proto_depIdxs, - MessageInfos: file_test_v1_topic_foo_p_j5s_proto_msgTypes, - }.Build() - File_test_v1_topic_foo_p_j5s_proto = out.File - file_test_v1_topic_foo_p_j5s_proto_rawDesc = nil - file_test_v1_topic_foo_p_j5s_proto_goTypes = nil - file_test_v1_topic_foo_p_j5s_proto_depIdxs = nil -} diff --git a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_grpc.pb.go b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_grpc.pb.go deleted file mode 100644 index c0bc391..0000000 --- a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_grpc.pb.go +++ /dev/null @@ -1,111 +0,0 @@ -// Code generated by protoc-gen-go-grpc. DO NOT EDIT. -// versions: -// - protoc-gen-go-grpc v1.4.0 -// - protoc (unknown) -// source: test/v1/topic/foo.p.j5s.proto - -package test_tpb - -import ( - context "context" - grpc "google.golang.org/grpc" - codes "google.golang.org/grpc/codes" - status "google.golang.org/grpc/status" - emptypb "google.golang.org/protobuf/types/known/emptypb" -) - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -// Requires gRPC-Go v1.62.0 or later. -const _ = grpc.SupportPackageIsVersion8 - -const ( - FooPublishTopic_FooEvent_FullMethodName = "/test.v1.topic.FooPublishTopic/FooEvent" -) - -// FooPublishTopicClient is the client API for FooPublishTopic service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. -type FooPublishTopicClient interface { - FooEvent(ctx context.Context, in *FooEventMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) -} - -type fooPublishTopicClient struct { - cc grpc.ClientConnInterface -} - -func NewFooPublishTopicClient(cc grpc.ClientConnInterface) FooPublishTopicClient { - return &fooPublishTopicClient{cc} -} - -func (c *fooPublishTopicClient) FooEvent(ctx context.Context, in *FooEventMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(emptypb.Empty) - err := c.cc.Invoke(ctx, FooPublishTopic_FooEvent_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - -// FooPublishTopicServer is the server API for FooPublishTopic service. -// All implementations must embed UnimplementedFooPublishTopicServer -// for forward compatibility -type FooPublishTopicServer interface { - FooEvent(context.Context, *FooEventMessage) (*emptypb.Empty, error) - mustEmbedUnimplementedFooPublishTopicServer() -} - -// UnimplementedFooPublishTopicServer must be embedded to have forward compatible implementations. -type UnimplementedFooPublishTopicServer struct { -} - -func (UnimplementedFooPublishTopicServer) FooEvent(context.Context, *FooEventMessage) (*emptypb.Empty, error) { - return nil, status.Errorf(codes.Unimplemented, "method FooEvent not implemented") -} -func (UnimplementedFooPublishTopicServer) mustEmbedUnimplementedFooPublishTopicServer() {} - -// UnsafeFooPublishTopicServer may be embedded to opt out of forward compatibility for this service. -// Use of this interface is not recommended, as added methods to FooPublishTopicServer will -// result in compilation errors. -type UnsafeFooPublishTopicServer interface { - mustEmbedUnimplementedFooPublishTopicServer() -} - -func RegisterFooPublishTopicServer(s grpc.ServiceRegistrar, srv FooPublishTopicServer) { - s.RegisterService(&FooPublishTopic_ServiceDesc, srv) -} - -func _FooPublishTopic_FooEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(FooEventMessage) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(FooPublishTopicServer).FooEvent(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: FooPublishTopic_FooEvent_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(FooPublishTopicServer).FooEvent(ctx, req.(*FooEventMessage)) - } - return interceptor(ctx, in, info, handler) -} - -// FooPublishTopic_ServiceDesc is the grpc.ServiceDesc for FooPublishTopic service. -// It's only intended for direct use with grpc.RegisterService, -// and not to be introspected or modified (even as a copy) -var FooPublishTopic_ServiceDesc = grpc.ServiceDesc{ - ServiceName: "test.v1.topic.FooPublishTopic", - HandlerType: (*FooPublishTopicServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "FooEvent", - Handler: _FooPublishTopic_FooEvent_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "test/v1/topic/foo.p.j5s.proto", -} diff --git a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_j5.pb.go b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_j5.pb.go new file mode 100644 index 0000000..6b54772 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_j5.pb.go @@ -0,0 +1,11 @@ +// Code generated by protoc-gen-go-j5. DO NOT EDIT. + +package test_tpb + +import ( + j5reflect "github.com/pentops/j5/lib/j5reflect" +) + +func (msg *FooEventMessage) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg) +} diff --git a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_o5_messaging.pb.go b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_o5_messaging.pb.go deleted file mode 100644 index 47a9326..0000000 --- a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_o5_messaging.pb.go +++ /dev/null @@ -1,95 +0,0 @@ -// Code generated by protoc-gen-go-o5-messaging. DO NOT EDIT. -// versions: -// - protoc-gen-go-o5-messaging 0.0.0 -// source: test/v1/topic/foo.p.j5s.proto - -package test_tpb - -import ( - context "context" - messaging_pb "github.com/pentops/o5-messaging/gen/o5/messaging/v1/messaging_pb" - o5msg "github.com/pentops/o5-messaging/o5msg" -) - -// Service: FooPublishTopic -// Method: FooEvent - -func (msg *FooEventMessage) O5MessageHeader() o5msg.Header { - header := o5msg.Header{ - GrpcService: "test.v1.topic.FooPublishTopic", - GrpcMethod: "FooEvent", - Headers: map[string]string{}, - DestinationTopic: "foo_publish", - } - header.Extension = &messaging_pb.Message_Event_{ - Event: &messaging_pb.Message_Event{ - EntityName: "test.v1.Foo", - }, - } - return header -} - -type FooPublishTopicTxSender[C any] struct { - sender o5msg.TxSender[C] -} - -func NewFooPublishTopicTxSender[C any](sender o5msg.TxSender[C]) *FooPublishTopicTxSender[C] { - sender.Register(o5msg.TopicDescriptor{ - Service: "test.v1.topic.FooPublishTopic", - Methods: []o5msg.MethodDescriptor{ - { - Name: "FooEvent", - Message: (*FooEventMessage).ProtoReflect(nil).Descriptor(), - }, - }, - }) - return &FooPublishTopicTxSender[C]{sender: sender} -} - -type FooPublishTopicCollector[C any] struct { - collector o5msg.Collector[C] -} - -func NewFooPublishTopicCollector[C any](collector o5msg.Collector[C]) *FooPublishTopicCollector[C] { - collector.Register(o5msg.TopicDescriptor{ - Service: "test.v1.topic.FooPublishTopic", - Methods: []o5msg.MethodDescriptor{ - { - Name: "FooEvent", - Message: (*FooEventMessage).ProtoReflect(nil).Descriptor(), - }, - }, - }) - return &FooPublishTopicCollector[C]{collector: collector} -} - -type FooPublishTopicPublisher struct { - publisher o5msg.Publisher -} - -func NewFooPublishTopicPublisher(publisher o5msg.Publisher) *FooPublishTopicPublisher { - publisher.Register(o5msg.TopicDescriptor{ - Service: "test.v1.topic.FooPublishTopic", - Methods: []o5msg.MethodDescriptor{ - { - Name: "FooEvent", - Message: (*FooEventMessage).ProtoReflect(nil).Descriptor(), - }, - }, - }) - return &FooPublishTopicPublisher{publisher: publisher} -} - -// Method: FooEvent - -func (send FooPublishTopicTxSender[C]) FooEvent(ctx context.Context, sendContext C, msg *FooEventMessage) error { - return send.sender.Send(ctx, sendContext, msg) -} - -func (collect FooPublishTopicCollector[C]) FooEvent(sendContext C, msg *FooEventMessage) { - collect.collector.Collect(sendContext, msg) -} - -func (publish FooPublishTopicPublisher) FooEvent(ctx context.Context, msg *FooEventMessage) error { - return publish.publisher.Publish(ctx, msg) -} diff --git a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_psm_publish.pb.go b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_psm_publish.pb.go deleted file mode 100644 index 9e18254..0000000 --- a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_psm_publish.pb.go +++ /dev/null @@ -1,35 +0,0 @@ -// Code generated by protoc-gen-go-psm. DO NOT EDIT. - -package test_tpb - -import ( - context "context" - test_pb "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" - psm "github.com/pentops/protostate/psm" -) - -// Publish Toipc for test.v1.Foo -func PublishFoo() psm.GeneralEventHook[ - *test_pb.FooKeys, // implements psm.IKeyset - *test_pb.FooState, // implements psm.IState - test_pb.FooStatus, // implements psm.IStatusEnum - *test_pb.FooData, // implements psm.IStateData - *test_pb.FooEvent, // implements psm.IEvent - test_pb.FooPSMEvent, // implements psm.IInnerEvent -] { - return test_pb.FooPSMEventPublishHook(func( - ctx context.Context, - publisher psm.Publisher, - state *test_pb.FooState, - event *test_pb.FooEvent, - ) error { - publisher.Publish(&FooEventMessage{ - Metadata: event.EventPublishMetadata(), - Keys: event.Keys, - Event: event.Event, - Data: state.Data, - Status: state.Status, - }) - return nil - }) -} diff --git a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_sugar.pb.go b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_sugar.pb.go deleted file mode 100644 index 5c3223e..0000000 --- a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_sugar.pb.go +++ /dev/null @@ -1,3 +0,0 @@ -// Code generated by protoc-gen-go-sugar. DO NOT EDIT. - -package test_tpb diff --git a/j5.yaml b/j5.yaml index 37abbb4..7417887 100644 --- a/j5.yaml +++ b/j5.yaml @@ -21,6 +21,7 @@ generate: - base: go-sugar - base: go-psm - base: go-o5-messaging + - base: go-j5 managedPaths: - internal/testproto/gen @@ -51,3 +52,13 @@ plugins: name: go-o5-messaging docker: image: ghcr.io/pentops/protoc-gen-go-o5-messaging:fba07334e9aa1affc26b34eae82254a36f955267 + + - base: go + name: go-j5 + docker: + image: ghcr.io/pentops/protoc-gen-go-j5:latest + +pluginOverrides: + - name: go-j5 + local: + cmd: protoc-gen-go-j5 diff --git a/pquery/filter.go b/pquery/filter.go index 7dc34b6..cc7d4f0 100644 --- a/pquery/filter.go +++ b/pquery/filter.go @@ -2,18 +2,20 @@ package pquery import ( "fmt" + "regexp" "strconv" - "strings" "time" sq "github.com/elgris/sqrl" "github.com/elgris/sqrl/pg" "github.com/google/uuid" "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" + "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" + "github.com/pentops/j5/lib/id62" + "github.com/pentops/j5/lib/j5schema" "github.com/pentops/protostate/internal/pgstore" - "google.golang.org/protobuf/proto" + "github.com/shopspring/decimal" "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/types/descriptorpb" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -22,273 +24,207 @@ type filterSpec struct { filterVals []any } -func filtersForField(field protoreflect.FieldDescriptor) ([]any, error) { +func filtersForField(field *j5schema.ObjectProperty) ([]any, error) { - fieldOpts := proto.GetExtension(field.Options().(*descriptorpb.FieldOptions), list_j5pb.E_Field).(*list_j5pb.FieldConstraint) - vals := []any{} + switch bigType := field.Schema.(type) { + case *j5schema.EnumField: + schema := bigType.Schema() - if fieldOpts == nil || fieldOpts.Type == nil { - return nil, nil - } - - switch fieldOps := fieldOpts.Type.(type) { - case *list_j5pb.FieldConstraint_Float: - if fieldOps.Float.Filtering != nil && fieldOps.Float.Filtering.Filterable { - for _, val := range fieldOps.Float.Filtering.DefaultFilters { - v, err := strconv.ParseFloat(val, 32) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) - } - - vals = append(vals, v) - } + if bigType.ListRules == nil || bigType.ListRules.Filtering == nil || !bigType.ListRules.Filtering.Filterable { + return nil, nil } - case *list_j5pb.FieldConstraint_Double: - if fieldOps.Double.Filtering != nil && fieldOps.Double.Filtering.Filterable { - for _, val := range fieldOps.Double.Filtering.DefaultFilters { - v, err := strconv.ParseFloat(val, 64) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) - } - - vals = append(vals, v) + vals := []any{} + for _, val := range bigType.ListRules.Filtering.DefaultFilters { + option := schema.OptionByName(val) + if option == nil { + return nil, fmt.Errorf("default filter value '%s' not found in enum '%s'", val, field.JSONName) } + + vals = append(vals, option.Name()) } - case *list_j5pb.FieldConstraint_Fixed32: - if fieldOps.Fixed32.Filtering != nil && fieldOps.Fixed32.Filtering.Filterable { - for _, val := range fieldOps.Fixed32.Filtering.DefaultFilters { - v, err := strconv.ParseUint(val, 10, 32) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) - } + return vals, nil - vals = append(vals, v) - } + case *j5schema.OneofField: + if bigType.ListRules == nil || bigType.ListRules.Filtering == nil || !bigType.ListRules.Filtering.Filterable { + return nil, nil } - case *list_j5pb.FieldConstraint_Fixed64: - if fieldOps.Fixed64.Filtering != nil && fieldOps.Fixed64.Filtering.Filterable { - for _, val := range fieldOps.Fixed64.Filtering.DefaultFilters { - v, err := strconv.ParseUint(val, 10, 64) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) - } - - vals = append(vals, v) - } + vals := []any{} + for _, val := range bigType.ListRules.Filtering.DefaultFilters { + // The value is the name of the set field + vals = append(vals, val) } - case *list_j5pb.FieldConstraint_Int32: - if fieldOps.Int32.Filtering != nil && fieldOps.Int32.Filtering.Filterable { - for _, val := range fieldOps.Int32.Filtering.DefaultFilters { - v, err := strconv.ParseInt(val, 10, 32) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) - } + return vals, nil - vals = append(vals, v) - } - } + case *j5schema.ScalarSchema: + j5Field := field.Schema.ToJ5Field() - case *list_j5pb.FieldConstraint_Int64: - if fieldOps.Int64.Filtering != nil && fieldOps.Int64.Filtering.Filterable { - for _, val := range fieldOps.Int64.Filtering.DefaultFilters { - v, err := strconv.ParseInt(val, 10, 64) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) - } + switch st := j5Field.Type.(type) { + case *schema_j5pb.Field_Float: - vals = append(vals, v) + if st.Float.ListRules == nil || st.Float.ListRules.Filtering == nil || !st.Float.ListRules.Filtering.Filterable { + return nil, nil } - } - - case *list_j5pb.FieldConstraint_Sfixed32: - if fieldOps.Sfixed32.Filtering != nil && fieldOps.Sfixed32.Filtering.Filterable { - for _, val := range fieldOps.Sfixed32.Filtering.DefaultFilters { - v, err := strconv.ParseInt(val, 10, 32) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) - } - vals = append(vals, v) + var size int + switch st.Float.Format { + case schema_j5pb.FloatField_FORMAT_FLOAT32: + size = 32 + case schema_j5pb.FloatField_FORMAT_FLOAT64: + size = 64 + default: + return nil, fmt.Errorf("unknown float format for default filter (%s): %s", field.JSONName, st.Float.Format) } - } - case *list_j5pb.FieldConstraint_Sfixed64: - if fieldOps.Sfixed64.Filtering != nil && fieldOps.Sfixed64.Filtering.Filterable { - for _, val := range fieldOps.Sfixed64.Filtering.DefaultFilters { - v, err := strconv.ParseInt(val, 10, 64) + vals := []any{} + for _, val := range st.Float.ListRules.Filtering.DefaultFilters { + + v, err := strconv.ParseFloat(val, size) if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) + return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) } vals = append(vals, v) } - } - case *list_j5pb.FieldConstraint_Sint32: - if fieldOps.Sint32.Filtering != nil && fieldOps.Sint32.Filtering.Filterable { - for _, val := range fieldOps.Sint32.Filtering.DefaultFilters { - v, err := strconv.ParseInt(val, 10, 32) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) - } + return vals, nil - vals = append(vals, v) + case *schema_j5pb.Field_Integer: + if st.Integer.ListRules == nil || st.Integer.ListRules.Filtering == nil || !st.Integer.ListRules.Filtering.Filterable { + return nil, nil } - } - case *list_j5pb.FieldConstraint_Sint64: - if fieldOps.Sint64.Filtering != nil && fieldOps.Sint64.Filtering.Filterable { - for _, val := range fieldOps.Sint64.Filtering.DefaultFilters { - v, err := strconv.ParseInt(val, 10, 64) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) - } + vals := []any{} + for _, val := range st.Integer.ListRules.Filtering.DefaultFilters { + var v any + var err error - vals = append(vals, v) - } - } + switch st.Integer.Format { + case schema_j5pb.IntegerField_FORMAT_INT32: + v, err = strconv.ParseInt(val, 10, 32) + if err != nil { + return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) + } - case *list_j5pb.FieldConstraint_Uint32: - if fieldOps.Uint32.Filtering != nil && fieldOps.Uint32.Filtering.Filterable { - for _, val := range fieldOps.Uint32.Filtering.DefaultFilters { - v, err := strconv.ParseUint(val, 10, 32) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) - } + case schema_j5pb.IntegerField_FORMAT_INT64: + v, err = strconv.ParseInt(val, 10, 64) + if err != nil { + return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) + } - vals = append(vals, v) - } - } + case schema_j5pb.IntegerField_FORMAT_UINT32: + v, err = strconv.ParseUint(val, 10, 32) + if err != nil { + return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) + } - case *list_j5pb.FieldConstraint_Uint64: - if fieldOps.Uint64.Filtering != nil && fieldOps.Uint64.Filtering.Filterable { - for _, val := range fieldOps.Uint64.Filtering.DefaultFilters { - v, err := strconv.ParseUint(val, 10, 64) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) + case schema_j5pb.IntegerField_FORMAT_UINT64: + v, err = strconv.ParseUint(val, 10, 64) + if err != nil { + return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) + } + + default: + return nil, fmt.Errorf("unknown integer format for default filter (%s): %s", field.JSONName, st.Integer.Format) } vals = append(vals, v) + } - } + return vals, nil - case *list_j5pb.FieldConstraint_Bool: - if fieldOps.Bool.Filtering != nil && fieldOps.Bool.Filtering.Filterable { - for _, val := range fieldOps.Bool.Filtering.DefaultFilters { + case *schema_j5pb.Field_Bool: + if st.Bool.ListRules == nil || st.Bool.ListRules.Filtering == nil || !st.Bool.ListRules.Filtering.Filterable { + return nil, nil + } + vals := []any{} + for _, val := range st.Bool.ListRules.Filtering.DefaultFilters { v, err := strconv.ParseBool(val) if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) + return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) } vals = append(vals, v) } - } + return vals, nil - case *list_j5pb.FieldConstraint_String_: - switch fieldOps := fieldOps.String_.WellKnown.(type) { - case *list_j5pb.StringRules_Date: - if fieldOps.Date.Filtering != nil && fieldOps.Date.Filtering.Filterable { - for _, val := range fieldOps.Date.Filtering.DefaultFilters { - if !dateRegex.MatchString(val) { - return nil, fmt.Errorf("invalid date format for default filter (%s): %s", field.JSONName(), val) - } + case *schema_j5pb.Field_Key: - // TODO: change to using ranges for date to handle whole - // year, whole month, whole day - vals = append(vals, val) - } + if st.Key.ListRules == nil || st.Key.ListRules.Filtering == nil || !st.Key.ListRules.Filtering.Filterable { + return nil, nil } - case *list_j5pb.StringRules_ForeignKey: - switch fieldOps := fieldOps.ForeignKey.Type.(type) { - case *list_j5pb.ForeignKeyRules_UniqueString: - if fieldOps.UniqueString.Filtering != nil && fieldOps.UniqueString.Filtering.Filterable { - for _, val := range fieldOps.UniqueString.Filtering.DefaultFilters { - vals = append(vals, val) - } - } - - case *list_j5pb.ForeignKeyRules_Id62: - if fieldOps.Id62.Filtering != nil && fieldOps.Id62.Filtering.Filterable { - for _, val := range fieldOps.Id62.Filtering.DefaultFilters { - vals = append(vals, val) - } - } - - case *list_j5pb.ForeignKeyRules_Uuid: - if fieldOps.Uuid.Filtering != nil && fieldOps.Uuid.Filtering.Filterable { - for _, val := range fieldOps.Uuid.Filtering.DefaultFilters { - _, err := uuid.Parse(val) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) - } - - vals = append(vals, val) - } - } - + vals := []any{} + for _, val := range st.Key.ListRules.Filtering.DefaultFilters { + vals = append(vals, val) } - } + return vals, nil - case *list_j5pb.FieldConstraint_Enum: - if fieldOps.Enum.Filtering != nil && fieldOps.Enum.Filtering.Filterable { - for _, val := range fieldOps.Enum.Filtering.DefaultFilters { - vals = append(vals, val) + case *schema_j5pb.Field_Timestamp: + if st.Timestamp.ListRules == nil || st.Timestamp.ListRules.Filtering == nil || !st.Timestamp.ListRules.Filtering.Filterable { + return nil, nil } - } - case *list_j5pb.FieldConstraint_Timestamp: - if fieldOps.Timestamp.Filtering != nil && fieldOps.Timestamp.Filtering.Filterable { - for _, val := range fieldOps.Timestamp.Filtering.DefaultFilters { + vals := []any{} + for _, val := range st.Timestamp.ListRules.Filtering.DefaultFilters { t, err := time.Parse(time.RFC3339, val) if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) + return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) } - vals = append(vals, timestamppb.New(t)) } - } + return vals, nil - case *list_j5pb.FieldConstraint_Date: - if fieldOps.Date.Filtering != nil && fieldOps.Date.Filtering.Filterable { - for _, val := range fieldOps.Date.Filtering.DefaultFilters { + case *schema_j5pb.Field_Date: + if st.Date.ListRules == nil || st.Date.ListRules.Filtering == nil || !st.Date.ListRules.Filtering.Filterable { + return nil, nil + } + vals := []any{} + for _, val := range st.Date.ListRules.Filtering.DefaultFilters { t, err := time.Parse(time.DateOnly, val) if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName(), err) + return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) } - vals = append(vals, t) } - } + return vals, nil + case *schema_j5pb.Field_String_: + // no filters definable + return nil, nil - case *list_j5pb.FieldConstraint_Oneof: - if fieldOps.Oneof.Filtering != nil && fieldOps.Oneof.Filtering.Filterable { - for _, val := range fieldOps.Oneof.Filtering.DefaultFilters { - vals = append(vals, val) + case *schema_j5pb.Field_Decimal: + if st.Decimal.ListRules == nil || st.Decimal.ListRules.Filtering == nil || !st.Decimal.ListRules.Filtering.Filterable { + return nil, nil } - } + vals := []any{} + for _, val := range st.Decimal.ListRules.Filtering.DefaultFilters { + v, err := decimal.NewFromString(val) + if err != nil { + return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) + } + vals = append(vals, v) + } + return vals, nil + + default: + return nil, fmt.Errorf("unknown scalar type for default filter (%s): %T", field.JSONName, st) + } default: - return nil, fmt.Errorf("unknown field type for filter rules %T", fieldOps) + return nil, fmt.Errorf("unknown field type for filter rules %T", bigType) } - return vals, nil } -func buildDefaultFilters(columnName string, message protoreflect.MessageDescriptor) ([]filterSpec, error) { +func buildDefaultFilters(columnName string, message *j5schema.ObjectSchema) ([]filterSpec, error) { var filters []filterSpec err := pgstore.WalkPathNodes(message, func(path pgstore.Path) error { field := path.LeafField() if field == nil { - oneof := path.LeafOneofWrapper() - if oneof == nil { - return nil - } - field = oneof + return nil } vals, err := filtersForField(field) @@ -321,7 +257,7 @@ func (ll *Lister[REQ, RES]) buildDynamicFilter(tableAlias string, filters []*lis switch filters[i].GetType().(type) { case *list_j5pb.Filter_Field: pathSpec := pgstore.ParseJSONPathSpec(filters[i].GetField().GetName()) - spec, err := pgstore.NewJSONPath(ll.arrayField.Message(), pathSpec) + spec, err := pgstore.NewJSONPath(ll.arrayObject, pathSpec) if err != nil { return nil, fmt.Errorf("dynamic filter: find field: %w", err) } @@ -331,20 +267,9 @@ func (ll *Lister[REQ, RES]) buildDynamicFilter(tableAlias string, filters []*lis RootColumn: ll.dataColumn, } - var o sq.Sqlizer - switch leaf := spec.Leaf().(type) { - case protoreflect.OneofDescriptor: - o, err = ll.buildDynamicFilterOneof(tableAlias, biggerSpec, filters[i]) - if err != nil { - return nil, fmt.Errorf("dynamic filter: build oneof: %w", err) - } - case protoreflect.FieldDescriptor: - o, err = ll.buildDynamicFilterField(tableAlias, biggerSpec, filters[i]) - if err != nil { - return nil, fmt.Errorf("dynamic filter: build field: %w", err) - } - default: - return nil, fmt.Errorf("unknown leaf type %T", leaf) + o, err := ll.buildDynamicFilterField(tableAlias, biggerSpec, filters[i]) + if err != nil { + return nil, fmt.Errorf("dynamic filter: build field: %w", err) } out = append(out, o) @@ -379,21 +304,9 @@ func (ll *Lister[REQ, RES]) buildDynamicFilterField(tableAlias string, spec *pgs return nil, fmt.Errorf("dynamic filter: field is nil") } - leafField := spec.Path.LeafField() - switch ft := filter.GetField().GetType().Type.(type) { case *list_j5pb.FieldType_Value: val := ft.Value - if leafField.Kind() == protoreflect.EnumKind { - name := strings.ToTitle(val) - prefix := strings.TrimSuffix(string(leafField.Enum().Values().Get(0).Name()), "_UNSPECIFIED") - - if !strings.HasPrefix(val, prefix) { - name = prefix + "_" + name - } - - val = name - } out = sq.And{sq.Expr( fmt.Sprintf("jsonb_path_query_array(%s.%s, '%s') @> ?", @@ -445,7 +358,7 @@ func (ll *Lister[REQ, RES]) buildDynamicFilterOneof(tableAlias string, ospec *pg return out, nil } -func validateQueryRequestFilters(message protoreflect.MessageDescriptor, filters []*list_j5pb.Filter) error { +func validateQueryRequestFilters(message *j5schema.ObjectSchema, filters []*list_j5pb.Filter) error { for i := range filters { switch filters[i].GetType().(type) { case *list_j5pb.Filter_Field: @@ -466,7 +379,7 @@ func validateFiltersAnnotations(_ protoreflect.FieldDescriptors) error { return nil } -func validateQueryRequestFilterField(message protoreflect.MessageDescriptor, filterField *list_j5pb.Field) error { +func validateQueryRequestFilterField(message *j5schema.ObjectSchema, filterField *list_j5pb.Field) error { jsonPath := pgstore.ParseJSONPathSpec(filterField.GetName()) spec, err := pgstore.NewJSONPath(message, jsonPath) @@ -474,352 +387,212 @@ func validateQueryRequestFilterField(message protoreflect.MessageDescriptor, fil return fmt.Errorf("find field: %w", err) } - // validate the fields are annotated correctly for the request query - // and the values are valid for the field - - switch leaf := spec.Leaf().(type) { - case protoreflect.OneofDescriptor: - filterable := false - - filterOpts := proto.GetExtension(leaf.Options().(*descriptorpb.OneofOptions), list_j5pb.E_Oneof).(*list_j5pb.OneofRules) - - if filterOpts == nil { - - fmt.Printf("No Filter Opts %s\n", filterField.GetName()) - wrapperField := spec.LeafOneofWrapper() - if wrapperField != nil { - fmt.Printf("WWWW %s\n", wrapperField.Name()) - fieldConstraint := proto.GetExtension(wrapperField.Options().(*descriptorpb.FieldOptions), list_j5pb.E_Field).(*list_j5pb.FieldConstraint) - if fieldConstraint != nil { - oneof := fieldConstraint.GetOneof() - if oneof != nil && oneof.Filtering != nil { - filterOpts = oneof - } - } - } - } - - if filterOpts != nil { - filterable = filterOpts.GetFiltering().Filterable - - if filterable { - switch filterField.Type.Type.(type) { - case *list_j5pb.FieldType_Value: - val := filterField.Type.GetValue() - - found := false - for i := range leaf.Fields().Len() { - f := leaf.Fields().Get(i) - if strings.EqualFold(string(f.Name()), val) { - found = true - break - } - } + fieldSpec := spec.LeafField() + if !schemaIsFilterable(fieldSpec) { + return fmt.Errorf("requested filter field '%s' is not filterable", filterField.Name) + } - if !found { - return fmt.Errorf("filter value '%s' is not found in oneof '%s'", val, filterField.Name) - } - case *list_j5pb.FieldType_Range: - return fmt.Errorf("oneofs cannot be filtered by range") - } - } + switch filterField.Type.Type.(type) { + case *list_j5pb.FieldType_Value: + err := validateFilterFieldValue(fieldSpec, filterField.Type.GetValue()) + if err != nil { + return fmt.Errorf("filter value: %w", err) } - - if !filterable { - return fmt.Errorf("requested filter field '%s' is not filterable", filterField.Name) + case *list_j5pb.FieldType_Range: + err := validateFilterFieldValue(fieldSpec, filterField.Type.GetRange().GetMin()) + if err != nil { + return fmt.Errorf("filter min value: %w", err) } - return nil - case protoreflect.FieldDescriptor: - - filterOpts, ok := proto.GetExtension(leaf.Options().(*descriptorpb.FieldOptions), list_j5pb.E_Field).(*list_j5pb.FieldConstraint) - if !ok { - return fmt.Errorf("requested filter field '%s' does not have any filterable constraints defined", filterField.Name) + err = validateFilterFieldValue(fieldSpec, filterField.Type.GetRange().GetMax()) + if err != nil { + return fmt.Errorf("filter max value: %w", err) } + } - filterable := false - - if filterOpts != nil { - switch leaf.Kind() { - case protoreflect.DoubleKind: - if filterOpts.GetDouble().Filtering != nil { - filterable = filterOpts.GetDouble().GetFiltering().Filterable - } - - case protoreflect.Fixed32Kind: - if filterOpts.GetFixed32().Filtering != nil { - filterable = filterOpts.GetFixed32().GetFiltering().Filterable - } - - case protoreflect.Fixed64Kind: - if filterOpts.GetFixed64().Filtering != nil { - filterable = filterOpts.GetFixed64().GetFiltering().Filterable - } - - case protoreflect.FloatKind: - if filterOpts.GetFloat().Filtering != nil { - filterable = filterOpts.GetFloat().GetFiltering().Filterable - } - case protoreflect.Int32Kind: - if filterOpts.GetInt32().Filtering != nil { - filterable = filterOpts.GetInt32().GetFiltering().Filterable - } - - case protoreflect.Int64Kind: - if filterOpts.GetInt64().Filtering != nil { - filterable = filterOpts.GetInt64().GetFiltering().Filterable - } - - case protoreflect.Sfixed32Kind: - if filterOpts.GetSfixed32().Filtering != nil { - filterable = filterOpts.GetSfixed32().GetFiltering().Filterable - } - - case protoreflect.Sfixed64Kind: - if filterOpts.GetSfixed64().Filtering != nil { - filterable = filterOpts.GetSfixed64().GetFiltering().Filterable - } - - case protoreflect.Sint32Kind: - if filterOpts.GetSint32().Filtering != nil { - filterable = filterOpts.GetSint32().GetFiltering().Filterable - } - - case protoreflect.Sint64Kind: - if filterOpts.GetSint64().Filtering != nil { - filterable = filterOpts.GetSint64().GetFiltering().Filterable - } - - case protoreflect.Uint32Kind: - if filterOpts.GetUint32().Filtering != nil { - filterable = filterOpts.GetUint32().GetFiltering().Filterable - } - - case protoreflect.Uint64Kind: - if filterOpts.GetUint64().Filtering != nil { - filterable = filterOpts.GetUint64().GetFiltering().Filterable - } - - case protoreflect.BoolKind: - if filterOpts.GetBool().Filtering != nil { - filterable = filterOpts.GetBool().GetFiltering().Filterable - } - - case protoreflect.EnumKind: - if filterOpts.GetEnum().Filtering != nil { - filterable = filterOpts.GetEnum().GetFiltering().Filterable - } - - case protoreflect.StringKind: - switch filterOpts.GetString_().WellKnown.(type) { - case *list_j5pb.StringRules_Date: - if filterOpts.GetString_().GetDate().Filtering != nil { - filterable = filterOpts.GetString_().GetDate().Filtering.Filterable - } - - case *list_j5pb.StringRules_ForeignKey: - switch filterOpts.GetString_().GetForeignKey().GetType().(type) { - case *list_j5pb.ForeignKeyRules_UniqueString: - if filterOpts.GetString_().GetForeignKey().GetUniqueString().Filtering != nil { - filterable = filterOpts.GetString_().GetForeignKey().GetUniqueString().Filtering.Filterable - } - - case *list_j5pb.ForeignKeyRules_Id62: - if filterOpts.GetString_().GetForeignKey().GetId62().Filtering != nil { - filterable = filterOpts.GetString_().GetForeignKey().GetId62().Filtering.Filterable - } - - case *list_j5pb.ForeignKeyRules_Uuid: - if filterOpts.GetString_().GetForeignKey().GetUuid().Filtering != nil { - filterable = filterOpts.GetString_().GetForeignKey().GetUuid().Filtering.Filterable - } - - } - } - case protoreflect.MessageKind: - if leaf.Message().FullName() == "google.protobuf.Timestamp" && filterOpts.GetTimestamp().Filtering != nil { - filterable = filterOpts.GetTimestamp().GetFiltering().Filterable - } - } - - if filterable { - switch filterField.Type.Type.(type) { - case *list_j5pb.FieldType_Value: - err := validateFilterFieldValue(filterOpts, leaf, filterField.Type.GetValue()) - if err != nil { - return fmt.Errorf("filter value: %w", err) - } - case *list_j5pb.FieldType_Range: - err := validateFilterFieldValue(filterOpts, leaf, filterField.Type.GetRange().GetMin()) - if err != nil { - return fmt.Errorf("filter min value: %w", err) - } - - err = validateFilterFieldValue(filterOpts, leaf, filterField.Type.GetRange().GetMax()) - if err != nil { - return fmt.Errorf("filter max value: %w", err) - } - } - } - } + return nil +} - if !filterable { - return fmt.Errorf("requested filter field '%s' is not filterable", filterField.Name) +func schemaIsFilterable(prop *j5schema.ObjectProperty) bool { + + switch bigSchema := prop.Schema.(type) { + case *j5schema.EnumField: + return bigSchema.ListRules != nil && bigSchema.ListRules.Filtering != nil && bigSchema.ListRules.Filtering.Filterable + case *j5schema.OneofField: + return bigSchema.ListRules != nil && bigSchema.ListRules.Filtering != nil && bigSchema.ListRules.Filtering.Filterable + case *j5schema.ScalarSchema: + j5Field := bigSchema.ToJ5Field() + switch st := j5Field.Type.(type) { + case *schema_j5pb.Field_Float: + return st.Float.ListRules != nil && st.Float.ListRules.Filtering != nil && st.Float.ListRules.Filtering.Filterable + case *schema_j5pb.Field_Integer: + return st.Integer.ListRules != nil && st.Integer.ListRules.Filtering != nil && st.Integer.ListRules.Filtering.Filterable + case *schema_j5pb.Field_Bool: + return st.Bool.ListRules != nil && st.Bool.ListRules.Filtering != nil && st.Bool.ListRules.Filtering.Filterable + case *schema_j5pb.Field_Key: + return st.Key.ListRules != nil && st.Key.ListRules.Filtering != nil && st.Key.ListRules.Filtering.Filterable + case *schema_j5pb.Field_Timestamp: + return st.Timestamp.ListRules != nil && st.Timestamp.ListRules.Filtering != nil && st.Timestamp.ListRules.Filtering.Filterable + case *schema_j5pb.Field_Date: + return st.Date.ListRules != nil && st.Date.ListRules.Filtering != nil && st.Date.ListRules.Filtering.Filterable + case *schema_j5pb.Field_Decimal: + return st.Decimal.ListRules != nil && st.Decimal.ListRules.Filtering != nil && st.Decimal.ListRules.Filtering.Filterable + default: + // If we don't know the type, we assume it's not filterable + return false } - - return nil default: - return fmt.Errorf("unknown leaf type %v", leaf) + // If we don't know the type, we assume it's not filterable + return false } } -func validateFilterFieldValue(filterOpts *list_j5pb.FieldConstraint, field protoreflect.FieldDescriptor, value string) error { +func validateFilterFieldValue(prop *j5schema.ObjectProperty, value string) error { if value == "" { return nil } - switch field.Kind() { - case protoreflect.DoubleKind: - if filterOpts.GetDouble().GetFiltering().Filterable { - _, err := strconv.ParseFloat(value, 64) - if err != nil { - return fmt.Errorf("parsing double: %w", err) - } + switch bigType := prop.Schema.(type) { + case *j5schema.EnumField: + schema := bigType.Schema() + if bigType.ListRules == nil || bigType.ListRules.Filtering == nil || !bigType.ListRules.Filtering.Filterable { + return fmt.Errorf("enum field '%s' is not filterable", prop.JSONName) } - case protoreflect.Fixed32Kind: - if filterOpts.GetFixed32().GetFiltering().Filterable { - _, err := strconv.ParseUint(value, 10, 32) - if err != nil { - return fmt.Errorf("parsing fixed32: %w", err) - } + if schema.OptionByName(value) == nil { + return fmt.Errorf("enum value '%s' is not a valid option for field '%s'", value, prop.JSONName) } - case protoreflect.Fixed64Kind: - if filterOpts.GetFixed64().GetFiltering().Filterable { - _, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return fmt.Errorf("parsing fixed64: %w", err) - } + case *j5schema.OneofField: + if bigType.ListRules == nil || bigType.ListRules.Filtering == nil || !bigType.ListRules.Filtering.Filterable { + return fmt.Errorf("oneof field '%s' is not filterable", prop.JSONName) } - case protoreflect.FloatKind: - if filterOpts.GetFloat().GetFiltering().Filterable { - _, err := strconv.ParseFloat(value, 32) - if err != nil { - return fmt.Errorf("parsing float: %w", err) - } + oneof := bigType.OneofSchema() + if oneof.Properties.ByJSONName(value) == nil { + return fmt.Errorf("oneof value '%s' is not a valid option for field '%s'", value, prop.JSONName) } - case protoreflect.Int32Kind: - if filterOpts.GetInt32().GetFiltering().Filterable { - _, err := strconv.ParseInt(value, 10, 32) - if err != nil { - return fmt.Errorf("parsing int32: %w", err) + + case *j5schema.ScalarSchema: + j5Field := bigType.ToJ5Field() + switch st := j5Field.Type.(type) { + case *schema_j5pb.Field_Float: + if st.Float.ListRules == nil || st.Float.ListRules.Filtering == nil || !st.Float.ListRules.Filtering.Filterable { + return fmt.Errorf("float field '%s' is not filterable", prop.JSONName) } - } - case protoreflect.Int64Kind: - if filterOpts.GetInt64().GetFiltering().Filterable { - _, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return fmt.Errorf("parsing int64: %w", err) + if st.Float.Format == schema_j5pb.FloatField_FORMAT_FLOAT32 { + _, err := strconv.ParseFloat(value, 32) + if err != nil { + return fmt.Errorf("parsing float32 value '%s' for field '%s': %w", value, prop.JSONName, err) + } + } else if st.Float.Format == schema_j5pb.FloatField_FORMAT_FLOAT64 { + _, err := strconv.ParseFloat(value, 64) + if err != nil { + return fmt.Errorf("parsing float64 value '%s' for field '%s': %w", value, prop.JSONName, err) + } + } else { + return fmt.Errorf("unknown float format '%s' for field '%s'", st.Float.Format, prop.JSONName) } - } - case protoreflect.Sfixed32Kind: - if filterOpts.GetSfixed32().GetFiltering().Filterable { - _, err := strconv.ParseInt(value, 10, 32) - if err != nil { - return fmt.Errorf("parsing sfixed32: %w", err) + case *schema_j5pb.Field_Integer: + if st.Integer.ListRules == nil || st.Integer.ListRules.Filtering == nil || !st.Integer.ListRules.Filtering.Filterable { + return fmt.Errorf("integer field '%s' is not filterable", prop.JSONName) } - } - case protoreflect.Sfixed64Kind: - if filterOpts.GetSfixed64().GetFiltering().Filterable { - _, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return fmt.Errorf("parsing sfixed64: %w", err) + switch st.Integer.Format { + case schema_j5pb.IntegerField_FORMAT_INT32: + _, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return fmt.Errorf("parsing int32 value '%s' for field '%s': %w", value, prop.JSONName, err) + } + case schema_j5pb.IntegerField_FORMAT_INT64: + _, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return fmt.Errorf("parsing int64 value '%s' for field '%s': %w", value, prop.JSONName, err) + } + case schema_j5pb.IntegerField_FORMAT_UINT32: + _, err := strconv.ParseUint(value, 10, 32) + if err != nil { + return fmt.Errorf("parsing uint32 value '%s' for field '%s': %w", value, prop.JSONName, err) + } + case schema_j5pb.IntegerField_FORMAT_UINT64: + _, err := strconv.ParseUint(value, 10, 64) + if err != nil { + return fmt.Errorf("parsing uint64 value '%s' for field '%s': %w", value, prop.JSONName, err) + } + default: + return fmt.Errorf("unknown integer format '%s' for field '%s'", st.Integer.Format, prop.JSONName) } - } - case protoreflect.Sint32Kind: - if filterOpts.GetSint32().GetFiltering().Filterable { - _, err := strconv.ParseInt(value, 10, 32) - if err != nil { - return fmt.Errorf("parsing sint32: %w", err) + case *schema_j5pb.Field_Bool: + if st.Bool.ListRules == nil || st.Bool.ListRules.Filtering == nil || !st.Bool.ListRules.Filtering.Filterable { + return fmt.Errorf("bool field '%s' is not filterable", prop.JSONName) } - } - case protoreflect.Sint64Kind: - if filterOpts.GetSint64().GetFiltering().Filterable { - _, err := strconv.ParseInt(value, 10, 64) + _, err := strconv.ParseBool(value) if err != nil { - return fmt.Errorf("parsing sint64: %w", err) + return fmt.Errorf("parsing bool value '%s' for field '%s': %w", value, prop.JSONName, err) } - } - case protoreflect.Uint32Kind: - if filterOpts.GetUint32().GetFiltering().Filterable { - _, err := strconv.ParseUint(value, 10, 32) - if err != nil { - return fmt.Errorf("parsing uint32: %w", err) + case *schema_j5pb.Field_Key: + if st.Key.ListRules == nil || st.Key.ListRules.Filtering == nil || !st.Key.ListRules.Filtering.Filterable { + return fmt.Errorf("key field '%s' is not filterable", prop.JSONName) } - } - case protoreflect.Uint64Kind: - if filterOpts.GetUint64().GetFiltering().Filterable { - _, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return fmt.Errorf("parsing uint64: %w", err) + switch ft := st.Key.Format.Type.(type) { + case *schema_j5pb.KeyFormat_Uuid: + if _, err := uuid.Parse(value); err != nil { + return fmt.Errorf("parsing uuid value '%s' for field '%s': %w", value, prop.JSONName, err) + } + + case *schema_j5pb.KeyFormat_Informal_: + // pass + case *schema_j5pb.KeyFormat_Id62: + if _, err := id62.Parse(value); err != nil { + return fmt.Errorf("parsing id62 value '%s' for field '%s': %w", value, prop.JSONName, err) + } + + case *schema_j5pb.KeyFormat_Custom_: + re, err := regexp.Compile(ft.Custom.Pattern) + if err != nil { + return fmt.Errorf("parsing custom key regex '%s' for field '%s': %w", ft.Custom.Pattern, prop.JSONName, err) + } + if !re.MatchString(value) { + return fmt.Errorf("value '%s' for field '%s' does not match custom key regex '%s'", value, prop.JSONName, ft.Custom.Pattern) + } + + default: + return fmt.Errorf("unknown key type for field '%s'", prop.JSONName) } - } - case protoreflect.BoolKind: - if filterOpts.GetBool().GetFiltering().Filterable { - _, err := strconv.ParseBool(value) - if err != nil { - return fmt.Errorf("parsing bool: %w", err) + case *schema_j5pb.Field_Timestamp: + if st.Timestamp.ListRules == nil || st.Timestamp.ListRules.Filtering == nil || !st.Timestamp.ListRules.Filtering.Filterable { + return fmt.Errorf("timestamp field '%s' is not filterable", prop.JSONName) } - } - case protoreflect.EnumKind: - if filterOpts.GetEnum().GetFiltering().Filterable { - name := strings.ToTitle(value) - prefix := strings.TrimSuffix(string(field.Enum().Values().Get(0).Name()), "_UNSPECIFIED") - - if !strings.HasPrefix(value, prefix) { - name = prefix + "_" + name + _, err := time.Parse(time.RFC3339, value) + if err != nil { + return fmt.Errorf("parsing timestamp value '%s' for field '%s': %w", value, prop.JSONName, err) } - eval := field.Enum().Values().ByName(protoreflect.Name(name)) - - if eval == nil { - return fmt.Errorf("enum value %s is not a valid enum value for field", value) + case *schema_j5pb.Field_Date: + if st.Date.ListRules == nil || st.Date.ListRules.Filtering == nil || !st.Date.ListRules.Filtering.Filterable { + return fmt.Errorf("date field '%s' is not filterable", prop.JSONName) } - } - case protoreflect.StringKind: - switch filterOpts.GetString_().WellKnown.(type) { - case *list_j5pb.StringRules_Date: - if filterOpts.GetString_().GetDate().Filtering.Filterable { - _, err := time.Parse("2006-01-02", value) + _, err := time.Parse("2006-01-02", value) + if err != nil { + _, err = time.Parse("2006-01", value) if err != nil { - _, err = time.Parse("2006-01", value) + _, err = time.Parse("2006", value) if err != nil { - _, err = time.Parse("2006", value) - if err != nil { - return fmt.Errorf("parsing date: %w", err) - } + return fmt.Errorf("parsing date value '%s' for field '%s': %w", value, prop.JSONName, err) } } } - case *list_j5pb.StringRules_ForeignKey: - switch filterOpts.GetString_().GetForeignKey().GetType().(type) { - case *list_j5pb.ForeignKeyRules_Uuid: - if filterOpts.GetString_().GetForeignKey().GetUuid().Filtering.Filterable { - _, err := uuid.Parse(value) - if err != nil { - return fmt.Errorf("parsing uuid: %w", err) - } - } + case *schema_j5pb.Field_Decimal: + if st.Decimal.ListRules == nil || st.Decimal.ListRules.Filtering == nil || !st.Decimal.ListRules.Filtering.Filterable { + return fmt.Errorf("decimal field '%s' is not filterable", prop.JSONName) } - } - case protoreflect.MessageKind: - if field.Message().FullName() == "google.protobuf.Timestamp" { - if filterOpts.GetTimestamp().GetFiltering().Filterable { - _, err := time.Parse(time.RFC3339, value) - if err != nil { - return fmt.Errorf("parsing timestamp: %w", err) - } + _, err := decimal.NewFromString(value) + if err != nil { + return fmt.Errorf("parsing decimal value '%s' for field '%s': %w", value, prop.JSONName, err) } + case *schema_j5pb.Field_String_: + return fmt.Errorf("string field '%s' is not filterable", prop.JSONName) + default: + return fmt.Errorf("unknown scalar type for field '%s': %T", prop.JSONName, st) } + default: + // If we don't know the type, we assume it's not filterable + return fmt.Errorf("unknown field type for filter validation: %T", bigType) } - return nil } diff --git a/pquery/getter.go b/pquery/getter.go index d05b850..de8da3a 100644 --- a/pquery/getter.go +++ b/pquery/getter.go @@ -7,24 +7,24 @@ import ( "fmt" "strings" - "buf.build/go/protovalidate" sq "github.com/elgris/sqrl" "github.com/lib/pq" + "github.com/pentops/j5/lib/j5codec" + "github.com/pentops/j5/lib/j5reflect" + "github.com/pentops/j5/lib/j5schema" + "github.com/pentops/j5/lib/j5validate" "github.com/pentops/protostate/internal/dbconvert" "github.com/pentops/sqrlx.go/sqrlx" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" ) type GetRequest interface { - proto.Message + j5reflect.Object } type GetResponse interface { - proto.Message + j5reflect.Object } type GetSpec[ @@ -38,7 +38,7 @@ type GetSpec[ PrimaryKey func(REQ) (map[string]any, error) - StateResponseField protoreflect.Name + StateResponseField string Join *GetJoinSpec } @@ -81,7 +81,7 @@ type GetJoinSpec struct { TableName string DataColumn string On JoinFields - FieldInParent protoreflect.Name + FieldInParent string } func (gc GetJoinSpec) validate() error { @@ -100,9 +100,9 @@ func (gc GetJoinSpec) validate() error { type Getter[ REQ GetRequest, - RES proto.Message, + RES j5reflect.Object, ] struct { - stateField protoreflect.FieldDescriptor + stateField *j5schema.ObjectProperty dataColumn string tableName string @@ -112,7 +112,7 @@ type Getter[ queryLogger QueryLogger - validator protovalidate.Validator + validator *j5validate.Validator join *getJoin } @@ -120,7 +120,7 @@ type Getter[ type getJoin struct { dataColumn string tableName string - fieldInParent protoreflect.FieldDescriptor // wraps the ListFooEventResponse type + fieldInParent *j5schema.ObjectProperty // wraps the ListFooEventResponse type on JoinFields } @@ -143,9 +143,9 @@ func NewGetter[ defaultState := false if spec.StateResponseField == "" { defaultState = true - spec.StateResponseField = protoreflect.Name("state") + spec.StateResponseField = "state" } - sc.stateField = resDesc.Fields().ByName(spec.StateResponseField) + sc.stateField = resDesc.Properties.ByJSONName(spec.StateResponseField) if sc.stateField == nil { if defaultState { return nil, fmt.Errorf("no 'state' field in proto message - did you mean to override StateResponseField?") @@ -163,12 +163,12 @@ func NewGetter[ return nil, fmt.Errorf("invalid join spec: %w", err) } - joinField := resDesc.Fields().ByName(protoreflect.Name(spec.Join.FieldInParent)) + joinField := resDesc.Properties.ByJSONName(spec.Join.FieldInParent) if joinField == nil { return nil, fmt.Errorf("field %s not found in response message", spec.Join.FieldInParent) } - if !joinField.IsList() { + if _, ok := joinField.Schema.(*j5schema.ArrayField); !ok { return nil, fmt.Errorf("field %s, in join spec, is not a list", spec.Join.FieldInParent) } @@ -180,11 +180,7 @@ func NewGetter[ } } - var err error - sc.validator, err = protovalidate.New() - if err != nil { - return nil, fmt.Errorf("failed to initialize validator: %w", err) - } + sc.validator = j5validate.Global return sc, nil } @@ -198,8 +194,6 @@ func (gc *Getter[REQ, RES]) Get(ctx context.Context, db Transactor, reqMsg REQ, as := newAliasSet() rootAlias := as.Next(gc.tableName) - resReflect := resMsg.ProtoReflect() - if err := gc.validator.Validate(reqMsg); err != nil { return err } @@ -316,24 +310,40 @@ func (gc *Getter[REQ, RES]) Get(ctx context.Context, db Transactor, reqMsg REQ, return status.Error(codes.NotFound, "not found") } - stateMsg := resReflect.NewField(gc.stateField) - if err := protojson.Unmarshal(foundJSON, stateMsg.Message().Interface()); err != nil { + stateMsg, err := resMsg.GetOrCreateValue(gc.stateField.JSONName) + if err != nil { + return err + } + + stateObj, ok := stateMsg.AsObject() + if !ok { + return fmt.Errorf("field %s in response message is not an object", gc.stateField.JSONName) + } + + err = j5codec.Global.JSONToReflect(foundJSON, stateObj) + if err != nil { return err } - resReflect.Set(gc.stateField, stateMsg) if gc.join != nil { - elementList := resReflect.Mutable(gc.join.fieldInParent).List() + element, err := resMsg.GetOrCreateValue(gc.join.fieldInParent.JSONName) + if err != nil { + return err + } + elementList, ok := element.AsArrayOfContainer() + if !ok { + return fmt.Errorf("field %s in response message is not an array of containers", gc.join.fieldInParent.JSONName) + } + for _, eventBytes := range joinedJSON { if eventBytes == "" { continue } - rowMessage := elementList.NewElement().Message() - if err := protojson.Unmarshal([]byte(eventBytes), rowMessage.Interface()); err != nil { - return fmt.Errorf("joined unmarshal: %w", err) + rowMessage, _ := elementList.NewContainerElement() + if err := j5codec.Global.JSONToReflect([]byte(eventBytes), rowMessage); err != nil { + return fmt.Errorf("joined json to reflect: %w", err) } - elementList.Append(protoreflect.ValueOf(rowMessage)) } } diff --git a/pquery/lister.go b/pquery/lister.go index dd59eee..02bad47 100644 --- a/pquery/lister.go +++ b/pquery/lister.go @@ -10,20 +10,19 @@ import ( "time" "unicode" - "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" - "buf.build/go/protovalidate" sq "github.com/elgris/sqrl" "github.com/elgris/sqrl/pg" "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" + "github.com/pentops/j5/lib/j5codec" + "github.com/pentops/j5/lib/j5reflect" + "github.com/pentops/j5/lib/j5schema" + "github.com/pentops/j5/lib/j5validate" "github.com/pentops/log.go/log" "github.com/pentops/protostate/internal/pgstore" "github.com/pentops/sqrlx.go/sqrlx" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/types/descriptorpb" "google.golang.org/protobuf/types/dynamicpb" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -31,11 +30,11 @@ import ( var dateRegex = regexp.MustCompile(`^\d{4}(-\d{2}(-\d{2})?)?$`) type ListRequest interface { - proto.Message + j5reflect.Object } type ListResponse interface { - proto.Message + j5reflect.Object } type TableSpec struct { @@ -53,14 +52,14 @@ type TableSpec struct { // ProtoField represents a field within a the root data. type ProtoField struct { // path from the root object to this field - pathInRoot pgstore.ProtoPathSpec + pathInRoot pgstore.JSONPathSpec // optional column name when the field is also stored as a scalar directly valueColumn *string } func NewProtoField(protoPath string, columnName *string) ProtoField { - pp := pgstore.ParseProtoPathSpec(protoPath) + pp := pgstore.ParseJSONPathSpec(protoPath) return ProtoField{ valueColumn: columnName, pathInRoot: pp, @@ -77,7 +76,7 @@ type Column struct { type ListSpec[REQ ListRequest, RES ListResponse] struct { TableSpec - RequestFilter func(REQ) (map[string]any, error) + RequestFilter func(j5reflect.Object) (map[string]any, error) } type QueryLogger func(sqrlx.Sqlizer) @@ -85,18 +84,20 @@ type QueryLogger func(sqrlx.Sqlizer) type ListReflectionSet struct { defaultPageSize uint64 - arrayField protoreflect.FieldDescriptor - pageResponseField protoreflect.FieldDescriptor - pageRequestField protoreflect.FieldDescriptor - queryRequestField protoreflect.FieldDescriptor + arrayField *j5schema.ObjectProperty + arrayObject *j5schema.ObjectSchema + + pageResponseField *j5schema.ObjectProperty + pageRequestField *j5schema.ObjectProperty + queryRequestField *j5schema.ObjectProperty defaultSortFields []sortSpec tieBreakerFields []sortSpec defaultFilterFields []filterSpec - RequestFilterFields []protoreflect.FieldDescriptor + RequestFilterFields []*j5schema.ObjectProperty - tsvColumnMap map[string]string + //tsvColumnMap map[string]string // TODO: This should be an array/map of columns to data types, allowing // multiple JSONB values, as well as cached field values direcrly on the @@ -104,40 +105,44 @@ type ListReflectionSet struct { dataColumn string } -func BuildListReflection(req protoreflect.MessageDescriptor, res protoreflect.MessageDescriptor, table TableSpec) (*ListReflectionSet, error) { +func BuildListReflection(req *j5schema.ObjectSchema, res *j5schema.ObjectSchema, table TableSpec) (*ListReflectionSet, error) { return buildListReflection(req, res, table) } -func buildListReflection(req protoreflect.MessageDescriptor, res protoreflect.MessageDescriptor, table TableSpec) (*ListReflectionSet, error) { +func buildListReflection(req *j5schema.ObjectSchema, res *j5schema.ObjectSchema, table TableSpec) (*ListReflectionSet, error) { var err error ll := ListReflectionSet{ defaultPageSize: uint64(20), dataColumn: table.DataColumn, } - fields := res.Fields() - for i := range fields.Len() { - field := fields.Get(i) - msg := field.Message() - if msg == nil { - return nil, fmt.Errorf("field %s is a '%s', but should be a message", field.Name(), field.Kind()) - } + for _, field := range res.Properties { + switch ft := field.Schema.(type) { + case *j5schema.ObjectField: - if msg.FullName() == "j5.list.v1.PageResponse" { - ll.pageResponseField = field - continue - } + if ft.FullName() == "j5.list.v1.PageResponse" { + ll.pageResponseField = field + continue + } + + case *j5schema.ArrayField: + + objectField, ok := ft.ItemSchema.(*j5schema.ObjectField) + if !ok { + return nil, fmt.Errorf("array field %s must be an object field, got %s", field.FullName(), ft.ItemSchema.FullName()) + } - if field.Cardinality() == protoreflect.Repeated { if ll.arrayField != nil { - return nil, fmt.Errorf("multiple repeated fields (%s and %s)", ll.arrayField.Name(), field.Name()) + return nil, fmt.Errorf("multiple repeated fields (%s and %s)", ll.arrayField.FullName(), field.FullName()) } ll.arrayField = field + ll.arrayObject = objectField.ObjectSchema() continue + } - return nil, fmt.Errorf("unknown field in response: '%s' of type %s", field.Name(), field.Kind()) + return nil, fmt.Errorf("unknown field in response: '%s' of type %s", field.FullName(), field.Schema.TypeName()) } if ll.arrayField == nil { @@ -148,62 +153,51 @@ func buildListReflection(req protoreflect.MessageDescriptor, res protoreflect.Me return nil, fmt.Errorf("no page field in response, %s must have a j5.list.v1.PageResponse", res.FullName()) } - err = validateListAnnotations(ll.arrayField.Message().Fields()) + err = validateListAnnotations(ll.arrayObject.Properties) if err != nil { - return nil, fmt.Errorf("validate list annotations on %s: %w", ll.arrayField.Message().FullName(), err) + return nil, fmt.Errorf("validate list annotations on %s: %w", ll.arrayField.FullName(), err) } - ll.defaultSortFields, err = buildDefaultSorts(ll.dataColumn, ll.arrayField.Message()) + ll.defaultSortFields, err = buildDefaultSorts(ll.dataColumn, ll.arrayObject) if err != nil { return nil, fmt.Errorf("default sorts: %w", err) } - ll.tieBreakerFields, err = buildTieBreakerFields(ll.dataColumn, req, ll.arrayField.Message(), table.FallbackSortColumns) + ll.tieBreakerFields, err = buildTieBreakerFields(ll.dataColumn, req, ll.arrayObject, table.FallbackSortColumns) if err != nil { return nil, fmt.Errorf("tie breaker fields: %w", err) } if len(ll.defaultSortFields) == 0 && len(ll.tieBreakerFields) == 0 { - return nil, fmt.Errorf("no default sort field found, %s must have at least one field annotated as default sort, or specify a tie breaker in %s", ll.arrayField.Message().FullName(), req.FullName()) + return nil, fmt.Errorf("no default sort field found, %s must have at least one field annotated as default sort, or specify a tie breaker in %s", ll.arrayField.FullName(), req.FullName()) } - f, err := buildDefaultFilters(ll.dataColumn, ll.arrayField.Message()) + f, err := buildDefaultFilters(ll.dataColumn, ll.arrayObject) if err != nil { return nil, fmt.Errorf("default filters: %w", err) } ll.defaultFilterFields = f - ll.tsvColumnMap = buildTsvColumnMap(ll.arrayField.Message()) + //ll.tsvColumnMap = buildTsvColumnMap(ll.arrayObject) - requestFields := req.Fields() - for i := range requestFields.Len() { - field := requestFields.Get(i) - msg := field.Message() - if msg != nil { - switch msg.FullName() { + for _, field := range req.Properties { + switch ft := field.Schema.(type) { + case *j5schema.ObjectField: + switch ft.FullName() { case "j5.list.v1.PageRequest": ll.pageRequestField = field - continue case "j5.list.v1.QueryRequest": ll.queryRequestField = field - continue - case "j5.types.date.v1.Date": - ll.RequestFilterFields = append(ll.RequestFilterFields, field) - continue default: - return nil, fmt.Errorf("unknown field in request: '%s' of type %s", field.Name(), field.Kind()) + return nil, fmt.Errorf("unknown field in request: '%s' of type %s", field.FullName(), field.Schema.TypeName()) } - } - // Assume this is a filter field - switch field.Kind() { - case protoreflect.StringKind: - ll.RequestFilterFields = append(ll.RequestFilterFields, field) - case protoreflect.BoolKind: + case *j5schema.ScalarSchema: ll.RequestFilterFields = append(ll.RequestFilterFields, field) + default: - return nil, fmt.Errorf("unsupported filter field in request: '%s' of type %s", field.Name(), field.Kind()) + return nil, fmt.Errorf("unsupported filter field in request: '%s' of type %s", field.FullName(), field.Schema.TypeName()) } } @@ -215,11 +209,10 @@ func buildListReflection(req protoreflect.MessageDescriptor, res protoreflect.Me return nil, fmt.Errorf("no query field in request, %s must have a j5.list.v1.QueryRequest", req.FullName()) } - arrayFieldOpt := ll.arrayField.Options().(*descriptorpb.FieldOptions) - validateOpt := proto.GetExtension(arrayFieldOpt, validate.E_Field).(*validate.FieldRules) - if repeated := validateOpt.GetRepeated(); repeated != nil { - if repeated.MaxItems != nil { - ll.defaultPageSize = *repeated.MaxItems + arraySchema := ll.arrayField.Schema.(*j5schema.ArrayField) + if arraySchema.Rules != nil { + if arraySchema.Rules.MaxItems != nil { + ll.defaultPageSize = *arraySchema.Rules.MaxItems } } @@ -236,9 +229,9 @@ type Lister[REQ ListRequest, RES ListResponse] struct { auth AuthProvider authJoin []*LeftJoin - requestFilter func(REQ) (map[string]any, error) + requestFilter func(j5reflect.Object) (map[string]any, error) - validator protovalidate.Validator + validator *j5validate.Validator } func NewLister[ @@ -261,10 +254,7 @@ func NewLister[ ll.requestFilter = spec.RequestFilter - ll.validator, err = protovalidate.New() - if err != nil { - return nil, fmt.Errorf("failed to initialize validator: %w", err) - } + ll.validator = j5validate.Global return ll, nil } @@ -273,14 +263,11 @@ func (ll *Lister[REQ, RES]) SetQueryLogger(logger QueryLogger) { ll.queryLogger = logger } -func (ll *Lister[REQ, RES]) List(ctx context.Context, db Transactor, reqMsg proto.Message, resMsg proto.Message) error { - if err := ll.validator.Validate(reqMsg); err != nil { - return fmt.Errorf("validating request %s: %w", reqMsg.ProtoReflect().Descriptor().FullName(), err) +func (ll *Lister[REQ, RES]) List(ctx context.Context, db Transactor, req, res j5reflect.Object) error { + if err := ll.validator.Validate(req); err != nil { + return fmt.Errorf("validating request %s: %w", req.SchemaName(), err) } - res := resMsg.ProtoReflect() - req := reqMsg.ProtoReflect() - pageSize, err := ll.getPageSize(req) if err != nil { return fmt.Errorf("get page size: %w", err) @@ -326,16 +313,22 @@ func (ll *Lister[REQ, RES]) List(ctx context.Context, db Transactor, reqMsg prot ll.queryLogger(selectQuery) } - list := res.Mutable(ll.arrayField).List() - res.Set(ll.arrayField, protoreflect.ValueOf(list)) + listField, err := res.GetOrCreateValue(ll.arrayField.JSONName) + if err != nil { + return err + } + list, ok := listField.AsArrayOfContainer() + if !ok { + return fmt.Errorf("field %s in response is not an array", ll.arrayField.FullName()) + } var nextToken string for idx, rowBytes := range jsonRows { - rowMessage := list.NewElement().Message() + rowMessage, _ := list.NewContainerElement() - err := protojson.Unmarshal(rowBytes, rowMessage.Interface()) + err := j5codec.Global.JSONToReflect(rowBytes, rowMessage) if err != nil { - return fmt.Errorf("unmarshal into %s from %s: %w", rowMessage.Descriptor().FullName(), string(rowBytes), err) + return fmt.Errorf("unmarshal into %s from %s: %w", rowMessage.SchemaName(), string(rowBytes), err) } if idx >= int(pageSize) { @@ -343,30 +336,40 @@ func (ll *Lister[REQ, RES]) List(ctx context.Context, db Transactor, reqMsg prot // The eventual solution will need to look at // the sorting and filtering of the query and either encode them // directly, or encode a subset of the message as required. - lastBytes, err := proto.Marshal(rowMessage.Interface()) - if err != nil { - return fmt.Errorf("marshalling final row: %w", err) - } - nextToken = base64.StdEncoding.EncodeToString(lastBytes) + nextToken = base64.StdEncoding.EncodeToString(rowBytes) break } - - list.Append(protoreflect.ValueOf(rowMessage)) } if nextToken != "" { - pageResponse := &list_j5pb.PageResponse{ - NextToken: &nextToken, + pageRes, err := res.GetOrCreateValue(ll.pageResponseField.JSONName) + if err != nil { + return err + } + + cont, ok := pageRes.AsContainer() + if !ok { + return fmt.Errorf("field %s in response is not a container", ll.pageResponseField.FullName()) } - res.Set(ll.pageResponseField, protoreflect.ValueOf(pageResponse.ProtoReflect())) + nextTokenVal, err := cont.GetOrCreateValue("nextToken") + if err != nil { + return fmt.Errorf("get nextToken field in response: %w", err) + } + nextTokenScalar, ok := nextTokenVal.AsScalar() + if !ok { + return fmt.Errorf("field %s in response is not a scalar", nextTokenVal.FullTypeName()) + } + if err := nextTokenScalar.SetGoValue(nextToken); err != nil { + return fmt.Errorf("set nextToken field in response: %w", err) + } } return nil } -func (ll *Lister[REQ, RES]) BuildQuery(ctx context.Context, req protoreflect.Message, res protoreflect.Message) (*sq.SelectBuilder, error) { +func (ll *Lister[REQ, RES]) BuildQuery(ctx context.Context, req j5reflect.Object, res j5reflect.Object) (*sq.SelectBuilder, error) { as := newAliasSet() tableAlias := as.Next(ll.tableName) @@ -378,7 +381,7 @@ func (ll *Lister[REQ, RES]) BuildQuery(ctx context.Context, req protoreflect.Mes filterFields := []sq.Sqlizer{} if ll.requestFilter != nil { - filter, err := ll.requestFilter(req.Interface().(REQ)) + filter, err := ll.requestFilter(req) if err != nil { return nil, err } @@ -629,19 +632,30 @@ func (ll *Lister[REQ, RES]) BuildQuery(ctx context.Context, req protoreflect.Mes return selectQuery, nil } -func (ll *Lister[REQ, RES]) getPageSize(req protoreflect.Message) (uint64, error) { - pageSize := ll.defaultPageSize +func (ll *Lister[REQ, RES]) getPageSize(req j5reflect.Object) (uint64, error) { + pageField, ok, err := req.GetField(ll.pageRequestField.JSONName, "pageSize") + if err != nil { + return 0, fmt.Errorf("get page request field %s: %w", ll.pageRequestField.JSONName, err) + } + if !ok { + return ll.defaultPageSize, nil + } - pageReq, ok := req.Get(ll.pageRequestField).Message().Interface().(*list_j5pb.PageRequest) - if ok && pageReq != nil && pageReq.PageSize != nil { - pageSize = uint64(*pageReq.PageSize) + val, ok := pageField.AsScalar() + if !ok { + return 0, fmt.Errorf("field %s in request is not a scalar", ll.pageRequestField.JSONName) + } + intVal, err := val.ToGoValue() + if err != nil { + return 0, fmt.Errorf("convert page request field %s to Go value: %w", ll.pageRequestField.JSONName, err) + } - if pageSize > ll.defaultPageSize { - return 0, fmt.Errorf("page size exceeds the maximum allowed size of %d", ll.defaultPageSize) - } + int64Val, ok := intVal.(int64) + if !ok { + return 0, fmt.Errorf("field %s in request is not an int64", ll.pageRequestField.JSONName) } - return pageSize, nil + return uint64(int64Val), nil } func camelToSnake(jsonName string) string { @@ -659,7 +673,7 @@ func camelToSnake(jsonName string) string { return out.String() } -func validateListAnnotations(fields protoreflect.FieldDescriptors) error { +func validateListAnnotations(fields []*j5schema.ObjectProperty) error { err := validateSortsAnnotations(fields) if err != nil { return fmt.Errorf("sort: %w", err) diff --git a/pquery/query.go b/pquery/query.go index 7306609..278ed87 100644 --- a/pquery/query.go +++ b/pquery/query.go @@ -4,9 +4,9 @@ import ( "context" "fmt" + "github.com/pentops/j5/lib/j5reflect" + "github.com/pentops/j5/lib/j5schema" "github.com/pentops/sqrlx.go/sqrlx" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" ) type aliasSet int @@ -43,16 +43,16 @@ type LeftJoin struct { } // MethodDescriptor is the RequestResponse pair in the gRPC Method -type methodDescriptor[REQ proto.Message, RES proto.Message] struct { - request protoreflect.MessageDescriptor - response protoreflect.MessageDescriptor +type methodDescriptor[REQ j5reflect.Object, RES j5reflect.Object] struct { + request *j5schema.ObjectSchema + response *j5schema.ObjectSchema } -func newMethodDescriptor[REQ proto.Message, RES proto.Message]() *methodDescriptor[REQ, RES] { +func newMethodDescriptor[REQ j5reflect.Object, RES j5reflect.Object]() *methodDescriptor[REQ, RES] { req := *new(REQ) res := *new(RES) return &methodDescriptor[REQ, RES]{ - request: req.ProtoReflect().Descriptor(), - response: res.ProtoReflect().Descriptor(), + request: req.ObjectSchema(), + response: res.ObjectSchema(), } } diff --git a/pquery/search.go b/pquery/search.go index 3602252..36ee732 100644 --- a/pquery/search.go +++ b/pquery/search.go @@ -3,28 +3,26 @@ package pquery import ( "fmt" + "maps" + sq "github.com/elgris/sqrl" "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" + "github.com/pentops/j5/lib/j5schema" "github.com/pentops/protostate/internal/pgstore" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/descriptorpb" - "maps" ) -func validateSearchesAnnotations(msg protoreflect.FieldDescriptors) error { - fields := make([]protoreflect.FieldDescriptor, msg.Len()) - for i := range msg.Len() { - fields[i] = msg.Get(i) - } - _, err := validateSearchesAnnotationsInner(fields) +func validateSearchesAnnotations(props []*j5schema.ObjectProperty) error { + _, err := validateSearchesAnnotationsInner(props) if err != nil { return fmt.Errorf("search validation: %w", err) } return nil } -func validateSearchesAnnotationsInner(fields []protoreflect.FieldDescriptor) (map[string]protoreflect.Name, error) { +func validateSearchesAnnotationsInner(fields []*j5schema.ObjectProperty) (map[string]protoreflect.Name, error) { // search annotations have a 'field_identifier' which specifies the database column name to use for the text-search-vector. // This function validates that the field_identifier is unique for the given field-set // In cases where the field is a message, it will recurse into the message to validate the field identifiers @@ -39,14 +37,13 @@ func validateSearchesAnnotationsInner(fields []protoreflect.FieldDescriptor) (ma // Oneof fields, however, can have the same field identifier on different // branches. - ids := make(map[string]protoreflect.Name) - oneofs := map[string][]protoreflect.FieldDescriptor{} + ids := make(map[string]string) + oneofs := map[string][]*j5schema.ObjectProperty{} for _, field := range fields { - if oneof := field.ContainingOneof(); oneof != nil { - name := string(oneof.Name()) - oneofs[name] = append(oneofs[name], field) + if oneof, ok := field.Schema.(*j5schema.OneofField); ok { + oneofs[field.JSONName] = append(oneofs[field.JSONName], oneof) continue } @@ -63,13 +60,13 @@ func validateSearchesAnnotationsInner(fields []protoreflect.FieldDescriptor) (ma // - with existing parent keys // - with other oneofs - combinedBranchIDs := make(map[string]protoreflect.Name) + combinedBranchIDs := make(map[string]string) for _, field := range oneofFields { // collect a new set of IDs for this branch as if it is the root of // the message. - branchIDs := make(map[string]protoreflect.Name) + branchIDs := make(map[string]string) err := validateSearchAnnotationsField(branchIDs, field) if err != nil { return nil, err @@ -99,7 +96,49 @@ func validateSearchesAnnotationsInner(fields []protoreflect.FieldDescriptor) (ma return ids, nil } -func validateSearchAnnotationsField(ids map[string]protoreflect.Name, field protoreflect.FieldDescriptor) error { +func validateSearchAnnotationsField(ids map[string]string, field *j5schema.ObjectProperty) error { + + switch bigType := field.Schema.(type) { + case *j5schema.ObjectField: + fields := bigType.ObjectSchema().Fields() + searchIdentifiers, err := validateSearchesAnnotationsInner(fields) + if err != nil { + return fmt.Errorf("object search validation: %w", err) + } + + for searchKey, usedIn := range searchIdentifiers { + if existing, ok := ids[searchKey]; ok { + return fmt.Errorf("field identifier '%s' is already used at %s", searchKey, existing) + } + ids[searchKey] = protoreflect.Name(fmt.Sprintf("%s.%s", field.JSONName, usedIn)) + } + + case *j5schema.OneofField: + fields := bigType.OneofSchema().Fields() + searchIdentifiers, err := validateSearchesAnnotationsInner(fields) + if err != nil { + return fmt.Errorf("oneof search validation: %w", err) + } + for searchKey, usedIn := range searchIdentifiers { + if existing, ok := ids[searchKey]; ok { + return fmt.Errorf("field identifier '%s' is already used at %s", searchKey, existing) + } + ids[searchKey] = protoreflect.Name(fmt.Sprintf("%s.%s", field.JSONName, usedIn)) + } + + case *j5schema.MapField: + items := bigType.MapSchema() + + case *j5schema.ArrayField: + + case *j5schema.ScalarField: + + schema := bigType + + } +} + +/* switch field.Kind() { case protoreflect.StringKind: @@ -148,10 +187,9 @@ func validateSearchAnnotationsField(ids map[string]protoreflect.Name, field prot } return nil +*/ -} - -func validateQueryRequestSearches(message protoreflect.MessageDescriptor, searches []*list_j5pb.Search) error { +func validateQueryRequestSearches(message *j5schema.ObjectSchema, searches []*list_j5pb.Search) error { for _, search := range searches { spec, err := pgstore.NewJSONPath(message, pgstore.ParseJSONPathSpec(search.GetField())) @@ -189,7 +227,7 @@ func validateQueryRequestSearches(message protoreflect.MessageDescriptor, search return nil } -func buildTsvColumnMap(message protoreflect.MessageDescriptor) map[string]string { +func buildTsvColumnMap(message *j5schema.ObjectSchema) map[string]string { out := make(map[string]string) for i := range message.Fields().Len() { diff --git a/pquery/sort.go b/pquery/sort.go index 9ce48fc..8ebb13a 100644 --- a/pquery/sort.go +++ b/pquery/sort.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" + "github.com/pentops/j5/lib/j5schema" "github.com/pentops/protostate/internal/pgstore" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" @@ -19,7 +20,7 @@ func (ss sortSpec) errorName() string { return ss.Path.JSONPathQuery() } -func buildTieBreakerFields(dataColumn string, req protoreflect.MessageDescriptor, arrayField protoreflect.MessageDescriptor, fallback []ProtoField) ([]sortSpec, error) { +func buildTieBreakerFields(dataColumn string, req *j5schema.ObjectSchema, arrayField *j5schema.ObjectSchema, fallback []ProtoField) ([]sortSpec, error) { listRequestAnnotation, ok := proto.GetExtension(req.Options().(*descriptorpb.MessageOptions), list_j5pb.E_ListRequest).(*list_j5pb.ListRequestMessage) if ok && listRequestAnnotation != nil && len(listRequestAnnotation.SortTiebreaker) > 0 { tieBreakerFields := make([]sortSpec, 0, len(listRequestAnnotation.SortTiebreaker)) @@ -66,7 +67,7 @@ func buildTieBreakerFields(dataColumn string, req protoreflect.MessageDescriptor return tieBreakerFields, nil } -func buildDefaultSorts(columnName string, message protoreflect.MessageDescriptor) ([]sortSpec, error) { +func buildDefaultSorts(columnName string, message *j5schema.ObjectSchema) ([]sortSpec, error) { var defaultSortFields []sortSpec err := pgstore.WalkPathNodes(message, func(path pgstore.Path) error { @@ -191,9 +192,8 @@ func (ll *Lister[REQ, RES]) buildDynamicSortSpec(sorts []*list_j5pb.Sort) ([]sor return results, nil } -func validateSortsAnnotations(fields protoreflect.FieldDescriptors) error { - for i := range fields.Len() { - field := fields.Get(i) +func validateSortsAnnotations(fields []*j5schema.ObjectProperty) error { + for _, field := range fields { if field.Kind() == protoreflect.MessageKind { subFields := field.Message().Fields() @@ -294,7 +294,7 @@ func isSortingAnnotated(opts *list_j5pb.FieldConstraint) bool { return annotated } -func validateQueryRequestSorts(message protoreflect.MessageDescriptor, sorts []*list_j5pb.Sort) error { +func validateQueryRequestSorts(message *j5schema.ObjectSchema, sorts []*list_j5pb.Sort) error { for _, sort := range sorts { pathSpec := pgstore.ParseJSONPathSpec(sort.Field) spec, err := pgstore.NewJSONPath(message, pathSpec) diff --git a/psm/builder.go b/psm/builder.go index cc0cc2c..24a5274 100644 --- a/psm/builder.go +++ b/psm/builder.go @@ -2,6 +2,7 @@ package psm import ( "context" + "fmt" "github.com/pentops/sqrlx.go/sqrlx" ) @@ -66,8 +67,14 @@ func (smc *StateMachineConfig[K, S, ST, SD, E, IE]) InitialStateFunc(cbFunc func func (smc *StateMachineConfig[K, S, ST, SD, E, IE]) apply() error { if smc.tableMap == nil { - state := (*new(S)).ProtoReflect().Descriptor() - event := (*new(E)).ProtoReflect().Descriptor() + state, ok := (*new(S)).J5Reflect().RootSchema() + if !ok { + return fmt.Errorf("invalid state type %T, must be a j5 root schema", new(S)) + } + event, ok := (*new(E)).J5Reflect().RootSchema() + if !ok { + return fmt.Errorf("invalid event type %T, must be a j5 root schema", new(E)) + } tableMap, err := tableMapFromStateAndEvent(state, event) if err != nil { return err @@ -94,8 +101,14 @@ func (smc *StateMachineConfig[K, S, ST, SD, E, IE]) BuildQueryTableSpec() (*Quer return nil, err } - state := (*new(S)).ProtoReflect().Descriptor() - event := (*new(E)).ProtoReflect().Descriptor() + state, ok := (*new(S)).J5Reflect().RootSchema() + if !ok { + return nil, fmt.Errorf("invalid state type %T, must be a j5 root schema", new(S)) + } + event, ok := (*new(E)).J5Reflect().RootSchema() + if !ok { + return nil, fmt.Errorf("invalid event type %T, must be a j5 root schema", new(E)) + } return &QueryTableSpec{ TableMap: *smc.tableMap, diff --git a/psm/gen_interfaces.go b/psm/gen_interfaces.go index 3064aeb..6e222d5 100644 --- a/psm/gen_interfaces.go +++ b/psm/gen_interfaces.go @@ -6,9 +6,9 @@ import ( "time" "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" + "github.com/pentops/j5/lib/j5reflect" "github.com/pentops/o5-messaging/o5msg" "github.com/pentops/sqrlx.go/sqrlx" - "google.golang.org/protobuf/proto" ) /* @@ -42,9 +42,14 @@ K, S, ST, E, and IE are set to one single type for the entire state machine SE is set to a single type for each transition. */ +type J5Message interface { + J5Reflect() j5reflect.Root +} + // IGenericProtoMessage is the base extensions shared by all message entities in the PSM generated code type IPSMMessage interface { - proto.Message + //proto.Message + J5Message PSMIsSet() bool } @@ -87,7 +92,8 @@ type IEvent[ SD IStateData, Inner any, ] interface { - proto.Message + //proto.Message + J5Message UnwrapPSMEvent() Inner SetPSMEvent(Inner) error PSMKeys() K @@ -137,7 +143,7 @@ type TransitionMutation[ func (f TransitionMutation[K, S, ST, SD, E, IE, SE]) runMutation(state S, event E) error { // nolint:unused // Used by genereted code. asType, ok := any(event.UnwrapPSMEvent()).(SE) if !ok { - name := event.ProtoReflect().Descriptor().FullName() + name := event.J5Reflect().SchemaName() return fmt.Errorf("unexpected event type in transition: %s [IE] does not match [SE] (%T)", name, new(SE)) } diff --git a/psm/state_query.go b/psm/state_query.go index a7b0e14..eee1231 100644 --- a/psm/state_query.go +++ b/psm/state_query.go @@ -5,16 +5,17 @@ import ( "fmt" "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" + "github.com/pentops/j5/lib/j5reflect" + "github.com/pentops/j5/lib/j5schema" "github.com/pentops/protostate/pquery" - "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" ) // QueryTableSpec the TableMap with descriptors for the messages, without using // generic parameters. type QueryTableSpec struct { - EventType protoreflect.MessageDescriptor - StateType protoreflect.MessageDescriptor + EventType *j5schema.ObjectSchema + StateType *j5schema.ObjectSchema TableMap } @@ -75,7 +76,7 @@ func (gc *StateQuerySet[ GetREQ, GetRES, ListREQ, ListRES, ListEventsREQ, ListEventsRES, -]) List(ctx context.Context, db Transactor, reqMsg proto.Message, resMsg proto.Message) error { +]) List(ctx context.Context, db Transactor, reqMsg *j5reflect.Object, resMsg *j5reflect.Object) error { return gc.MainLister.List(ctx, db, reqMsg, resMsg) } @@ -83,7 +84,7 @@ func (gc *StateQuerySet[ GetREQ, GetRES, ListREQ, ListRES, ListEventsREQ, ListEventsRES, -]) ListEvents(ctx context.Context, db Transactor, reqMsg proto.Message, resMsg proto.Message) error { +]) ListEvents(ctx context.Context, db Transactor, reqMsg *j5reflect.Object, resMsg *j5reflect.Object) error { return gc.EventLister.List(ctx, db, reqMsg, resMsg) } diff --git a/psm/table_map.go b/psm/table_map.go index 696a652..b12f3ba 100644 --- a/psm/table_map.go +++ b/psm/table_map.go @@ -117,7 +117,7 @@ func safeTableName(name string) string { }, name) } -func BuildQueryTableSpec(stateMessage, eventMessage protoreflect.MessageDescriptor) (QueryTableSpec, error) { +func BuildQueryTableSpec(stateMessage, eventMessage *j5schema.ObjectSchema) (QueryTableSpec, error) { tableMap, err := tableMapFromStateAndEvent(stateMessage, eventMessage) if err != nil { return QueryTableSpec{}, err @@ -213,14 +213,12 @@ func keyFieldColumn(field *j5schema.ObjectProperty, desc protoreflect.FieldDescr return kc, nil } -func tableMapFromStateAndEvent(stateMessage, eventMessage protoreflect.MessageDescriptor) (*TableMap, error) { +func tableMapFromStateAndEvent(stateMessage, eventMessage *j5schema.ObjectSchema) (*TableMap, error) { var stateKeyField protoreflect.FieldDescriptor var keyMessage protoreflect.MessageDescriptor - fields := stateMessage.Fields() - for idx := range fields.Len() { - field := fields.Get(idx) + for _, field := range stateMessage.Properties { if field.Kind() != protoreflect.MessageKind { continue } @@ -239,9 +237,7 @@ func tableMapFromStateAndEvent(stateMessage, eventMessage protoreflect.MessageDe var eventKeysField protoreflect.FieldDescriptor - fields = eventMessage.Fields() - for idx := range fields.Len() { - field := fields.Get(idx) + for _, field := range eventMessage.Properties { if field.Kind() != protoreflect.MessageKind { continue } From 92cffbc9ae42e460f7379cacdcc000800b63303e Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Thu, 17 Jul 2025 16:19:36 -0700 Subject: [PATCH 02/12] More WIP --- internal/pgstore/path.go | 58 +++++++-- pquery/filter.go | 7 -- pquery/lister.go | 149 +++++++++++++++------- pquery/lister_test.go | 26 +++- pquery/search.go | 262 +++++++++++++++------------------------ pquery/search_test.go | 50 ++++++-- pquery/sort.go | 199 +++++++---------------------- 7 files changed, 371 insertions(+), 380 deletions(-) diff --git a/internal/pgstore/path.go b/internal/pgstore/path.go index 7172f7d..789efcc 100644 --- a/internal/pgstore/path.go +++ b/internal/pgstore/path.go @@ -49,7 +49,6 @@ func (nf *NestedField) Selector(inTable string) string { type pathNode struct { name string field *j5schema.ObjectProperty - //oneof protoreflect.OneofDescriptor } type Path struct { @@ -222,6 +221,48 @@ func (jp JSONPathSpec) String() string { } func NewJSONPath(rootMessage *j5schema.ObjectSchema, fieldPath JSONPathSpec) (*Path, error) { + return newPath(rootMessage, []string(fieldPath), clientPath) +} + +func NewOuterPath(rootMessage *j5schema.ObjectSchema, fieldPath JSONPathSpec) (*Path, error) { + return newPath(rootMessage, []string(fieldPath), outerPath) +} + +type pathType int + +const ( + clientPath pathType = iota // the JSON path from the client side, flattened fields are anonymous + outerPath // flattened fields included with their name +) + +type hasPropertySet interface { + AllProperties() j5schema.PropertySet + ClientProperties() j5schema.PropertySet + FullName() string +} + +func objectProperty(obj hasPropertySet, pathElem string, pt pathType) (*j5schema.ObjectProperty, error) { + switch pt { + case clientPath: + prop := obj.ClientProperties().ByJSONName(pathElem) + if prop == nil { + return nil, fmt.Errorf("property %s not found in object %s", pathElem, obj.FullName()) + } + return prop, nil + case outerPath: + prop := obj.AllProperties().ByJSONName(pathElem) + if prop == nil { + return nil, fmt.Errorf("property %s not found in object %s", pathElem, obj.FullName()) + } + return prop, nil + + default: + return nil, fmt.Errorf("unknown path type %d", pt) + } + +} + +func newPath(rootMessage *j5schema.ObjectSchema, fieldPath []string, pathType pathType) (*Path, error) { if len(fieldPath) == 0 { return nil, fmt.Errorf("fieldPath must have at least one element") @@ -243,10 +284,11 @@ func NewJSONPath(rootMessage *j5schema.ObjectSchema, fieldPath JSONPathSpec) (*P var node pathNode switch walkMessage := walkMessage.(type) { case *j5schema.ObjectSchema: - field := walkMessage.ClientProperties().ByJSONName(pathElem) - if field == nil { - return nil, fmt.Errorf("field %s not found in object %s", pathElem, walkMessage.FullName()) + field, err := objectProperty(walkMessage, pathElem, pathType) + if err != nil { + return nil, err } + node = pathNode{ name: pathElem, field: field, @@ -262,11 +304,11 @@ func NewJSONPath(rootMessage *j5schema.ObjectSchema, fieldPath JSONPathSpec) (*P break } - field := walkMessage.ClientProperties().ByJSONName(pathElem) - if field == nil { - - return nil, fmt.Errorf("field %s not found in oneof %s", pathElem, walkMessage.FullName()) + field, err := objectProperty(walkMessage, pathElem, pathType) + if err != nil { + return nil, err } + node = pathNode{ name: pathElem, field: field, diff --git a/pquery/filter.go b/pquery/filter.go index cc7d4f0..8f7aaca 100644 --- a/pquery/filter.go +++ b/pquery/filter.go @@ -15,7 +15,6 @@ import ( "github.com/pentops/j5/lib/j5schema" "github.com/pentops/protostate/internal/pgstore" "github.com/shopspring/decimal" - "google.golang.org/protobuf/reflect/protoreflect" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -373,12 +372,6 @@ func validateQueryRequestFilters(message *j5schema.ObjectSchema, filters []*list return nil } -func validateFiltersAnnotations(_ protoreflect.FieldDescriptors) error { - - // TODO: This existed pre refactor, check if it should do something. - return nil -} - func validateQueryRequestFilterField(message *j5schema.ObjectSchema, filterField *list_j5pb.Field) error { jsonPath := pgstore.ParseJSONPathSpec(filterField.GetName()) diff --git a/pquery/lister.go b/pquery/lister.go index 02bad47..89885db 100644 --- a/pquery/lister.go +++ b/pquery/lister.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "encoding/base64" + "encoding/json" "fmt" "regexp" "strings" @@ -76,7 +77,7 @@ type Column struct { type ListSpec[REQ ListRequest, RES ListResponse] struct { TableSpec - RequestFilter func(j5reflect.Object) (map[string]any, error) + RequestFilter func(REQ) (map[string]any, error) } type QueryLogger func(sqrlx.Sqlizer) @@ -97,7 +98,7 @@ type ListReflectionSet struct { defaultFilterFields []filterSpec RequestFilterFields []*j5schema.ObjectProperty - //tsvColumnMap map[string]string + tsvColumnMap map[string]string // map[JSON Path]PGColumName // TODO: This should be an array/map of columns to data types, allowing // multiple JSONB values, as well as cached field values direcrly on the @@ -120,7 +121,7 @@ func buildListReflection(req *j5schema.ObjectSchema, res *j5schema.ObjectSchema, switch ft := field.Schema.(type) { case *j5schema.ObjectField: - if ft.FullName() == "j5.list.v1.PageResponse" { + if ft.ObjectSchema().FullName() == "j5.list.v1.PageResponse" { ll.pageResponseField = field continue } @@ -153,7 +154,7 @@ func buildListReflection(req *j5schema.ObjectSchema, res *j5schema.ObjectSchema, return nil, fmt.Errorf("no page field in response, %s must have a j5.list.v1.PageResponse", res.FullName()) } - err = validateListAnnotations(ll.arrayObject.Properties) + err = validateListAnnotations(ll.arrayObject) if err != nil { return nil, fmt.Errorf("validate list annotations on %s: %w", ll.arrayField.FullName(), err) } @@ -179,12 +180,15 @@ func buildListReflection(req *j5schema.ObjectSchema, res *j5schema.ObjectSchema, ll.defaultFilterFields = f - //ll.tsvColumnMap = buildTsvColumnMap(ll.arrayObject) + ll.tsvColumnMap, err = buildTsvColumnMap(ll.arrayObject) + if err != nil { + return nil, fmt.Errorf("build tsv column map: %w", err) + } for _, field := range req.Properties { switch ft := field.Schema.(type) { case *j5schema.ObjectField: - switch ft.FullName() { + switch ft.ObjectSchema().FullName() { case "j5.list.v1.PageRequest": ll.pageRequestField = field case "j5.list.v1.QueryRequest": @@ -229,7 +233,7 @@ type Lister[REQ ListRequest, RES ListResponse] struct { auth AuthProvider authJoin []*LeftJoin - requestFilter func(j5reflect.Object) (map[string]any, error) + requestFilter func(REQ) (map[string]any, error) validator *j5validate.Validator } @@ -369,6 +373,64 @@ func (ll *Lister[REQ, RES]) List(ctx context.Context, db Transactor, req, res j5 return nil } +func encodePageToken(rowMessage j5reflect.Object, sortFields []sortSpec) (string, error) { + vals := map[string]any{} + for _, sortField := range sortFields { + fieldVal, ok, err := sortField.Path.GetValue(rowMessage) + if err != nil { + return "", fmt.Errorf("sort field %s: %w", sortField.errorName(), err) + } + if !ok { + vals[sortField.Path.IDPath()] = nil + continue + } + vals[sortField.Path.IDPath()] = fieldVal + + } + valJSON, err := json.Marshal(vals) + if err != nil { + return "", fmt.Errorf("marshal page token values: %w", err) + } + return base64.StdEncoding.EncodeToString(valJSON), nil +} + +func decodePageToken(token string, sortFields []sortSpec) (map[string]any, error) { + vals := map[string]any{} + jsonBytes, err := base64.StdEncoding.DecodeString(token) + if err != nil { + return nil, fmt.Errorf("decode page token: %w", err) + } + if err := json.Unmarshal(jsonBytes, &vals); err != nil { + return nil, fmt.Errorf("unmarshal page token: %w", err) + } + if len(vals) != len(sortFields) { + return nil, fmt.Errorf("page token has %d values, expected %d", len(vals), len(sortFields)) + } + + return vals, nil +} + +func fieldAs[T any](obj j5reflect.Object, path ...string) (val T, ok bool, err error) { + + endField, ok, err := obj.GetField(path...) + if err != nil || !ok { + return val, ok, err + } + + objField, ok := endField.AsObject() + if !ok { + return val, false, fmt.Errorf("field %s in %s not an object", path, obj.SchemaName()) + } + + val, ok = objField.ProtoReflect().Interface().(T) + if !ok { + return val, false, fmt.Errorf("field %s in %s not of type %T", path, obj.SchemaName(), (*T)(nil)) + } + + return val, true, nil + +} + func (ll *Lister[REQ, RES]) BuildQuery(ctx context.Context, req j5reflect.Object, res j5reflect.Object) (*sq.SelectBuilder, error) { as := newAliasSet() tableAlias := as.Next(ll.tableName) @@ -381,7 +443,12 @@ func (ll *Lister[REQ, RES]) BuildQuery(ctx context.Context, req j5reflect.Object filterFields := []sq.Sqlizer{} if ll.requestFilter != nil { - filter, err := ll.requestFilter(req) + reqVal, ok := req.ProtoReflect().Interface().(REQ) + if !ok { + return nil, fmt.Errorf("request is not of type %T", (*REQ)(nil)) + } + + filter, err := ll.requestFilter(reqVal) if err != nil { return nil, err } @@ -396,8 +463,11 @@ func (ll *Lister[REQ, RES]) BuildQuery(ctx context.Context, req j5reflect.Object } } - reqQuery, ok := req.Get(ll.queryRequestField).Message().Interface().(*list_j5pb.QueryRequest) - if ok && reqQuery != nil { + reqQuery, ok, err := fieldAs[*list_j5pb.QueryRequest](req, ll.queryRequestField.JSONName) + if err != nil { + return nil, err + } + if ok { if err := ll.validateQueryRequest(reqQuery); err != nil { return nil, status.Errorf(codes.InvalidArgument, "query validation: %s", err) } @@ -496,17 +566,11 @@ func (ll *Lister[REQ, RES]) BuildQuery(ctx context.Context, req j5reflect.Object selectQuery.Limit(pageSize + 1) - reqPage, ok := req.Get(ll.pageRequestField).Message().Interface().(*list_j5pb.PageRequest) - if ok && reqPage != nil && reqPage.GetToken() != "" { - rowMessage := dynamicpb.NewMessage(ll.arrayField.Message()) - - rowBytes, err := base64.StdEncoding.DecodeString(reqPage.GetToken()) + reqPage, ok, err := fieldAs[*list_j5pb.PageRequest](req, ll.pageRequestField.JSONName) + if ok && reqPage.GetToken() != "" { + pageFields, err := decodePageToken(reqPage.GetToken(), sortFields) if err != nil { - return nil, fmt.Errorf("decode token: %w", err) - } - - if err := proto.Unmarshal(rowBytes, rowMessage.Interface()); err != nil { - return nil, fmt.Errorf("unmarshal into %s from %s: %w", rowMessage.Descriptor().FullName(), string(rowBytes), err) + return nil, fmt.Errorf("decode page token: %w", err) } lhsFields := make([]string, 0, len(sortFields)) @@ -517,12 +581,11 @@ func (ll *Lister[REQ, RES]) BuildQuery(ctx context.Context, req j5reflect.Object rowSelecter := sortField.Selector(tableAlias) valuePlaceholder := "?" - fieldVal, err := sortField.Path.GetValue(rowMessage) - if err != nil { - return nil, fmt.Errorf("sort field %s: %w", sortField.errorName(), err) + dbVal, ok := pageFields[sortField.Path.IDPath()] + if !ok { + return nil, fmt.Errorf("page token missing value for sort field %s", sortField.errorName()) } - dbVal := fieldVal.Interface() switch subType := dbVal.(type) { case *dynamicpb.Message: name := subType.Descriptor().FullName() @@ -673,40 +736,38 @@ func camelToSnake(jsonName string) string { return out.String() } -func validateListAnnotations(fields []*j5schema.ObjectProperty) error { - err := validateSortsAnnotations(fields) - if err != nil { - return fmt.Errorf("sort: %w", err) - } - - err = validateFiltersAnnotations(fields) - if err != nil { - return fmt.Errorf("filter: %w", err) - } +func validateListAnnotations(object *j5schema.ObjectSchema) error { + /* + err := validateSortsAnnotations(object.Properties) + if err != nil { + return fmt.Errorf("sort: %w", err) + }*/ - err = validateSearchesAnnotations(fields) - if err != nil { - return fmt.Errorf("search: %w", err) - } + /* + err = validateSearchesAnnotations(object) + if err != nil { + return fmt.Errorf("search: %w", err) + }*/ return nil } func (ll *Lister[REQ, RES]) validateQueryRequest(query *list_j5pb.QueryRequest) error { - err := validateQueryRequestSorts(ll.arrayField.Message(), query.GetSorts()) + err := validateQueryRequestSorts(ll.arrayObject, query.GetSorts()) if err != nil { return fmt.Errorf("sort validation: %w", err) } - err = validateQueryRequestFilters(ll.arrayField.Message(), query.GetFilters()) + err = validateQueryRequestFilters(ll.arrayObject, query.GetFilters()) if err != nil { return fmt.Errorf("filter validation: %w", err) } - err = validateQueryRequestSearches(ll.arrayField.Message(), query.GetSearches()) - if err != nil { - return fmt.Errorf("search validation: %w", err) - } + /* + err = validateQueryRequestSearches(ll.arrayObject, query.GetSearches()) + if err != nil { + return fmt.Errorf("search validation: %w", err) + }*/ return nil } diff --git a/pquery/lister_test.go b/pquery/lister_test.go index e8522a4..aa52d98 100644 --- a/pquery/lister_test.go +++ b/pquery/lister_test.go @@ -6,8 +6,10 @@ import ( "github.com/pentops/flowtest/prototest" "github.com/pentops/golib/gl" + "github.com/pentops/j5/lib/j5schema" "github.com/pentops/protostate/internal/pgstore" "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" ) @@ -51,6 +53,10 @@ func (c composed) toString() string { return out } +type J5Proto struct { + proto.Message +} + func TestBuildListReflection(t *testing.T) { type tableMod func(t testing.TB, spec *TableSpec, req, res protoreflect.MessageDescriptor) @@ -70,18 +76,32 @@ func TestBuildListReflection(t *testing.T) { import "j5/types/date/v1/date.proto"; import "buf/validate/validate.proto"; import "google/protobuf/timestamp.proto"; + + service FooService { + rpc FooList(FooListRequest) returns (FooListResponse) {} + } + ` + input, }) requestDesc := pdf.MessageByName(t, "test.FooListRequest") responseDesc := pdf.MessageByName(t, "test.FooListResponse") - table := &TableSpec{} if spec != nil { spec(t, table, requestDesc, responseDesc) } - return buildListReflection(requestDesc, responseDesc, *table) + schemaSet := j5schema.NewSchemaCache() + requestObj, err := schemaSet.Schema(requestDesc) + if err != nil { + t.Fatal(err) + } + responseObj, err := schemaSet.Schema(responseDesc) + if err != nil { + t.Fatal(err) + } + + return buildListReflection(requestObj.(*j5schema.ObjectSchema), responseObj.(*j5schema.ObjectSchema), *table) } runHappy := func(name string, input string, spec tableMod, callback func(*testing.T, *ListReflectionSet)) { @@ -176,7 +196,7 @@ func TestBuildListReflection(t *testing.T) { func(t testing.TB, table *TableSpec, req, res protoreflect.MessageDescriptor) { table.FallbackSortColumns = []ProtoField{{ valueColumn: gl.Ptr("id"), - pathInRoot: pgstore.ProtoPathSpec{"id"}, + pathInRoot: pgstore.JSONPathSpec{"id"}, }} }, func(t *testing.T, lr *ListReflectionSet) { diff --git a/pquery/search.go b/pquery/search.go index 36ee732..e42b1eb 100644 --- a/pquery/search.go +++ b/pquery/search.go @@ -2,143 +2,93 @@ package pquery import ( "fmt" - - "maps" + "regexp" sq "github.com/elgris/sqrl" "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" + "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" "github.com/pentops/j5/lib/j5schema" "github.com/pentops/protostate/internal/pgstore" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/types/descriptorpb" ) -func validateSearchesAnnotations(props []*j5schema.ObjectProperty) error { - _, err := validateSearchesAnnotationsInner(props) - if err != nil { - return fmt.Errorf("search validation: %w", err) +/* + func validateSearchesAnnotations(props []*j5schema.ObjectProperty) error { + _, err := validateSearchesAnnotationsInner(props) + if err != nil { + return fmt.Errorf("search validation: %w", err) + } + return nil } - return nil -} -func validateSearchesAnnotationsInner(fields []*j5schema.ObjectProperty) (map[string]protoreflect.Name, error) { - // search annotations have a 'field_identifier' which specifies the database column name to use for the text-search-vector. - // This function validates that the field_identifier is unique for the given field-set - // In cases where the field is a message, it will recurse into the message to validate the field identifiers + func validateSearchesAnnotationsInner(fields []*j5schema.ObjectProperty) (map[string]protoreflect.Name, error) { + // search annotations have a 'field_identifier' which specifies the database column name to use for the text-search-vector. + // This function validates that the field_identifier is unique for the given field-set + // In cases where the field is a message, it will recurse into the message to validate the field identifiers - // When the same message is used in multiple places, any field of that - // message or a child message will, by definition, have the same field - // identifier, and is therefore invalid. + // When the same message is used in multiple places, any field of that + // message or a child message will, by definition, have the same field + // identifier, and is therefore invalid. - // Search annotation cannot be used in repeated or map fields - // Recursion is already invalid. + // Search annotation cannot be used in repeated or map fields + // Recursion is already invalid. - // Oneof fields, however, can have the same field identifier on different - // branches. + // Oneof fields, however, can have the same field identifier on different + // branches. - ids := make(map[string]string) - oneofs := map[string][]*j5schema.ObjectProperty{} + ids := make(map[string]string) - for _, field := range fields { + for _, field := range fields { - if oneof, ok := field.Schema.(*j5schema.OneofField); ok { - oneofs[field.JSONName] = append(oneofs[field.JSONName], oneof) - continue + err := validateSearchAnnotationsField(ids, field) + if err != nil { + return nil, err + } } - err := validateSearchAnnotationsField(ids, field) - if err != nil { - return nil, err - } + return ids, nil } - for _, oneofFields := range oneofs { - - // oneof fields can have the same field identifier on different branches but must be unique: - // - within the branch, as before - // - with existing parent keys - // - with other oneofs - - combinedBranchIDs := make(map[string]string) - - for _, field := range oneofFields { +func validateSearchAnnotationsField(ids map[string]string, field *j5schema.ObjectProperty) error { - // collect a new set of IDs for this branch as if it is the root of - // the message. - branchIDs := make(map[string]string) - err := validateSearchAnnotationsField(branchIDs, field) + switch bigType := field.Schema.(type) { + case *j5schema.ObjectField: + fields := bigType.ObjectSchema().Fields() + searchIdentifiers, err := validateSearchesAnnotationsInner(fields) if err != nil { - return nil, err + return fmt.Errorf("object search validation: %w", err) } - // Compare the branch IDs to the root IDs, if a branch conflicts - // with the root that is an error. - for searchKey, usedIn := range branchIDs { + for searchKey, usedIn := range searchIdentifiers { if existing, ok := ids[searchKey]; ok { - return nil, fmt.Errorf("field identifier '%s' is used at %s and %s within the same oneof branch", searchKey, existing, usedIn) + return fmt.Errorf("field identifier '%s' is already used at %s", searchKey, existing) } - - // the latest wins here, this makes a worse error message but - // doesn't actually effect the outcome. - combinedBranchIDs[searchKey] = usedIn + ids[searchKey] = protoreflect.Name(fmt.Sprintf("%s.%s", field.JSONName, usedIn)) } - } - - // The IDs inside each branch are unique, and unique with the parent keys. - - // We still need to ensure that the IDs are unique with other oneofs. - maps.Copy(ids, combinedBranchIDs) - - } - - return ids, nil -} - -func validateSearchAnnotationsField(ids map[string]string, field *j5schema.ObjectProperty) error { - - switch bigType := field.Schema.(type) { - case *j5schema.ObjectField: - fields := bigType.ObjectSchema().Fields() - searchIdentifiers, err := validateSearchesAnnotationsInner(fields) - if err != nil { - return fmt.Errorf("object search validation: %w", err) - } - - for searchKey, usedIn := range searchIdentifiers { - if existing, ok := ids[searchKey]; ok { - return fmt.Errorf("field identifier '%s' is already used at %s", searchKey, existing) + case *j5schema.OneofField: + fields := bigType.OneofSchema().Fields() + searchIdentifiers, err := validateSearchesAnnotationsInner(fields) + if err != nil { + return fmt.Errorf("oneof search validation: %w", err) } - ids[searchKey] = protoreflect.Name(fmt.Sprintf("%s.%s", field.JSONName, usedIn)) - } - - case *j5schema.OneofField: - fields := bigType.OneofSchema().Fields() - searchIdentifiers, err := validateSearchesAnnotationsInner(fields) - if err != nil { - return fmt.Errorf("oneof search validation: %w", err) - } - for searchKey, usedIn := range searchIdentifiers { - if existing, ok := ids[searchKey]; ok { - return fmt.Errorf("field identifier '%s' is already used at %s", searchKey, existing) + for searchKey, usedIn := range searchIdentifiers { + if existing, ok := ids[searchKey]; ok { + return fmt.Errorf("field identifier '%s' is already used at %s", searchKey, existing) + } + ids[searchKey] = protoreflect.Name(fmt.Sprintf("%s.%s", field.JSONName, usedIn)) } - ids[searchKey] = protoreflect.Name(fmt.Sprintf("%s.%s", field.JSONName, usedIn)) - } - case *j5schema.MapField: - items := bigType.MapSchema() + case *j5schema.MapField: + items := bigType.MapSchema() - case *j5schema.ArrayField: + case *j5schema.ArrayField: - case *j5schema.ScalarField: + case *j5schema.ScalarField: - schema := bigType + schema := bigType + } } -} - -/* switch field.Kind() { case protoreflect.StringKind: @@ -187,90 +137,84 @@ func validateSearchAnnotationsField(ids map[string]string, field *j5schema.Objec } return nil -*/ -func validateQueryRequestSearches(message *j5schema.ObjectSchema, searches []*list_j5pb.Search) error { - for _, search := range searches { + func validateQueryRequestSearches(message *j5schema.ObjectSchema, searches []*list_j5pb.Search) error { + for _, search := range searches { - spec, err := pgstore.NewJSONPath(message, pgstore.ParseJSONPathSpec(search.GetField())) - if err != nil { - return fmt.Errorf("field spec: %w", err) - } + spec, err := pgstore.NewJSONPath(message, pgstore.ParseJSONPathSpec(search.GetField())) + if err != nil { + return fmt.Errorf("field spec: %w", err) + } - field := spec.LeafField() - if field == nil { - return fmt.Errorf("leaf '%s' is not a field", spec.JSONPathQuery()) - } + field := spec.LeafField() + if field == nil { + return fmt.Errorf("leaf '%s' is not a field", spec.JSONPathQuery()) + } - // validate the fields are annotated correctly for the request query - searchOpts, ok := proto.GetExtension(field.Options().(*descriptorpb.FieldOptions), list_j5pb.E_Field).(*list_j5pb.FieldConstraint) - if !ok { - return fmt.Errorf("requested search field '%s' does not have any searchable constraints defined", search.Field) - } + // validate the fields are annotated correctly for the request query + searchOpts, ok := proto.GetExtension(field.Options().(*descriptorpb.FieldOptions), list_j5pb.E_Field).(*list_j5pb.FieldConstraint) + if !ok { + return fmt.Errorf("requested search field '%s' does not have any searchable constraints defined", search.Field) + } - searchable := false - if searchOpts != nil { - switch field.Kind() { - case protoreflect.StringKind: - switch searchOpts.GetString_().WellKnown.(type) { - case *list_j5pb.StringRules_OpenText: - searchable = searchOpts.GetString_().GetOpenText().GetSearching().Searchable + searchable := false + if searchOpts != nil { + switch field.Kind() { + case protoreflect.StringKind: + switch searchOpts.GetString_().WellKnown.(type) { + case *list_j5pb.StringRules_OpenText: + searchable = searchOpts.GetString_().GetOpenText().GetSearching().Searchable + } } } - } - if !searchable { - return fmt.Errorf("requested search field '%s' is not searchable", search.Field) + if !searchable { + return fmt.Errorf("requested search field '%s' is not searchable", search.Field) + } } - } - return nil -} + return nil + } +*/ +var rePgUnsafe = regexp.MustCompile(`[^a-zA-Z0-9_]`) -func buildTsvColumnMap(message *j5schema.ObjectSchema) map[string]string { +func buildTsvColumnMap(message *j5schema.ObjectSchema) (map[string]string, error) { out := make(map[string]string) - for i := range message.Fields().Len() { - field := message.Fields().Get(i) + err := pgstore.WalkPathNodes(message, func(path pgstore.Path) error { + field := path.LeafField() + switch bigSchema := field.Schema.(type) { - switch field.Kind() { - case protoreflect.StringKind: - fieldOpts, ok := proto.GetExtension(field.Options().(*descriptorpb.FieldOptions), list_j5pb.E_Field).(*list_j5pb.FieldConstraint) - if !ok { - continue - } - - switch fieldOpts.GetString_().GetWellKnown().(type) { - case *list_j5pb.StringRules_OpenText: - searchOpts := fieldOpts.GetString_().GetOpenText().GetSearching() - if searchOpts == nil || !searchOpts.Searchable { - continue + case *j5schema.ScalarSchema: + innerSchema := bigSchema.ToJ5Field() + switch ist := innerSchema.Type.(type) { + case *schema_j5pb.Field_String_: + if ist.String_.ListRules == nil || ist.String_.ListRules.Searching == nil || !ist.String_.ListRules.Searching.Searchable { + return nil // not searchable } - out[field.TextName()] = searchOpts.GetFieldIdentifier() - } - - continue - case protoreflect.MessageKind: - nestedMap := buildTsvColumnMap(field.Message()) - - for nk, nv := range nestedMap { - k := fmt.Sprintf("%s.%s", field.TextName(), nk) - out[k] = nv + idPath := path.IDPath() + columnName := rePgUnsafe.ReplaceAllString(idPath, "_") + out[idPath] = fmt.Sprintf("tsv_%s", columnName) } } + + return nil + }) + if err != nil { + return nil, err } - return out + return out, nil } func (ll *Lister[REQ, RES]) buildDynamicSearches(tableAlias string, searches []*list_j5pb.Search) ([]sq.Sqlizer, error) { out := []sq.Sqlizer{} for i := range searches { - col, ok := ll.tsvColumnMap[camelToSnake(searches[i].GetField())] + col, ok := ll.tsvColumnMap[searches[i].GetField()] if !ok { - return nil, fmt.Errorf("unknown field name '%s'", searches[i].GetField()) + return nil, fmt.Errorf("unknown search field %q", searches[i].GetField()) } out = append(out, sq.And{sq.Expr(fmt.Sprintf("%s.%s @@ phraseto_tsquery(?)", tableAlias, col), searches[i].GetValue())}) diff --git a/pquery/search_test.go b/pquery/search_test.go index 5298166..688396d 100644 --- a/pquery/search_test.go +++ b/pquery/search_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/pentops/flowtest/prototest" + "github.com/pentops/j5/lib/j5schema" "github.com/stretchr/testify/assert" ) @@ -35,8 +36,15 @@ func TestBuildTsvColumnMap(t *testing.T) { `}) fooDesc := descFiles.MessageByName(t, "test.Foo") + fooObj, err := j5schema.Global.ObjectSchema(fooDesc) + if err != nil { + t.Fatalf("failed to get schema for Foo: %v", err) + } - columnMap := buildTsvColumnMap(fooDesc) + columnMap, err := buildTsvColumnMap(fooObj) + if err != nil { + t.Fatalf("failed to build TSV column map: %v", err) + } assert.Len(t, columnMap, 2) for f, c := range columnMap { @@ -44,6 +52,7 @@ func TestBuildTsvColumnMap(t *testing.T) { } } +/* func TestValidateSearchAnnotations(t *testing.T) { t.Run("happy path", func(t *testing.T) { descFiles := prototest.DescriptorsFromSource(t, map[string]string{ @@ -74,8 +83,12 @@ func TestValidateSearchAnnotations(t *testing.T) { `}) fooDesc := descFiles.MessageByName(t, "test.Foo") + fooObj, err := j5schema.Global.ObjectSchema(fooDesc) + if err != nil { + t.Fatalf("failed to get schema for Foo: %v", err) + } - err := validateSearchesAnnotations(fooDesc.Fields()) + err = validateSearchesAnnotations(fooObj.Properties) assert.NoError(t, err) }) @@ -144,19 +157,32 @@ func TestValidateSearchAnnotations(t *testing.T) { // The two instances of Msg within Foo are mutually exclusive, so is OK fooDesc := descFiles.MessageByName(t, "test.Foo") - err := validateSearchesAnnotations(fooDesc.Fields()) + fooObj, err := j5schema.Global.ObjectSchema(fooDesc) + if err != nil { + t.Fatalf("failed to get schema for Foo: %v", err) + } + err = validateSearchesAnnotations(fooObj.Properties) assert.NoError(t, err) // The two instances of Msg within Bar are NOT mutually exclusive, this // is not OK. barDesc := descFiles.MessageByName(t, "test.Bar") - err = validateSearchesAnnotations(barDesc.Fields()) + barObj, err := j5schema.Global.ObjectSchema(barDesc) + if err != nil { + t.Fatalf("failed to get schema for Bar: %v", err) + } + err = validateSearchesAnnotations(barObj.Properties) assert.Error(t, err) // Each oneof in Baz is OK by itself, but Type1 and Type3 can be set // together, and so the search key can be duplicated. bazDesc := descFiles.MessageByName(t, "test.Baz") - err = validateSearchesAnnotations(bazDesc.Fields()) + bazObj, err := j5schema.Global.ObjectSchema(bazDesc) + if err != nil { + t.Fatalf("failed to get schema for Baz: %v", err) + } + + err = validateSearchesAnnotations(bazObj.Properties) assert.Error(t, err) }) @@ -188,8 +214,12 @@ func TestValidateSearchAnnotations(t *testing.T) { `}) fooDesc := descFiles.MessageByName(t, "test.Foo") + fooObj, err := j5schema.Global.ObjectSchema(fooDesc) + if err != nil { + t.Fatalf("failed to get schema for Foo: %v", err) + } - err := validateSearchesAnnotations(fooDesc.Fields()) + err = validateSearchesAnnotations(fooObj.Properties) assert.Error(t, err) }) @@ -212,8 +242,12 @@ func TestValidateSearchAnnotations(t *testing.T) { `}) fooDesc := descFiles.MessageByName(t, "test.Foo") + fooObj, err := j5schema.Global.ObjectSchema(fooDesc) + if err != nil { + t.Fatalf("failed to get schema for Foo: %v", err) + } - err := validateSearchesAnnotations(fooDesc.Fields()) + err = validateSearchesAnnotations(fooObj.Properties) assert.Error(t, err) }) -} +}*/ diff --git a/pquery/sort.go b/pquery/sort.go index 8ebb13a..f7a3c04 100644 --- a/pquery/sort.go +++ b/pquery/sort.go @@ -4,11 +4,9 @@ import ( "fmt" "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" + "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" "github.com/pentops/j5/lib/j5schema" "github.com/pentops/protostate/internal/pgstore" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/types/descriptorpb" ) type sortSpec struct { @@ -21,11 +19,11 @@ func (ss sortSpec) errorName() string { } func buildTieBreakerFields(dataColumn string, req *j5schema.ObjectSchema, arrayField *j5schema.ObjectSchema, fallback []ProtoField) ([]sortSpec, error) { - listRequestAnnotation, ok := proto.GetExtension(req.Options().(*descriptorpb.MessageOptions), list_j5pb.E_ListRequest).(*list_j5pb.ListRequestMessage) - if ok && listRequestAnnotation != nil && len(listRequestAnnotation.SortTiebreaker) > 0 { - tieBreakerFields := make([]sortSpec, 0, len(listRequestAnnotation.SortTiebreaker)) - for _, tieBreaker := range listRequestAnnotation.SortTiebreaker { - spec, err := pgstore.NewProtoPath(arrayField, pgstore.ParseProtoPathSpec(tieBreaker)) + + if req.ListRequest != nil && len(req.ListRequest.SortTiebreaker) > 0 { + tieBreakerFields := make([]sortSpec, 0, len(req.ListRequest.SortTiebreaker)) + for _, tieBreaker := range req.ListRequest.SortTiebreaker { + spec, err := pgstore.NewOuterPath(arrayField, tieBreaker) if err != nil { return nil, fmt.Errorf("field %s in annotated sort tiebreaker for %s: %w", tieBreaker, req.FullName(), err) } @@ -49,7 +47,7 @@ func buildTieBreakerFields(dataColumn string, req *j5schema.ObjectSchema, arrayF tieBreakerFields := make([]sortSpec, 0, len(fallback)) for _, tieBreaker := range fallback { - path, err := pgstore.NewProtoPath(arrayField, tieBreaker.pathInRoot) + path, err := pgstore.NewOuterPath(arrayField, tieBreaker.pathInRoot) if err != nil { return nil, fmt.Errorf("field %s in fallback sort tiebreaker for %s: %w", tieBreaker.pathInRoot, req.FullName(), err) } @@ -67,6 +65,40 @@ func buildTieBreakerFields(dataColumn string, req *j5schema.ObjectSchema, arrayF return tieBreakerFields, nil } +func getFieldSorting(field *j5schema.ObjectProperty) *list_j5pb.SortingConstraint { + scalar, ok := field.Schema.(*j5schema.ScalarSchema) + if !ok { + return nil // only scalars are sortable + } + + schema := scalar.ToJ5Field() + + switch st := schema.Type.(type) { + case *schema_j5pb.Field_Float: + if st.Float.ListRules == nil { + return nil + } + return st.Float.ListRules.Sorting + case *schema_j5pb.Field_Integer: + if st.Integer.ListRules == nil { + return nil + } + return st.Integer.ListRules.Sorting + case *schema_j5pb.Field_Timestamp: + if st.Timestamp.ListRules == nil { + return nil + } + return st.Timestamp.ListRules.Sorting + case *schema_j5pb.Field_Decimal: + if st.Decimal.ListRules == nil { + return nil + } + return st.Decimal.ListRules.Sorting + default: + return nil + } +} + func buildDefaultSorts(columnName string, message *j5schema.ObjectSchema) ([]sortSpec, error) { var defaultSortFields []sortSpec @@ -76,68 +108,11 @@ func buildDefaultSorts(columnName string, message *j5schema.ObjectSchema) ([]sor return nil // oneof or something } - fieldOpts := proto.GetExtension(field.Options().(*descriptorpb.FieldOptions), list_j5pb.E_Field).(*list_j5pb.FieldConstraint) - - if fieldOpts == nil { - return nil + sortConstraint := getFieldSorting(field) + if sortConstraint == nil { + return nil // not a sortable field } - isDefaultSort := false - - switch fieldOps := fieldOpts.Type.(type) { - case *list_j5pb.FieldConstraint_Double: - if fieldOps.Double.Sorting != nil && fieldOps.Double.Sorting.DefaultSort { - isDefaultSort = true - } - case *list_j5pb.FieldConstraint_Fixed32: - if fieldOps.Fixed32.Sorting != nil && fieldOps.Fixed32.Sorting.DefaultSort { - isDefaultSort = true - } - case *list_j5pb.FieldConstraint_Fixed64: - if fieldOps.Fixed64.Sorting != nil && fieldOps.Fixed64.Sorting.DefaultSort { - isDefaultSort = true - } - case *list_j5pb.FieldConstraint_Float: - if fieldOps.Float.Sorting != nil && fieldOps.Float.Sorting.DefaultSort { - isDefaultSort = true - } - case *list_j5pb.FieldConstraint_Int32: - if fieldOps.Int32.Sorting != nil && fieldOps.Int32.Sorting.DefaultSort { - isDefaultSort = true - } - case *list_j5pb.FieldConstraint_Int64: - if fieldOps.Int64.Sorting != nil && fieldOps.Int64.Sorting.DefaultSort { - isDefaultSort = true - } - case *list_j5pb.FieldConstraint_Sfixed32: - if fieldOps.Sfixed32.Sorting != nil && fieldOps.Sfixed32.Sorting.DefaultSort { - isDefaultSort = true - } - case *list_j5pb.FieldConstraint_Sfixed64: - if fieldOps.Sfixed64.Sorting != nil && fieldOps.Sfixed64.Sorting.DefaultSort { - isDefaultSort = true - } - case *list_j5pb.FieldConstraint_Sint32: - if fieldOps.Sint32.Sorting != nil && fieldOps.Sint32.Sorting.DefaultSort { - isDefaultSort = true - } - case *list_j5pb.FieldConstraint_Sint64: - if fieldOps.Sint64.Sorting != nil && fieldOps.Sint64.Sorting.DefaultSort { - isDefaultSort = true - } - case *list_j5pb.FieldConstraint_Uint32: - if fieldOps.Uint32.Sorting != nil && fieldOps.Uint32.Sorting.DefaultSort { - isDefaultSort = true - } - case *list_j5pb.FieldConstraint_Uint64: - if fieldOps.Uint64.Sorting != nil && fieldOps.Uint64.Sorting.DefaultSort { - isDefaultSort = true - } - case *list_j5pb.FieldConstraint_Timestamp: - if fieldOps.Timestamp.Sorting != nil && fieldOps.Timestamp.Sorting.DefaultSort { - isDefaultSort = true - } - } - if isDefaultSort { + if sortConstraint.DefaultSort { defaultSortFields = append(defaultSortFields, sortSpec{ NestedField: &pgstore.NestedField{ RootColumn: columnName, @@ -160,7 +135,7 @@ func (ll *Lister[REQ, RES]) buildDynamicSortSpec(sorts []*list_j5pb.Sort) ([]sor direction := "" for _, sort := range sorts { pathSpec := pgstore.ParseJSONPathSpec(sort.Field) - spec, err := pgstore.NewJSONPath(ll.arrayField.Message(), pathSpec) + spec, err := pgstore.NewJSONPath(ll.arrayObject, pathSpec) if err != nil { return nil, fmt.Errorf("dynamic filter: find field: %w", err) } @@ -192,45 +167,6 @@ func (ll *Lister[REQ, RES]) buildDynamicSortSpec(sorts []*list_j5pb.Sort) ([]sor return results, nil } -func validateSortsAnnotations(fields []*j5schema.ObjectProperty) error { - for _, field := range fields { - - if field.Kind() == protoreflect.MessageKind { - subFields := field.Message().Fields() - - for i := range subFields.Len() { - subField := subFields.Get(i) - - if subField.Kind() == protoreflect.MessageKind { - err := validateSortsAnnotations(subField.Message().Fields()) - if err != nil { - return fmt.Errorf("message sort validation: %w", err) - } - } else { - if field.Cardinality() == protoreflect.Repeated { - // check options of subfield for sorting - fieldOpts := proto.GetExtension(subField.Options().(*descriptorpb.FieldOptions), list_j5pb.E_Field).(*list_j5pb.FieldConstraint) - if isSortingAnnotated(fieldOpts) { - return fmt.Errorf("sorting not allowed on subfield of repeated parent: %s", field.Name()) - } - } - } - } - } else { - if field.Cardinality() == protoreflect.Repeated { - // check options of parent field for sorting - fieldOpts := proto.GetExtension(field.Options().(*descriptorpb.FieldOptions), list_j5pb.E_Field).(*list_j5pb.FieldConstraint) - if isSortingAnnotated(fieldOpts) { - return fmt.Errorf("sorting not allowed on repeated field, must be a scalar: %s", field.Name()) - } - } - - } - } - - return nil -} - func isSortingAnnotated(opts *list_j5pb.FieldConstraint) bool { annotated := false @@ -307,47 +243,8 @@ func validateQueryRequestSorts(message *j5schema.ObjectSchema, sorts []*list_j5p return fmt.Errorf("node %s is not a field", spec.DebugName()) } - // validate the fields are annotated correctly for the request query - sortOpts, ok := proto.GetExtension(field.Options().(*descriptorpb.FieldOptions), list_j5pb.E_Field).(*list_j5pb.FieldConstraint) - if !ok { - return fmt.Errorf("requested sort field '%s' does not have any sortable constraints defined", sort.Field) - } - - sortable := false - if sortOpts != nil { - switch field.Kind() { - case protoreflect.DoubleKind: - sortable = sortOpts.GetDouble().GetSorting().Sortable - case protoreflect.Fixed32Kind: - sortable = sortOpts.GetFixed32().GetSorting().Sortable - case protoreflect.Fixed64Kind: - sortable = sortOpts.GetFixed64().GetSorting().Sortable - case protoreflect.FloatKind: - sortable = sortOpts.GetFloat().GetSorting().Sortable - case protoreflect.Int32Kind: - sortable = sortOpts.GetInt32().GetSorting().Sortable - case protoreflect.Int64Kind: - sortable = sortOpts.GetInt64().GetSorting().Sortable - case protoreflect.Sfixed32Kind: - sortable = sortOpts.GetSfixed32().GetSorting().Sortable - case protoreflect.Sfixed64Kind: - sortable = sortOpts.GetSfixed64().GetSorting().Sortable - case protoreflect.Sint32Kind: - sortable = sortOpts.GetSint32().GetSorting().Sortable - case protoreflect.Sint64Kind: - sortable = sortOpts.GetSint64().GetSorting().Sortable - case protoreflect.Uint32Kind: - sortable = sortOpts.GetUint32().GetSorting().Sortable - case protoreflect.Uint64Kind: - sortable = sortOpts.GetUint64().GetSorting().Sortable - case protoreflect.MessageKind: - if field.Message().FullName() == "google.protobuf.Timestamp" { - sortable = sortOpts.GetTimestamp().GetSorting().Sortable - } - } - } - - if !sortable { + sortAnnotation := getFieldSorting(field) + if sortAnnotation == nil || !sortAnnotation.Sortable { return fmt.Errorf("requested sort field '%s' is not sortable", sort.Field) } } From f37f7f36a369f47f7317fd39de9b248bcdcd57f4 Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Fri, 18 Jul 2025 10:47:52 -0700 Subject: [PATCH 03/12] Tests --- pquery/filter.go | 21 ++++++++++++--- pquery/lister_test.go | 62 +++++++++++++++++++++++-------------------- 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/pquery/filter.go b/pquery/filter.go index 8f7aaca..abfe113 100644 --- a/pquery/filter.go +++ b/pquery/filter.go @@ -190,9 +190,6 @@ func filtersForField(field *j5schema.ObjectProperty) ([]any, error) { vals = append(vals, t) } return vals, nil - case *schema_j5pb.Field_String_: - // no filters definable - return nil, nil case *schema_j5pb.Field_Decimal: if st.Decimal.ListRules == nil || st.Decimal.ListRules.Filtering == nil || !st.Decimal.ListRules.Filtering.Filterable { @@ -208,10 +205,28 @@ func filtersForField(field *j5schema.ObjectProperty) ([]any, error) { vals = append(vals, v) } return vals, nil + case *schema_j5pb.Field_String_: + // no filters definable + return nil, nil + case *schema_j5pb.Field_Bytes: + return nil, nil default: return nil, fmt.Errorf("unknown scalar type for default filter (%s): %T", field.JSONName, st) } + case *j5schema.ObjectField: + // none + return nil, nil + case *j5schema.AnyField: + // none + return nil, nil + case *j5schema.MapField: + // none + return nil, nil + case *j5schema.ArrayField: + // none + return nil, nil + default: return nil, fmt.Errorf("unknown field type for filter rules %T", bigType) } diff --git a/pquery/lister_test.go b/pquery/lister_test.go index aa52d98..7767ade 100644 --- a/pquery/lister_test.go +++ b/pquery/lister_test.go @@ -62,6 +62,7 @@ func TestBuildListReflection(t *testing.T) { type tableMod func(t testing.TB, spec *TableSpec, req, res protoreflect.MessageDescriptor) build := func(t testing.TB, input string, spec tableMod) (*ListReflectionSet, error) { + t.Helper() pdf := prototest.DescriptorsFromSource(t, map[string]string{ "test.proto": ` syntax = "proto3"; @@ -94,11 +95,11 @@ func TestBuildListReflection(t *testing.T) { schemaSet := j5schema.NewSchemaCache() requestObj, err := schemaSet.Schema(requestDesc) if err != nil { - t.Fatal(err) + return nil, err } responseObj, err := schemaSet.Schema(responseDesc) if err != nil { - t.Fatal(err) + return nil, err } return buildListReflection(requestObj.(*j5schema.ObjectSchema), responseObj.(*j5schema.ObjectSchema), *table) @@ -349,7 +350,7 @@ func TestBuildListReflection(t *testing.T) { `, }.toString(), nil, - "should be a message", + "unknown field in response", ) runSad("extra array field in response", composed{ @@ -360,7 +361,7 @@ func TestBuildListReflection(t *testing.T) { `, }.toString(), nil, - "multiple repeated fields") + "multiple repeated") runSad("no array field in response", composed{ FooListResponse: ` @@ -368,7 +369,7 @@ func TestBuildListReflection(t *testing.T) { `, }.toString(), nil, - "no repeated field in response", + "does not contain a repeated message field", ) runSad("no page field in response", composed{ @@ -402,7 +403,7 @@ func TestBuildListReflection(t *testing.T) { `, }.toString(), nil, - "no field named 'missing'", + `unknown proto field "missing"`, ) runSad("no page field", composed{ @@ -449,32 +450,35 @@ func TestBuildListReflection(t *testing.T) { } `, nil, - "sorting not allowed on repeated field", + "list constraints not supported for arrays", ) - runSad("repeated sub field sort", ` - message FooListRequest { - j5.list.v1.PageRequest page = 1; - j5.list.v1.QueryRequest query = 2; - } + /* + Test Change: Repeated isn't walked for sorting, but isn't invalid in case Profile is used in another non-array context. + runSad("repeated sub field sort", ` + message FooListRequest { + j5.list.v1.PageRequest page = 1; + j5.list.v1.QueryRequest query = 2; + } - message FooListResponse { - repeated Foo foos = 1; - j5.list.v1.PageResponse page = 2; - } + message FooListResponse { + repeated Foo foos = 1; + j5.list.v1.PageResponse page = 2; + } - message Foo { - string id = 1; - int64 seq = 2 [(j5.list.v1.field).int64.sorting = {sortable: true, default_sort: true}]; - repeated Profile profiles = 3; - } + message Foo { + string id = 1; + int64 seq = 2 [(j5.list.v1.field).int64.sorting = {sortable: true, default_sort: true}]; + repeated Profile profiles = 3; + } - message Profile { - string name = 1; - int64 weight = 2 [(j5.list.v1.field).int64.sorting.sortable = true]; - } - `, - nil, - "sorting not allowed on subfield of repeated parent", - ) + message Profile { + string name = 1; + int64 weight = 2 [(j5.list.v1.field).int64.sorting.sortable = true]; + } + `, + nil, + "sorting not allowed on subfield of repeated parent", + ) + */ } From f6cc820c9a7e209dee0bbeafd5775d07a79f5191 Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Sun, 27 Jul 2025 13:21:59 -0700 Subject: [PATCH 04/12] Tests Pass --- cmd/protoc-gen-go-psm/main.go | 3 +- internal/dbconvert/dbconvert.go | 43 +- .../integration/query_test/filter_test.go | 290 ++-- internal/integration/query_test/helpers.go | 2 +- .../query_test/marshalling_test.go | 46 +- .../integration/query_test/pagination_test.go | 32 +- .../integration/query_test/search_test.go | 6 +- internal/integration/query_test/setup.go | 3 +- internal/integration/query_test/sort_test.go | 82 +- internal/integration/query_test/universe.go | 2 +- internal/pgstore/pgmigrate/psm.go | 51 +- internal/pgstore/pgmigrate/psm_test.go | 8 +- internal/protogen/query/code.go | 48 +- internal/protogen/query/parse.go | 67 +- internal/protogen/state/code.go | 9 +- internal/protogen/state/parse.go | 12 +- .../gen/test/v1/test_pb/bar.j5s.pb.go | 868 ++++++++++++ .../gen/test/v1/test_pb/bar.j5s_j5.pb.go | 80 +- .../gen/test/v1/test_pb/bar.j5s_psm.pb.go | 618 +++++++++ .../gen/test/v1/test_pb/bar.j5s_sugar.pb.go | 120 ++ .../gen/test/v1/test_pb/foo.j5s.pb.go | 1201 +++++++++++++++++ .../gen/test/v1/test_pb/foo.j5s_j5.pb.go | 100 +- .../gen/test/v1/test_pb/foo.j5s_psm.pb.go | 628 +++++++++ .../gen/test/v1/test_pb/foo.j5s_sugar.pb.go | 120 ++ .../gen/test/v1/test_spb/bar.p.j5s.pb.go | 649 +++++++++ .../gen/test/v1/test_spb/bar.p.j5s_grpc.pb.go | 186 +++ .../gen/test/v1/test_spb/bar.p.j5s_j5.pb.go | 61 +- .../v1/test_spb/bar.p.j5s_psm_query.pb.go | 105 ++ .../test/v1/test_spb/bar.p.j5s_sugar.pb.go | 3 + .../gen/test/v1/test_spb/foo.p.j5s.pb.go | 768 +++++++++++ .../gen/test/v1/test_spb/foo.p.j5s_grpc.pb.go | 277 ++++ .../gen/test/v1/test_spb/foo.p.j5s_j5.pb.go | 81 +- .../v1/test_spb/foo.p.j5s_psm_query.pb.go | 103 ++ .../test/v1/test_spb/foo.p.j5s_sugar.pb.go | 3 + .../gen/test/v1/test_tpb/bar.p.j5s.pb.go | 235 ++++ .../gen/test/v1/test_tpb/bar.p.j5s_grpc.pb.go | 111 ++ .../gen/test/v1/test_tpb/bar.p.j5s_j5.pb.go | 11 +- .../v1/test_tpb/bar.p.j5s_o5_messaging.pb.go | 95 ++ .../v1/test_tpb/bar.p.j5s_psm_publish.pb.go | 35 + .../test/v1/test_tpb/bar.p.j5s_sugar.pb.go | 3 + .../gen/test/v1/test_tpb/foo.p.j5s.pb.go | 235 ++++ .../gen/test/v1/test_tpb/foo.p.j5s_grpc.pb.go | 111 ++ .../gen/test/v1/test_tpb/foo.p.j5s_j5.pb.go | 11 +- .../v1/test_tpb/foo.p.j5s_o5_messaging.pb.go | 95 ++ .../v1/test_tpb/foo.p.j5s_psm_publish.pb.go | 35 + .../test/v1/test_tpb/foo.p.j5s_sugar.pb.go | 3 + pquery/filter.go | 111 +- pquery/getter.go | 45 +- pquery/lister.go | 420 +++--- pquery/lister_test.go | 118 +- pquery/method.go | 30 + {internal/pgstore => pquery}/path.go | 68 +- {internal/pgstore => pquery}/path_test.go | 7 +- pquery/query.go | 17 - pquery/search.go | 50 +- pquery/search_test.go | 17 +- pquery/sort.go | 90 +- pquery/types.go | 1 + psm/builder.go | 44 +- psm/gen_interfaces.go | 8 + psm/run.go | 9 +- psm/state_query.go | 231 ++-- psm/statemachine.go | 103 +- psm/table_map.go | 121 +- 64 files changed, 8151 insertions(+), 994 deletions(-) create mode 100644 internal/testproto/gen/test/v1/test_pb/bar.j5s.pb.go create mode 100644 internal/testproto/gen/test/v1/test_pb/bar.j5s_psm.pb.go create mode 100644 internal/testproto/gen/test/v1/test_pb/bar.j5s_sugar.pb.go create mode 100644 internal/testproto/gen/test/v1/test_pb/foo.j5s.pb.go create mode 100644 internal/testproto/gen/test/v1/test_pb/foo.j5s_psm.pb.go create mode 100644 internal/testproto/gen/test/v1/test_pb/foo.j5s_sugar.pb.go create mode 100644 internal/testproto/gen/test/v1/test_spb/bar.p.j5s.pb.go create mode 100644 internal/testproto/gen/test/v1/test_spb/bar.p.j5s_grpc.pb.go create mode 100644 internal/testproto/gen/test/v1/test_spb/bar.p.j5s_psm_query.pb.go create mode 100644 internal/testproto/gen/test/v1/test_spb/bar.p.j5s_sugar.pb.go create mode 100644 internal/testproto/gen/test/v1/test_spb/foo.p.j5s.pb.go create mode 100644 internal/testproto/gen/test/v1/test_spb/foo.p.j5s_grpc.pb.go create mode 100644 internal/testproto/gen/test/v1/test_spb/foo.p.j5s_psm_query.pb.go create mode 100644 internal/testproto/gen/test/v1/test_spb/foo.p.j5s_sugar.pb.go create mode 100644 internal/testproto/gen/test/v1/test_tpb/bar.p.j5s.pb.go create mode 100644 internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_grpc.pb.go create mode 100644 internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_o5_messaging.pb.go create mode 100644 internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_psm_publish.pb.go create mode 100644 internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_sugar.pb.go create mode 100644 internal/testproto/gen/test/v1/test_tpb/foo.p.j5s.pb.go create mode 100644 internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_grpc.pb.go create mode 100644 internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_o5_messaging.pb.go create mode 100644 internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_psm_publish.pb.go create mode 100644 internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_sugar.pb.go create mode 100644 pquery/method.go rename {internal/pgstore => pquery}/path.go (85%) rename {internal/pgstore => pquery}/path_test.go (93%) create mode 100644 pquery/types.go diff --git a/cmd/protoc-gen-go-psm/main.go b/cmd/protoc-gen-go-psm/main.go index e97a6fd..e5eb51c 100644 --- a/cmd/protoc-gen-go-psm/main.go +++ b/cmd/protoc-gen-go-psm/main.go @@ -42,7 +42,8 @@ func generateFile(gen *protogen.Plugin, file *protogen.File) { stateSets, err := state.WalkFile(file) if err != nil { - gen.Error(err) + + gen.Error(fmt.Errorf("walkFile: %w", err)) return } diff --git a/internal/dbconvert/dbconvert.go b/internal/dbconvert/dbconvert.go index f5e3cf4..6a210a1 100644 --- a/internal/dbconvert/dbconvert.go +++ b/internal/dbconvert/dbconvert.go @@ -7,9 +7,8 @@ import ( sq "github.com/elgris/sqrl" "github.com/pentops/j5/j5types/date_j5t" - "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" + "github.com/pentops/j5/lib/j5codec" + "github.com/pentops/j5/lib/j5reflect" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -40,6 +39,10 @@ func FieldsToEqMap(ofTable string, m map[string]any) (sq.Eq, error) { return out, nil } +type j5message interface { + J5Reflect() j5reflect.Root +} + func interfaceToDBValue(i any) (any, error) { switch v := i.(type) { case *timestamppb.Timestamp: @@ -51,8 +54,8 @@ func interfaceToDBValue(i any) (any, error) { case driver.Valuer: return v, nil - case proto.Message: - i, err := MarshalProto(v) + case j5message: + i, err := marshalJ5(v) if err != nil { return nil, fmt.Errorf("interface values: %w", err) } @@ -75,6 +78,34 @@ func interfaceToDBValue(i any) (any, error) { return i, nil } +var codec *j5codec.Codec + +func init() { + codec = j5codec.NewCodec(j5codec.WithIncludeEmpty()) +} + +func MarshalJ5(msg j5reflect.Root) ([]byte, error) { + return codec.ReflectToJSON(msg) +} + +func UnmarshalJ5(b []byte, msg j5reflect.Root) error { + return codec.JSONToReflect(b, msg) +} + +func marshalJ5(msg j5message) (any, error) { + if msg == nil { + return nil, nil + } + + j5r := msg.J5Reflect() + if j5r == nil { + return nil, fmt.Errorf("j5reflect is nil for %T", msg) + } + + return MarshalJ5(j5r) +} + +/* func MarshalProto(state protoreflect.ProtoMessage) ([]byte, error) { // EmitDefaultValues behaves similarly to EmitUnpopulated, but does not emit "null"-value fields, // i.e. presence-sensing fields that are omitted will remain omitted to preserve presence-sensing. @@ -83,4 +114,4 @@ func MarshalProto(state protoreflect.ProtoMessage) ([]byte, error) { return b, fmt.Errorf("custom protomarshal: %w", err) } return b, nil -} +}*/ diff --git a/internal/integration/query_test/filter_test.go b/internal/integration/query_test/filter_test.go index 6f62967..721c52b 100644 --- a/internal/integration/query_test/filter_test.go +++ b/internal/integration/query_test/filter_test.go @@ -2,6 +2,7 @@ package integration import ( "context" + "encoding/base64" "testing" "time" @@ -44,7 +45,7 @@ func TestDefaultFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -119,7 +120,7 @@ func TestFilteringWithAuthScope(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -187,7 +188,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -212,23 +213,21 @@ func TestDynamicFiltering(t *testing.T) { }) }) - t.Run("Min Range Filter", func(t *testing.T) { - ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.characteristics.weight", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Min: "12", - }, + ss.Step("Min Range Filter", func(ctx context.Context, t flowtest.Asserter) { + req := &test_spb.FooListRequest{ + Page: &list_j5pb.PageRequest{ + PageSize: proto.Int64(5), + }, + Query: &list_j5pb.QueryRequest{ + Filters: []*list_j5pb.Filter{ + { + Type: &list_j5pb.Filter_Field{ + Field: &list_j5pb.Field{ + Name: "data.characteristics.weight", + Type: &list_j5pb.FieldType{ + Type: &list_j5pb.FieldType_Range{ + Range: &list_j5pb.Range{ + Min: "12", }, }, }, @@ -236,47 +235,48 @@ func TestDynamicFiltering(t *testing.T) { }, }, }, - } - res := &test_spb.FooListResponse{} + }, + } + res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) - if err != nil { - t.Fatal(err.Error()) - } + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + if err != nil { + t.Fatal(err.Error()) + } - if len(res.Foo) != 5 { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) + if len(res.Foo) != 5 { + for _, foo := range res.Foo { + t.Logf("Foo: %s, Weight: %d", foo.Data.Field, foo.Data.Characteristics.Weight) } + t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) + } - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } + for ii, state := range res.Foo { + t.Logf("%d: %s", ii, state.Data.Field) + } - for _, state := range res.Foo { - if state.Data.Characteristics.Weight < int64(12) { - t.Fatalf("expected weights greater than or equal to %d, got %d", 12, state.Data.Characteristics.Weight) - } + for _, state := range res.Foo { + if state.Data.Characteristics.Weight < int64(12) { + t.Fatalf("expected weights greater than or equal to %d, got %d", 12, state.Data.Characteristics.Weight) } - }) + } }) - t.Run("Max Range Filter", func(t *testing.T) { - ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.characteristics.weight", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Max: "15", - }, + ss.Step("Max Range Filter", func(ctx context.Context, t flowtest.Asserter) { + req := &test_spb.FooListRequest{ + Page: &list_j5pb.PageRequest{ + PageSize: proto.Int64(5), + }, + Query: &list_j5pb.QueryRequest{ + Filters: []*list_j5pb.Filter{ + { + Type: &list_j5pb.Filter_Field{ + Field: &list_j5pb.Field{ + Name: "data.characteristics.weight", + Type: &list_j5pb.FieldType{ + Type: &list_j5pb.FieldType_Range{ + Range: &list_j5pb.Range{ + Max: "15", }, }, }, @@ -284,69 +284,62 @@ func TestDynamicFiltering(t *testing.T) { }, }, }, - } - res := &test_spb.FooListResponse{} + }, + } + res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) - if err != nil { - t.Fatal(err.Error()) - } + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + if err != nil { + t.Fatal(err.Error()) + } - if len(res.Foo) != 5 { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } + if len(res.Foo) != 5 { + t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) + } - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } + for ii, state := range res.Foo { + t.Logf("%d: %s", ii, state.Data.Field) + } - for _, state := range res.Foo { - if state.Data.Characteristics.Weight > int64(15) { - t.Fatalf("expected weight less than or equal to %d, got %d", 15, state.Data.Characteristics.Weight) - } + for _, state := range res.Foo { + if state.Data.Characteristics.Weight > int64(15) { + t.Fatalf("expected weight less than or equal to %d, got %d", 15, state.Data.Characteristics.Weight) } - }) + } }) t.Run("Multi Range Filter", func(t *testing.T) { nextToken := "" - ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(10), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Or{ - Or: &list_j5pb.Or{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.characteristics.weight", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Min: "12", - Max: "20", - }, - }, + query := &list_j5pb.QueryRequest{ + Filters: []*list_j5pb.Filter{ + { + Type: &list_j5pb.Filter_Or{ + Or: &list_j5pb.Or{ + Filters: []*list_j5pb.Filter{ + { + Type: &list_j5pb.Filter_Field{ + Field: &list_j5pb.Field{ + Name: "data.characteristics.weight", + Type: &list_j5pb.FieldType{ + Type: &list_j5pb.FieldType_Range{ + Range: &list_j5pb.Range{ + Min: "12", + Max: "20", }, }, }, }, - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.characteristics.height", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Min: "16", - Max: "18", - }, - }, + }, + }, + { + Type: &list_j5pb.Filter_Field{ + Field: &list_j5pb.Field{ + Name: "data.characteristics.height", + Type: &list_j5pb.FieldType{ + Type: &list_j5pb.FieldType_Range{ + Range: &list_j5pb.Range{ + Min: "16", + Max: "18", }, }, }, @@ -357,10 +350,18 @@ func TestDynamicFiltering(t *testing.T) { }, }, }, + }, + } + ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { + req := &test_spb.FooListRequest{ + Page: &list_j5pb.PageRequest{ + PageSize: proto.Int64(10), + }, + Query: query, } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -395,60 +396,25 @@ func TestDynamicFiltering(t *testing.T) { } nextToken = pageResp.GetNextToken() + }) ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { + dec, err := base64.StdEncoding.DecodeString(nextToken) + if err != nil { + t.Fatalf("failed to decode next token: %v", err) + } + t.Log(string(dec)) req := &test_spb.FooListRequest{ Page: &list_j5pb.PageRequest{ PageSize: proto.Int64(10), Token: &nextToken, }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Or{ - Or: &list_j5pb.Or{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.characteristics.weight", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Min: "12", - Max: "20", - }, - }, - }, - }, - }, - }, - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.characteristics.height", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Min: "16", - Max: "18", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, + Query: query, } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -498,7 +464,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err) } @@ -540,7 +506,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err == nil { t.Fatalf("expected error, got nil") } @@ -561,7 +527,7 @@ func TestDynamicFiltering(t *testing.T) { Name: "status", Type: &list_j5pb.FieldType{ Type: &list_j5pb.FieldType_Value{ - Value: "active", + Value: "ACTIVE", }, }, }, @@ -572,7 +538,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err := queryer.List(ctx, db, req, res) + err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err) } @@ -616,7 +582,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err := queryer.List(ctx, db, req, res) + err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err) } @@ -660,7 +626,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err := queryer.List(ctx, db, req, res) + err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err == nil { t.Fatal("expected error, got nil") } @@ -696,7 +662,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -763,7 +729,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -804,7 +770,15 @@ func TestDynamicFiltering(t *testing.T) { { Type: &list_j5pb.Filter_Field{ Field: &list_j5pb.Field{ - Name: "event.type", + // match the JSON encoding + // { + // "event": { + // "!type": "created" + // "created": {...} + // } + // ... + // } + Name: "event.!type", Type: &list_j5pb.FieldType{ Type: &list_j5pb.FieldType_Value{ Value: "created", @@ -818,7 +792,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooEventsResponse{} - err := queryer.EventLister.List(ctx, db, req, res) + err := queryer.EventLister.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err) } @@ -863,7 +837,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooEventsResponse{} - err := queryer.EventLister.List(ctx, db, req, res) + err := queryer.EventLister.List(ctx, db, req.J5Object(), res.J5Object()) if err == nil { t.Fatal("expected error, got nil") } diff --git a/internal/integration/query_test/helpers.go b/internal/integration/query_test/helpers.go index c71e087..56b9297 100644 --- a/internal/integration/query_test/helpers.go +++ b/internal/integration/query_test/helpers.go @@ -26,7 +26,7 @@ func silenceLogger() func() { } } -func printQuery(t flowtest.TB, query *sqrl.SelectBuilder) { +func printQuery(t flowtest.TB, query sqrl.Sqlizer) { stmt, args, err := query.ToSql() if err != nil { t.Fatal(err.Error()) diff --git a/internal/integration/query_test/marshalling_test.go b/internal/integration/query_test/marshalling_test.go index 9f009cb..f360099 100644 --- a/internal/integration/query_test/marshalling_test.go +++ b/internal/integration/query_test/marshalling_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/google/uuid" + "github.com/pentops/golib/gl" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" "k8s.io/utils/ptr" @@ -39,7 +40,7 @@ func TestMarshaling(t *testing.T) { res := &test_spb.FooGetResponse{} - err = queryer.Get(ctx, db, req, res) + err = queryer.Get(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -61,6 +62,11 @@ func TestMarshaling(t *testing.T) { t.Fatalf("expected description to be present, but empty: %s", stateJSON) } + t.Log("Event Count:", len(res.Events)) + if len(res.Events) == 0 { + t.Fatal("expected at least one event") + } + eventJSON, err := getRawEvent(db, res.Events[len(res.Events)-1].Metadata.EventId) if err != nil { t.Fatal(err.Error()) @@ -87,7 +93,7 @@ func TestMarshaling(t *testing.T) { res := &test_spb.FooGetResponse{} - err = queryer.Get(ctx, db, req, res) + err = queryer.Get(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -109,6 +115,11 @@ func TestMarshaling(t *testing.T) { t.Fatalf("expected description to be present, and not empty: %s", stateJSON) } + t.Log("Event Count:", len(res.Events)) + if len(res.Events) == 0 { + t.Fatal("expected at least one event") + } + eventJSON, err := getRawEvent(db, res.Events[len(res.Events)-1].Metadata.EventId) if err != nil { t.Fatal(err.Error()) @@ -136,7 +147,7 @@ func TestMarshaling(t *testing.T) { res := &test_spb.FooGetResponse{} - err = queryer.Get(ctx, db, req, res) + err = queryer.Get(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -150,8 +161,8 @@ func TestMarshaling(t *testing.T) { t.Fatal(err.Error()) } - if strings.Contains(stateJSON, `"description":`) { - t.Fatalf("expected description to not be present: %s", stateJSON) + if !strings.Contains(stateJSON, `"description": null`) { + t.Fatalf("expected 'description' to be present as null in state: %s", stateJSON) } eventJSON, err := getRawEvent(db, res.Events[len(res.Events)-1].Metadata.EventId) @@ -159,8 +170,8 @@ func TestMarshaling(t *testing.T) { t.Fatal(err.Error()) } - if strings.Contains(eventJSON, `"description":`) { - t.Fatalf("expected description to not be present: %s", eventJSON) + if !strings.Contains(eventJSON, `"description": null`) { + t.Fatalf("expected 'description' to be present as null in event: %s", stateJSON) } }) }) @@ -171,8 +182,7 @@ func TestMarshaling(t *testing.T) { t.Run("Get with empty", func(t *testing.T) { event := newFooCreatedEvent(fooID, tenantID, func(c *test_pb.FooEventType_Created) { - c.Field = "" - c.Description = nil + c.Description = gl.Ptr("") }) _, err := sm.Transition(ctx, event) @@ -186,13 +196,13 @@ func TestMarshaling(t *testing.T) { res := &test_spb.FooGetResponse{} - err = queryer.Get(ctx, db, req, res) + err = queryer.Get(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } - if res.Foo.Data.Field != "" { - t.Fatalf("expected description to be empty") + if res.Foo.Data.Description == nil && *res.Foo.Data.Description != "" { + t.Errorf("expected description to be not nill but empty") } stateJSON, err := getRawState(db, fooID) @@ -200,8 +210,8 @@ func TestMarshaling(t *testing.T) { t.Fatal(err.Error()) } - if !strings.Contains(stateJSON, `"field": ""`) { - t.Fatalf("expected field to be present, but empty: %s", stateJSON) + if !strings.Contains(stateJSON, `"description": ""`) { + t.Fatalf("expected 'description' to be present as empty in: %s", stateJSON) } eventJSON, err := getRawEvent(db, res.Events[len(res.Events)-1].Metadata.EventId) @@ -209,8 +219,8 @@ func TestMarshaling(t *testing.T) { t.Fatal(err.Error()) } - if !strings.Contains(eventJSON, `"field": ""`) { - t.Fatalf("expected field to be present, but empty: %s", eventJSON) + if !strings.Contains(eventJSON, `"description": ""`) { + t.Fatalf("expected 'description' to be present, but empty, in: %s", eventJSON) } }) @@ -232,7 +242,7 @@ func TestMarshaling(t *testing.T) { res := &test_spb.FooGetResponse{} - err = queryer.Get(ctx, db, req, res) + err = queryer.Get(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -256,7 +266,7 @@ func TestMarshaling(t *testing.T) { } if !strings.Contains(eventJSON, `"field":`) { - t.Fatalf("expected field to be present: %s", eventJSON) + t.Fatalf("expected 'field' to be present in: %s", eventJSON) } }) }) diff --git a/internal/integration/query_test/pagination_test.go b/internal/integration/query_test/pagination_test.go index 4a79c00..deaa8ac 100644 --- a/internal/integration/query_test/pagination_test.go +++ b/internal/integration/query_test/pagination_test.go @@ -2,7 +2,6 @@ package integration import ( "context" - "encoding/base64" "fmt" "testing" "time" @@ -13,8 +12,6 @@ import ( "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" "github.com/pentops/protostate/psm" - "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/proto" "k8s.io/utils/ptr" ) @@ -83,13 +80,13 @@ func TestPagination(t *testing.T) { } res := &test_spb.FooListResponse{} - query, err := queryer.MainLister.BuildQuery(ctx, req.ProtoReflect(), res.ProtoReflect()) + query, err := queryer.MainLister.BuildQuery(ctx, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } printQuery(t, query) - err = queryer.List(ctx, uu.DB, req, res) + err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -152,7 +149,7 @@ func TestEventPagination(t *testing.T) { } res := &test_spb.FooEventsResponse{} - err = queryer.ListEvents(ctx, db, req, res) + err = queryer.ListEvents(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -182,19 +179,6 @@ func TestEventPagination(t *testing.T) { t.Fatalf("Should not be the final page") } - rowBytes, err := base64.StdEncoding.DecodeString(*pageResp.NextToken) - if err != nil { - t.Fatal(err.Error()) - } - - msg := &test_pb.FooEvent{} - if err := proto.Unmarshal(rowBytes, msg); err != nil { - t.Fatal(err.Error()) - } - - t.Log(protojson.Format(msg)) - t.Logf("Token entry, TS: %d, ID: %s", msg.Metadata.Timestamp.AsTime().Round(time.Microsecond).UnixMicro(), msg.Metadata.EventId) - }) ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { @@ -207,13 +191,13 @@ func TestEventPagination(t *testing.T) { } res := &test_spb.FooEventsResponse{} - query, err := queryer.EventLister.BuildQuery(ctx, req.ProtoReflect(), res.ProtoReflect()) + query, err := queryer.EventLister.BuildQuery(ctx, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } printQuery(t, query) - err = queryer.ListEvents(ctx, db, req, res) + err = queryer.ListEvents(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -278,7 +262,7 @@ func TestPageSize(t *testing.T) { req := &test_spb.FooListRequest{} res := &test_spb.FooListResponse{} - err := queryer.List(ctx, db, req, res) + err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -310,7 +294,7 @@ func TestPageSize(t *testing.T) { } res := &test_spb.FooListResponse{} - err := queryer.List(ctx, db, req, res) + err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -342,7 +326,7 @@ func TestPageSize(t *testing.T) { } res := &test_spb.FooListResponse{} - err := queryer.List(ctx, db, req, res) + err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err == nil { t.Fatal("expected error") } diff --git a/internal/integration/query_test/search_test.go b/internal/integration/query_test/search_test.go index da63b3e..420ef4e 100644 --- a/internal/integration/query_test/search_test.go +++ b/internal/integration/query_test/search_test.go @@ -38,7 +38,7 @@ func TestDynamicSearching(t *testing.T) { } res := &test_spb.FooListResponse{} - err := queryer.List(ctx, db, req, res) + err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -91,7 +91,7 @@ func TestDynamicSearching(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -156,7 +156,7 @@ func TestDynamicSearching(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } diff --git a/internal/integration/query_test/setup.go b/internal/integration/query_test/setup.go index 01486d4..224e0d1 100644 --- a/internal/integration/query_test/setup.go +++ b/internal/integration/query_test/setup.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/google/uuid" + "github.com/pentops/golib/gl" "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" "k8s.io/utils/ptr" @@ -67,7 +68,7 @@ func newFooCreatedEvent(fooID, tenantID string, mod ...func(c *test_pb.FooEventT created := &test_pb.FooEventType_Created{ Name: "foo", Field: fmt.Sprintf("weight: %d", weight), - Description: ptr.To("creation event for foo: " + fooID), + Description: gl.Ptr("creation event for foo: " + fooID), Weight: &weight, } diff --git a/internal/integration/query_test/sort_test.go b/internal/integration/query_test/sort_test.go index bfbac19..cac4904 100644 --- a/internal/integration/query_test/sort_test.go +++ b/internal/integration/query_test/sort_test.go @@ -2,6 +2,7 @@ package integration import ( "context" + "encoding/base64" "testing" "github.com/google/uuid" @@ -50,7 +51,7 @@ func TestSortingWithAuthScope(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -88,6 +89,12 @@ func TestSortingWithAuthScope(t *testing.T) { ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { ctx = tkn.WithToken(ctx) + pageTokenBytes, err := base64.StdEncoding.DecodeString(nextToken) + if err != nil { + t.Fatalf("failed to decode next token: %v", err) + } + t.Logf("Page Token: %s", string(pageTokenBytes)) + req := &test_spb.FooListRequest{ Page: &list_j5pb.PageRequest{ PageSize: proto.Int64(5), @@ -101,7 +108,7 @@ func TestSortingWithAuthScope(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -164,7 +171,7 @@ func TestSortingWithAuthNoScope(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -211,7 +218,7 @@ func TestSortingWithAuthNoScope(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -242,17 +249,17 @@ func TestSortingWithAuthNoScope(t *testing.T) { } func TestDynamicSorting(t *testing.T) { - ss, uu := NewFooUniverse(t) - sm := uu.SM - db := uu.DB - queryer := uu.Query - var err error - defer ss.RunSteps(t) - - tenants := []string{uuid.NewString()} - setupFooListableData(ss, sm, tenants, 30) t.Run("Top Level Field", func(t *testing.T) { + ss, uu := NewFooUniverse(t) + sm := uu.SM + db := uu.DB + queryer := uu.Query + var err error + defer ss.RunSteps(t) + + tenants := []string{uuid.NewString()} + setupFooListableData(ss, sm, tenants, 30) nextToken := "" ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { req := &test_spb.FooListRequest{ @@ -267,7 +274,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -312,7 +319,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -343,6 +350,15 @@ func TestDynamicSorting(t *testing.T) { }) t.Run("Nested Field", func(t *testing.T) { + ss, uu := NewFooUniverse(t) + sm := uu.SM + db := uu.DB + queryer := uu.Query + var err error + defer ss.RunSteps(t) + + tenants := []string{uuid.NewString()} + setupFooListableData(ss, sm, tenants, 30) nextToken := "" ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { req := &test_spb.FooListRequest{ @@ -357,7 +373,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -402,7 +418,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -433,6 +449,15 @@ func TestDynamicSorting(t *testing.T) { }) t.Run("Multiple Nested Fields", func(t *testing.T) { + ss, uu := NewFooUniverse(t) + sm := uu.SM + db := uu.DB + queryer := uu.Query + var err error + defer ss.RunSteps(t) + + tenants := []string{uuid.NewString()} + setupFooListableData(ss, sm, tenants, 30) nextToken := "" ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { req := &test_spb.FooListRequest{ @@ -448,7 +473,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -484,6 +509,12 @@ func TestDynamicSorting(t *testing.T) { }) ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { + + pageTokenBytes, err := base64.StdEncoding.DecodeString(nextToken) + if err != nil { + t.Fatalf("failed to decode next token: %v", err) + } + t.Logf("Page Token: %s", string(pageTokenBytes)) req := &test_spb.FooListRequest{ Page: &list_j5pb.PageRequest{ PageSize: proto.Int64(5), @@ -498,7 +529,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -533,6 +564,15 @@ func TestDynamicSorting(t *testing.T) { }) t.Run("Descending", func(t *testing.T) { + ss, uu := NewFooUniverse(t) + sm := uu.SM + db := uu.DB + queryer := uu.Query + var err error + defer ss.RunSteps(t) + + tenants := []string{uuid.NewString()} + setupFooListableData(ss, sm, tenants, 30) nextToken := "" ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { req := &test_spb.FooListRequest{ @@ -550,7 +590,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -598,7 +638,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req, res) + err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } diff --git a/internal/integration/query_test/universe.go b/internal/integration/query_test/universe.go index 09bcc96..84ea322 100644 --- a/internal/integration/query_test/universe.go +++ b/internal/integration/query_test/universe.go @@ -86,7 +86,7 @@ func (uu *Universe) ListFoo(t flowtest.Asserter, req *test_spb.FooListRequest) * ctx := context.Background() resp := &test_spb.FooListResponse{} - err := uu.Query.List(ctx, uu.DB, req, resp) + err := uu.Query.List(ctx, uu.DB, req.J5Object(), resp.J5Object()) if err != nil { t.Fatal(err.Error()) } diff --git a/internal/pgstore/pgmigrate/psm.go b/internal/pgstore/pgmigrate/psm.go index 3cda056..50008fd 100644 --- a/internal/pgstore/pgmigrate/psm.go +++ b/internal/pgstore/pgmigrate/psm.go @@ -6,14 +6,11 @@ import ( "strings" sq "github.com/elgris/sqrl" - "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" - "github.com/pentops/protostate/internal/pgstore" + "github.com/pentops/j5/lib/j5schema" + "github.com/pentops/protostate/pquery" "github.com/pentops/protostate/psm" "github.com/pentops/sqrlx.go/sqrlx" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" - "google.golang.org/protobuf/types/descriptorpb" ) func BuildStateMachineMigrations(specs ...psm.QueryTableSpec) ([]byte, error) { @@ -92,7 +89,7 @@ type searchSpec struct { tsvColumn string tableName string columnName string - path pgstore.Path + path pquery.Path } func (ss searchSpec) ToSQL() (string, error) { @@ -131,39 +128,23 @@ func AddIndexes(ctx context.Context, conn sqrlx.Connection, specs ...psm.QueryTa return writeIndexes(ctx, conn, allIndexes) } -func buildIndexes(tableName string, columnName string, rootType protoreflect.MessageDescriptor) ([]searchSpec, error) { +func buildIndexes(tableName string, columnName string, rootType *j5schema.ObjectSchema) ([]searchSpec, error) { - specs := []searchSpec{} - - if err := pgstore.WalkPathNodes(rootType, func(node pgstore.Path) error { - field := node.LeafField() - if field == nil || field.Kind() != protoreflect.StringKind { - return nil - } - - fieldOpts, ok := proto.GetExtension(field.Options().(*descriptorpb.FieldOptions), list_j5pb.E_Field).(*list_j5pb.FieldConstraint) - if !ok { - return nil - } + cols, err := pquery.TSVColumns(rootType) + if err != nil { + return nil, err + } - switch fieldOpts.GetString_().GetWellKnown().(type) { - case *list_j5pb.StringRules_OpenText: - searchOpts := fieldOpts.GetString_().GetOpenText().GetSearching() - if searchOpts == nil || !searchOpts.Searchable { - return nil - } + specs := []searchSpec{} - specs = append(specs, searchSpec{ - tsvColumn: searchOpts.GetFieldIdentifier(), - tableName: tableName, - columnName: columnName, - path: node, - }) - } + for _, col := range cols { + specs = append(specs, searchSpec{ + tsvColumn: col.ColumnName, + tableName: tableName, + columnName: columnName, + path: col.Path, + }) - return nil - }); err != nil { - return nil, err } return specs, nil diff --git a/internal/pgstore/pgmigrate/psm_test.go b/internal/pgstore/pgmigrate/psm_test.go index 33947af..ea715c8 100644 --- a/internal/pgstore/pgmigrate/psm_test.go +++ b/internal/pgstore/pgmigrate/psm_test.go @@ -10,16 +10,16 @@ import ( func TestBuildStateMachineOneKey(t *testing.T) { fooSpec, err := psm.BuildQueryTableSpec( - (&test_pb.FooState{}).ProtoReflect().Descriptor(), - (&test_pb.FooEvent{}).ProtoReflect().Descriptor(), + (&test_pb.FooState{}).J5Object().ObjectSchema(), + (&test_pb.FooEvent{}).J5Object().ObjectSchema(), ) if err != nil { t.Fatal(err) } barSpec, err := psm.BuildQueryTableSpec( - (&test_pb.BarState{}).ProtoReflect().Descriptor(), - (&test_pb.BarEvent{}).ProtoReflect().Descriptor(), + (&test_pb.BarState{}).J5Object().ObjectSchema(), + (&test_pb.BarEvent{}).J5Object().ObjectSchema(), ) if err != nil { t.Fatal(err) diff --git a/internal/protogen/query/code.go b/internal/protogen/query/code.go index e4ee24a..22e606a 100644 --- a/internal/protogen/query/code.go +++ b/internal/protogen/query/code.go @@ -7,10 +7,13 @@ import ( ) var ( - protoPackage = protogen.GoImportPath("google.golang.org/protobuf/proto") + //protoPackage = protogen.GoImportPath("google.golang.org/protobuf/proto") stateMachinePackage = protogen.GoImportPath("github.com/pentops/protostate/psm") sqrlxPkg = protogen.GoImportPath("github.com/pentops/sqrlx.go/sqrlx") + j5ReflectPackage = protogen.GoImportPath("github.com/pentops/j5/lib/j5reflect") + j5SchemaPackage = protogen.GoImportPath("github.com/pentops/j5/lib/j5schema") contextPkg = protogen.GoImportPath("context") + fmtPackage = protogen.GoImportPath("fmt") ) func quoteString(s string) string { @@ -57,24 +60,30 @@ func (qs PSMQuerySet) Write(g *protogen.GeneratedFile) { func (qs PSMQuerySet) writeQuerySet(g *protogen.GeneratedFile) { qs.genericTypeAlias(g, qs.GoName+"PSMQuerySet", "StateQuerySet") g.P("func New", qs.GoName, "PSMQuerySet(") - g.P("smSpec ", stateMachinePackage.Ident("QuerySpec"), "[") - qs.writeGenericTypeSet(g) - g.P("],") + g.P("smSpec ", stateMachinePackage.Ident("QuerySpec"), ",") g.P("options ", stateMachinePackage.Ident("StateQueryOptions"), ",") g.P(") (*", qs.GoName, "PSMQuerySet, error) {") - g.P("return ", stateMachinePackage.Ident("BuildStateQuerySet"), "[") - qs.writeGenericTypeSet(g) - g.P("](smSpec, options)") + g.P("return ", stateMachinePackage.Ident("BuildStateQuerySet"), "(smSpec, options)") g.P("}") } +func j5Method(g *protogen.GeneratedFile, m protogen.Method, name string) { + g.P(" ", name, ": &", j5SchemaPackage.Ident("MethodSchema"), "{") + g.P(" Request: ", j5SchemaPackage.Ident("MustObjectSchema"), "((&", m.Input.GoIdent, "{}).ProtoReflect().Descriptor()),") + g.P(" Response: ", j5SchemaPackage.Ident("MustObjectSchema"), "((&", m.Output.GoIdent, "{}).ProtoReflect().Descriptor()),") + g.P(" },") +} + func (qs PSMQuerySet) writeQuerySpec(g *protogen.GeneratedFile) { qs.genericTypeAlias(g, qs.GoName+"PSMQuerySpec", "QuerySpec") g.P() g.P("func Default", qs.GoName, "PSMQuerySpec(tableSpec ", stateMachinePackage.Ident("QueryTableSpec"), ") ", qs.GoName, "PSMQuerySpec {") - g.P(" return ", stateMachinePackage.Ident("QuerySpec"), "[") - qs.writeGenericTypeSet(g) - g.P(" ]{") + g.P(" return ", stateMachinePackage.Ident("QuerySpec"), "{") + j5Method(g, qs.GetMethod, "GetMethod") + j5Method(g, qs.ListMethod, "ListMethod") + if qs.ListEventsMethod != nil { + j5Method(g, *qs.ListEventsMethod, "ListEventsMethod") + } g.P(" QueryTableSpec: tableSpec,") qs.listFilter(g, qs.ListREQ, "ListRequestFilter", qs.ListRequestFilter) qs.listFilter(g, *qs.ListEventsREQ, "ListEventsRequestFilter", qs.ListEventsRequestFilter) @@ -102,7 +111,7 @@ func (qs PSMQuerySet) writeDefaultService(g *protogen.GeneratedFile) { g.P() g.P("func (s *", serviceName, ") ", qs.GetMethod.GoName, "(ctx ", contextPkg.Ident("Context"), ", req *", qs.GetREQ.GoName, ") (*", qs.GetRES.GoName, ", error) {") g.P(" resObject := &", qs.GetRES.GoName, "{}") - g.P(" err := s.querySet.Get(ctx, s.db, req, resObject)") + g.P(" err := s.querySet.Get(ctx, s.db, req.J5Object(), resObject.J5Object())") g.P(" if err != nil {") g.P(" return nil, err") g.P(" }") @@ -111,7 +120,7 @@ func (qs PSMQuerySet) writeDefaultService(g *protogen.GeneratedFile) { g.P() g.P("func (s *", serviceName, ") ", qs.ListMethod.GoName, "(ctx ", contextPkg.Ident("Context"), ", req *", qs.ListREQ.GoName, ") (*", qs.ListRES.GoName, ", error) {") g.P(" resObject := &", qs.ListRES.GoName, "{}") - g.P(" err := s.querySet.List(ctx, s.db, req, resObject)") + g.P(" err := s.querySet.List(ctx, s.db, req.J5Object(), resObject.J5Object())") g.P(" if err != nil {") g.P(" return nil, err") g.P(" }") @@ -123,7 +132,7 @@ func (qs PSMQuerySet) writeDefaultService(g *protogen.GeneratedFile) { g.P() g.P("func (s *", serviceName, ") ", qs.ListEventsMethod.GoName, "(ctx ", contextPkg.Ident("Context"), ", req *", *qs.ListEventsREQ, ") (*", *qs.ListEventsRES, ", error) {") g.P(" resObject := &", *qs.ListEventsRES, "{}") - g.P(" err := s.querySet.ListEvents(ctx, s.db, req, resObject)") + g.P(" err := s.querySet.ListEvents(ctx, s.db, req.J5Object(), resObject.J5Object())") g.P(" if err != nil {") g.P(" return nil, err") g.P(" }") @@ -131,6 +140,7 @@ func (qs PSMQuerySet) writeDefaultService(g *protogen.GeneratedFile) { g.P("}") } +/* func (qs PSMQuerySet) writeGenericTypeSet(g *protogen.GeneratedFile) { g.P("*", qs.GetREQ, ",") g.P("*", qs.GetRES, ",") @@ -144,17 +154,23 @@ func (qs PSMQuerySet) writeGenericTypeSet(g *protogen.GeneratedFile) { g.P(protoPackage.Ident("Message"), ",") } } +*/ func (qs PSMQuerySet) genericTypeAlias(g *protogen.GeneratedFile, typedName string, psmName string) { - g.P("type ", typedName, " = ", stateMachinePackage.Ident(psmName), "[") + g.P("type ", typedName, " = ", stateMachinePackage.Ident(psmName)) + /*"[") qs.writeGenericTypeSet(g) g.P("]") - g.P() + g.P()*/ } func (qs PSMQuerySet) listFilter(g *protogen.GeneratedFile, reqType protogen.GoIdent, name string, fields []ListFilterField) { // ListRequestFilter func(ListREQ) (map[string]interface{}, error) - g.P(name, ": func(req *", reqType, ") (map[string]interface{}, error) {") + g.P(name, ": func(reqReflect ", j5ReflectPackage.Ident("Object"), ") (map[string]interface{}, error) {") + g.P(" req, ok := reqReflect.Interface().(*", reqType, ")") + g.P(" if !ok {") + g.P(" return nil, ", fmtPackage.Ident("Errorf"), "(\"expected *", reqType, " but got %T\", req)") + g.P(" }") g.P(" filter := map[string]interface{}{}") for _, field := range fields { if field.Optional { diff --git a/internal/protogen/query/parse.go b/internal/protogen/query/parse.go index df7d7e5..bbc8283 100644 --- a/internal/protogen/query/parse.go +++ b/internal/protogen/query/parse.go @@ -7,6 +7,7 @@ import ( "github.com/iancoleman/strcase" "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" + "github.com/pentops/j5/lib/j5schema" "github.com/pentops/protostate/pquery" "github.com/pentops/protostate/psm" "google.golang.org/protobuf/compiler/protogen" @@ -133,6 +134,29 @@ func (qs QueryServiceGenerateSet) validate() error { return nil } +func fieldByDesc(fields []*protogen.Field, jsonName string) *protogen.Field { + for _, f := range fields { + if f.Desc.JSONName() == jsonName { + return f + } + } + return nil +} + +func methodPair(method *protogen.Method) (*j5schema.MethodSchema, error) { + reqObj, err := j5schema.Global.ObjectSchema(method.Input.Desc) + if err != nil { + return nil, fmt.Errorf("j5schema.ObjectSchema for %s: %w", method.Desc.FullName(), err) + } + resObj, err := j5schema.Global.ObjectSchema(method.Output.Desc) + if err != nil { + return nil, fmt.Errorf("j5schema.ObjectSchema for %s: %w", method.Desc.FullName(), err) + } + return &j5schema.MethodSchema{ + Request: reqObj, + Response: resObj, + }, nil +} func BuildQuerySet(qs QueryServiceGenerateSet) (*PSMQuerySet, error) { if err := qs.validate(); err != nil { @@ -164,8 +188,13 @@ func BuildQuerySet(qs QueryServiceGenerateSet) (*PSMQuerySet, error) { return nil, errors.Join(errs...) } + listMethod, err := methodPair(qs.listMethod) + if err != nil { + return nil, fmt.Errorf("building list method pair for %s: %w", qs.listMethod.Desc.FullName(), err) + } + // Empty table spec, the fields don't matter here. - listReflectionSet, err := pquery.BuildListReflection(qs.listMethod.Input.Desc, qs.listMethod.Output.Desc, pquery.TableSpec{}) + listReflectionSet, err := pquery.BuildListReflection(listMethod, pquery.TableSpec{}) if err != nil { return nil, fmt.Errorf("pquery.BuildListReflection for %s: %w", qs.listMethod.Desc.FullName(), err) } @@ -183,7 +212,8 @@ func BuildQuerySet(qs QueryServiceGenerateSet) (*PSMQuerySet, error) { GetMethod: *qs.getMethod, } - for _, field := range listReflectionSet.RequestFilterFields { + for _, fieldSpec := range listReflectionSet.RequestFilterFields { + field := fieldByDesc(qs.listMethod.Input.Fields, fieldSpec.JSONName).Desc genField := mapGenField(qs.listMethod.Input, field) ww.ListRequestFilter = append(ww.ListRequestFilter, ListFilterField{ DBName: string(field.Name()), @@ -192,7 +222,12 @@ func BuildQuerySet(qs QueryServiceGenerateSet) (*PSMQuerySet, error) { }) } - listEventsReflectionSet, err := pquery.BuildListReflection(qs.listEventsMethod.Input.Desc, qs.listEventsMethod.Output.Desc, pquery.TableSpec{}) + listEventsMethod, err := methodPair(qs.listEventsMethod) + if err != nil { + return nil, fmt.Errorf("building list events method pair for %s: %w", qs.listEventsMethod.Desc.FullName(), err) + } + + listEventsReflectionSet, err := pquery.BuildListReflection(listEventsMethod, pquery.TableSpec{}) if err != nil { return nil, fmt.Errorf("pquery.BuildListReflection for %s is not compatible with PSM: %w", qs.listEventsMethod.Desc.FullName(), err) } @@ -201,11 +236,11 @@ func BuildQuerySet(qs QueryServiceGenerateSet) (*PSMQuerySet, error) { ww.ListEventsRES = &qs.listEventsMethod.Output.GoIdent ww.ListEventsMethod = qs.listEventsMethod for _, field := range listEventsReflectionSet.RequestFilterFields { - genField := mapGenField(qs.listEventsMethod.Input, field) + genField := mapJSONField(qs.listEventsMethod.Input, field.JSONName) ww.ListEventsRequestFilter = append(ww.ListEventsRequestFilter, ListFilterField{ - DBName: string(field.Name()), + DBName: string(genField.Desc.Name()), Getter: genField.GoName, - Optional: field.HasOptionalKeyword(), + Optional: genField.Desc.HasOptionalKeyword(), }) } @@ -267,7 +302,16 @@ func deriveStateDescriptorFromQueryDescriptor(src QueryServiceGenerateSet) (*psm } } - spec, err := psm.BuildQueryTableSpec(stateMessage, eventMessage) + stateObject, err := j5schema.Global.ObjectSchema(stateMessage) + if err != nil { + return nil, fmt.Errorf("j5schema.ObjectSchema for %s: %w", stateMessage.FullName(), err) + } + eventObject, err := j5schema.Global.ObjectSchema(eventMessage) + if err != nil { + return nil, fmt.Errorf("j5schema.ObjectSchema for %s: %w", eventMessage.FullName(), err) + } + + spec, err := psm.BuildQueryTableSpec(stateObject, eventObject) if err != nil { return nil, err } @@ -286,3 +330,12 @@ func mapGenField(parent *protogen.Message, field protoreflect.FieldDescriptor) * } panic(fmt.Sprintf("field %s not found in parent %s", field.FullName(), parent.Desc.FullName())) } + +func mapJSONField(parent *protogen.Message, field string) *protogen.Field { + for _, f := range parent.Fields { + if f.Desc.JSONName() == field { + return f + } + } + panic(fmt.Sprintf("field %s not found in parent %s", field, parent.Desc.FullName())) +} diff --git a/internal/protogen/state/code.go b/internal/protogen/state/code.go index 6fafc15..21ec31d 100644 --- a/internal/protogen/state/code.go +++ b/internal/protogen/state/code.go @@ -8,7 +8,6 @@ import ( "github.com/pentops/protostate/psm" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" ) var ( @@ -155,7 +154,7 @@ func (ss PSMEntity) implementIKeyset(g *protogen.GeneratedFile) { if !columnSpec.Primary { continue } - field := fieldByDesc(ss.keyMessage.Fields, columnSpec.ProtoName) + field := fieldByDesc(ss.keyMessage.Fields, columnSpec.JSONFieldName) g.P(" \"", columnSpec.ColumnName, "\": msg.", field.GoName, ",") } g.P(" }") @@ -163,7 +162,7 @@ func (ss PSMEntity) implementIKeyset(g *protogen.GeneratedFile) { if columnSpec.Primary { continue } - field := fieldByDesc(ss.keyMessage.Fields, columnSpec.ProtoName) + field := fieldByDesc(ss.keyMessage.Fields, columnSpec.JSONFieldName) if columnSpec.ExplicitlyOptional { g.P("if msg.", field.GoName, " != nil {") g.P(" keyset[\"", columnSpec.ColumnName, "\"] = *msg.", field.GoName) @@ -357,9 +356,9 @@ func (ss PSMEntity) tableSpecAndConfig(g *protogen.GeneratedFile) { g.P() } -func fieldByDesc(fields []*protogen.Field, desc protoreflect.Name) *protogen.Field { +func fieldByDesc(fields []*protogen.Field, jsonName string) *protogen.Field { for _, f := range fields { - if f.Desc.Name() == desc { + if f.Desc.JSONName() == jsonName { return f } } diff --git a/internal/protogen/state/parse.go b/internal/protogen/state/parse.go index 3c20553..d2212a5 100644 --- a/internal/protogen/state/parse.go +++ b/internal/protogen/state/parse.go @@ -6,6 +6,7 @@ import ( "github.com/iancoleman/strcase" "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" + "github.com/pentops/j5/lib/j5schema" "github.com/pentops/protostate/psm" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/proto" @@ -274,7 +275,16 @@ func BuildStateSet(src StateEntityGenerateSet) (*PSMEntity, error) { keyMessage: src.keyMessage, } - spec, err := psm.BuildQueryTableSpec(src.state.message.Desc, src.event.message.Desc) + state, err := j5schema.Global.ObjectSchema(src.state.message.Desc) + if err != nil { + return nil, fmt.Errorf("state object %s: %w", src.options.EntityName, err) + } + event, err := j5schema.Global.ObjectSchema(src.event.message.Desc) + if err != nil { + return nil, fmt.Errorf("event object %s: %w", src.options.EntityName, err) + } + + spec, err := psm.BuildQueryTableSpec(state, event) if err != nil { return nil, err } diff --git a/internal/testproto/gen/test/v1/test_pb/bar.j5s.pb.go b/internal/testproto/gen/test/v1/test_pb/bar.j5s.pb.go new file mode 100644 index 0000000..8319813 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_pb/bar.j5s.pb.go @@ -0,0 +1,868 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc (unknown) +// source: test/v1/bar.j5s.proto + +package test_pb + +import ( + _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" + _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" + _ "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" + psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" + date_j5t "github.com/pentops/j5/j5types/date_j5t" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type BarStatus int32 + +const ( + BarStatus_BAR_STATUS_UNSPECIFIED BarStatus = 0 + BarStatus_BAR_STATUS_ACTIVE BarStatus = 1 + BarStatus_BAR_STATUS_DELETED BarStatus = 2 +) + +// Enum value maps for BarStatus. +var ( + BarStatus_name = map[int32]string{ + 0: "BAR_STATUS_UNSPECIFIED", + 1: "BAR_STATUS_ACTIVE", + 2: "BAR_STATUS_DELETED", + } + BarStatus_value = map[string]int32{ + "BAR_STATUS_UNSPECIFIED": 0, + "BAR_STATUS_ACTIVE": 1, + "BAR_STATUS_DELETED": 2, + } +) + +func (x BarStatus) Enum() *BarStatus { + p := new(BarStatus) + *p = x + return p +} + +func (x BarStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (BarStatus) Descriptor() protoreflect.EnumDescriptor { + return file_test_v1_bar_j5s_proto_enumTypes[0].Descriptor() +} + +func (BarStatus) Type() protoreflect.EnumType { + return &file_test_v1_bar_j5s_proto_enumTypes[0] +} + +func (x BarStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use BarStatus.Descriptor instead. +func (BarStatus) EnumDescriptor() ([]byte, []int) { + return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{0} +} + +type BarKeys struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BarId string `protobuf:"bytes,1,opt,name=bar_id,json=barId,proto3" json:"bar_id,omitempty"` + BarOtherId string `protobuf:"bytes,2,opt,name=bar_other_id,json=barOtherId,proto3" json:"bar_other_id,omitempty"` + DateKey *date_j5t.Date `protobuf:"bytes,3,opt,name=date_key,json=dateKey,proto3" json:"date_key,omitempty"` +} + +func (x *BarKeys) Reset() { + *x = BarKeys{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_bar_j5s_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarKeys) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarKeys) ProtoMessage() {} + +func (x *BarKeys) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_bar_j5s_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarKeys.ProtoReflect.Descriptor instead. +func (*BarKeys) Descriptor() ([]byte, []int) { + return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{0} +} + +func (x *BarKeys) GetBarId() string { + if x != nil { + return x.BarId + } + return "" +} + +func (x *BarKeys) GetBarOtherId() string { + if x != nil { + return x.BarOtherId + } + return "" +} + +func (x *BarKeys) GetDateKey() *date_j5t.Date { + if x != nil { + return x.DateKey + } + return nil +} + +type BarData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + TenantId *string `protobuf:"bytes,1,opt,name=tenant_id,json=tenantId,proto3,oneof" json:"tenant_id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + Field string `protobuf:"bytes,3,opt,name=field,proto3" json:"field,omitempty"` +} + +func (x *BarData) Reset() { + *x = BarData{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_bar_j5s_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarData) ProtoMessage() {} + +func (x *BarData) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_bar_j5s_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarData.ProtoReflect.Descriptor instead. +func (*BarData) Descriptor() ([]byte, []int) { + return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{1} +} + +func (x *BarData) GetTenantId() string { + if x != nil && x.TenantId != nil { + return *x.TenantId + } + return "" +} + +func (x *BarData) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *BarData) GetField() string { + if x != nil { + return x.Field + } + return "" +} + +type BarState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Metadata *psm_j5pb.StateMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + Keys *BarKeys `protobuf:"bytes,2,opt,name=keys,proto3" json:"keys,omitempty"` + Data *BarData `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + Status BarStatus `protobuf:"varint,4,opt,name=status,proto3,enum=test.v1.BarStatus" json:"status,omitempty"` +} + +func (x *BarState) Reset() { + *x = BarState{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_bar_j5s_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarState) ProtoMessage() {} + +func (x *BarState) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_bar_j5s_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarState.ProtoReflect.Descriptor instead. +func (*BarState) Descriptor() ([]byte, []int) { + return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{2} +} + +func (x *BarState) GetMetadata() *psm_j5pb.StateMetadata { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *BarState) GetKeys() *BarKeys { + if x != nil { + return x.Keys + } + return nil +} + +func (x *BarState) GetData() *BarData { + if x != nil { + return x.Data + } + return nil +} + +func (x *BarState) GetStatus() BarStatus { + if x != nil { + return x.Status + } + return BarStatus_BAR_STATUS_UNSPECIFIED +} + +type BarEventType struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Type: + // + // *BarEventType_Created_ + // *BarEventType_Updated_ + // *BarEventType_Deleted_ + Type isBarEventType_Type `protobuf_oneof:"type"` +} + +func (x *BarEventType) Reset() { + *x = BarEventType{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_bar_j5s_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarEventType) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarEventType) ProtoMessage() {} + +func (x *BarEventType) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_bar_j5s_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarEventType.ProtoReflect.Descriptor instead. +func (*BarEventType) Descriptor() ([]byte, []int) { + return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{3} +} + +func (m *BarEventType) GetType() isBarEventType_Type { + if m != nil { + return m.Type + } + return nil +} + +func (x *BarEventType) GetCreated() *BarEventType_Created { + if x, ok := x.GetType().(*BarEventType_Created_); ok { + return x.Created + } + return nil +} + +func (x *BarEventType) GetUpdated() *BarEventType_Updated { + if x, ok := x.GetType().(*BarEventType_Updated_); ok { + return x.Updated + } + return nil +} + +func (x *BarEventType) GetDeleted() *BarEventType_Deleted { + if x, ok := x.GetType().(*BarEventType_Deleted_); ok { + return x.Deleted + } + return nil +} + +type isBarEventType_Type interface { + isBarEventType_Type() +} + +type BarEventType_Created_ struct { + Created *BarEventType_Created `protobuf:"bytes,1,opt,name=created,proto3,oneof"` +} + +type BarEventType_Updated_ struct { + Updated *BarEventType_Updated `protobuf:"bytes,2,opt,name=updated,proto3,oneof"` +} + +type BarEventType_Deleted_ struct { + Deleted *BarEventType_Deleted `protobuf:"bytes,3,opt,name=deleted,proto3,oneof"` +} + +func (*BarEventType_Created_) isBarEventType_Type() {} + +func (*BarEventType_Updated_) isBarEventType_Type() {} + +func (*BarEventType_Deleted_) isBarEventType_Type() {} + +type BarEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Metadata *psm_j5pb.EventMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + Keys *BarKeys `protobuf:"bytes,2,opt,name=keys,proto3" json:"keys,omitempty"` + Event *BarEventType `protobuf:"bytes,3,opt,name=event,proto3" json:"event,omitempty"` +} + +func (x *BarEvent) Reset() { + *x = BarEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_bar_j5s_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarEvent) ProtoMessage() {} + +func (x *BarEvent) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_bar_j5s_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarEvent.ProtoReflect.Descriptor instead. +func (*BarEvent) Descriptor() ([]byte, []int) { + return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{4} +} + +func (x *BarEvent) GetMetadata() *psm_j5pb.EventMetadata { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *BarEvent) GetKeys() *BarKeys { + if x != nil { + return x.Keys + } + return nil +} + +func (x *BarEvent) GetEvent() *BarEventType { + if x != nil { + return x.Event + } + return nil +} + +type BarEventType_Created struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Field string `protobuf:"bytes,2,opt,name=field,proto3" json:"field,omitempty"` +} + +func (x *BarEventType_Created) Reset() { + *x = BarEventType_Created{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_bar_j5s_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarEventType_Created) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarEventType_Created) ProtoMessage() {} + +func (x *BarEventType_Created) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_bar_j5s_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarEventType_Created.ProtoReflect.Descriptor instead. +func (*BarEventType_Created) Descriptor() ([]byte, []int) { + return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{3, 0} +} + +func (x *BarEventType_Created) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *BarEventType_Created) GetField() string { + if x != nil { + return x.Field + } + return "" +} + +type BarEventType_Updated struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Field string `protobuf:"bytes,2,opt,name=field,proto3" json:"field,omitempty"` +} + +func (x *BarEventType_Updated) Reset() { + *x = BarEventType_Updated{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_bar_j5s_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarEventType_Updated) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarEventType_Updated) ProtoMessage() {} + +func (x *BarEventType_Updated) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_bar_j5s_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarEventType_Updated.ProtoReflect.Descriptor instead. +func (*BarEventType_Updated) Descriptor() ([]byte, []int) { + return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{3, 1} +} + +func (x *BarEventType_Updated) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *BarEventType_Updated) GetField() string { + if x != nil { + return x.Field + } + return "" +} + +type BarEventType_Deleted struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *BarEventType_Deleted) Reset() { + *x = BarEventType_Deleted{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_bar_j5s_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarEventType_Deleted) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarEventType_Deleted) ProtoMessage() {} + +func (x *BarEventType_Deleted) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_bar_j5s_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarEventType_Deleted.ProtoReflect.Descriptor instead. +func (*BarEventType_Deleted) Descriptor() ([]byte, []int) { + return file_test_v1_bar_j5s_proto_rawDescGZIP(), []int{3, 2} +} + +var File_test_v1_bar_j5s_proto protoreflect.FileDescriptor + +var file_test_v1_bar_j5s_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x61, 0x72, 0x2e, 0x6a, 0x35, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, + 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, + 0x35, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6a, 0x35, 0x2f, 0x6c, + 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x6a, 0x35, 0x2f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, 0x35, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x64, + 0x61, 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0xd5, 0x01, 0x0a, 0x07, 0x42, 0x61, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x33, 0x0a, + 0x06, 0x62, 0x61, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1c, 0xba, + 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, + 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x05, 0x62, 0x61, 0x72, + 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0c, 0x62, 0x61, 0x72, 0x5f, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1c, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, + 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, + 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x0a, 0x62, 0x61, 0x72, 0x4f, 0x74, 0x68, 0x65, 0x72, + 0x49, 0x64, 0x12, 0x40, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6a, 0x35, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, + 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x65, 0x42, 0x0d, 0xba, 0x48, + 0x03, 0xc8, 0x01, 0x01, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x07, 0x64, 0x61, 0x74, + 0x65, 0x4b, 0x65, 0x79, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, + 0x02, 0x07, 0x0a, 0x03, 0x62, 0x61, 0x72, 0x10, 0x01, 0x22, 0xd1, 0x01, 0x0a, 0x07, 0x42, 0x61, + 0x72, 0x44, 0x61, 0x74, 0x61, 0x12, 0x34, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x12, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, + 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0x48, 0x00, 0x52, 0x08, + 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x34, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x20, 0xc2, 0xff, 0x8e, 0x02, 0x03, + 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x12, 0x72, 0x10, 0x0a, 0x0e, 0x52, 0x0c, 0x08, + 0x01, 0x12, 0x08, 0x74, 0x73, 0x76, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x37, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x21, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x13, + 0x72, 0x11, 0x0a, 0x0f, 0x52, 0x0d, 0x08, 0x01, 0x12, 0x09, 0x74, 0x73, 0x76, 0x5f, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, + 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x62, 0x61, 0x72, 0x10, 0x04, 0x42, + 0x0c, 0x0a, 0x0a, 0x5f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, 0x9f, 0x02, + 0x0a, 0x08, 0x42, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6a, + 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, + 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x12, 0x35, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x4b, 0x65, 0x79, + 0x73, 0x42, 0x0f, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, + 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, + 0x2e, 0x42, 0x61, 0x72, 0x44, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, + 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4b, 0x0a, + 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x42, 0x1f, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, 0x01, 0x02, 0x10, 0x01, 0xc2, 0xff, + 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x07, 0xa2, 0x01, 0x04, 0x52, 0x02, + 0x08, 0x01, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, + 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x62, 0x61, 0x72, 0x10, 0x02, 0x22, + 0xa3, 0x03, 0x0a, 0x0c, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x42, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x07, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, + 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, + 0x00, 0x48, 0x00, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x1a, 0x50, 0x0a, 0x07, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x05, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x1a, 0x50, + 0x0a, 0x07, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, + 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, + 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, + 0x1a, 0x12, 0x0a, 0x07, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x3a, 0x07, 0xc2, 0xff, 0x8e, + 0x02, 0x02, 0x52, 0x00, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x42, 0x06, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xe6, 0x01, 0x0a, 0x08, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x35, 0x0a, 0x04, 0x6b, 0x65, 0x79, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, + 0x31, 0x2e, 0x42, 0x61, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0f, 0xba, 0x48, 0x03, 0xc8, 0x01, + 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, + 0x12, 0x47, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x42, 0x1a, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, + 0x8e, 0x02, 0x02, 0x62, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x07, 0xaa, 0x01, 0x04, 0x52, 0x02, + 0x08, 0x01, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, + 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x62, 0x61, 0x72, 0x10, 0x03, 0x2a, 0x56, + 0x0a, 0x09, 0x42, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x42, + 0x41, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, + 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x42, 0x41, 0x52, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x16, + 0x0a, 0x12, 0x42, 0x41, 0x52, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x44, 0x45, 0x4c, + 0x45, 0x54, 0x45, 0x44, 0x10, 0x02, 0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, + 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x74, + 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x62, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_test_v1_bar_j5s_proto_rawDescOnce sync.Once + file_test_v1_bar_j5s_proto_rawDescData = file_test_v1_bar_j5s_proto_rawDesc +) + +func file_test_v1_bar_j5s_proto_rawDescGZIP() []byte { + file_test_v1_bar_j5s_proto_rawDescOnce.Do(func() { + file_test_v1_bar_j5s_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_v1_bar_j5s_proto_rawDescData) + }) + return file_test_v1_bar_j5s_proto_rawDescData +} + +var file_test_v1_bar_j5s_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_test_v1_bar_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_test_v1_bar_j5s_proto_goTypes = []interface{}{ + (BarStatus)(0), // 0: test.v1.BarStatus + (*BarKeys)(nil), // 1: test.v1.BarKeys + (*BarData)(nil), // 2: test.v1.BarData + (*BarState)(nil), // 3: test.v1.BarState + (*BarEventType)(nil), // 4: test.v1.BarEventType + (*BarEvent)(nil), // 5: test.v1.BarEvent + (*BarEventType_Created)(nil), // 6: test.v1.BarEventType.Created + (*BarEventType_Updated)(nil), // 7: test.v1.BarEventType.Updated + (*BarEventType_Deleted)(nil), // 8: test.v1.BarEventType.Deleted + (*date_j5t.Date)(nil), // 9: j5.types.date.v1.Date + (*psm_j5pb.StateMetadata)(nil), // 10: j5.state.v1.StateMetadata + (*psm_j5pb.EventMetadata)(nil), // 11: j5.state.v1.EventMetadata +} +var file_test_v1_bar_j5s_proto_depIdxs = []int32{ + 9, // 0: test.v1.BarKeys.date_key:type_name -> j5.types.date.v1.Date + 10, // 1: test.v1.BarState.metadata:type_name -> j5.state.v1.StateMetadata + 1, // 2: test.v1.BarState.keys:type_name -> test.v1.BarKeys + 2, // 3: test.v1.BarState.data:type_name -> test.v1.BarData + 0, // 4: test.v1.BarState.status:type_name -> test.v1.BarStatus + 6, // 5: test.v1.BarEventType.created:type_name -> test.v1.BarEventType.Created + 7, // 6: test.v1.BarEventType.updated:type_name -> test.v1.BarEventType.Updated + 8, // 7: test.v1.BarEventType.deleted:type_name -> test.v1.BarEventType.Deleted + 11, // 8: test.v1.BarEvent.metadata:type_name -> j5.state.v1.EventMetadata + 1, // 9: test.v1.BarEvent.keys:type_name -> test.v1.BarKeys + 4, // 10: test.v1.BarEvent.event:type_name -> test.v1.BarEventType + 11, // [11:11] is the sub-list for method output_type + 11, // [11:11] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name +} + +func init() { file_test_v1_bar_j5s_proto_init() } +func file_test_v1_bar_j5s_proto_init() { + if File_test_v1_bar_j5s_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_test_v1_bar_j5s_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarKeys); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_bar_j5s_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_bar_j5s_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_bar_j5s_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarEventType); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_bar_j5s_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_bar_j5s_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarEventType_Created); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_bar_j5s_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarEventType_Updated); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_bar_j5s_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarEventType_Deleted); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_test_v1_bar_j5s_proto_msgTypes[1].OneofWrappers = []interface{}{} + file_test_v1_bar_j5s_proto_msgTypes[3].OneofWrappers = []interface{}{ + (*BarEventType_Created_)(nil), + (*BarEventType_Updated_)(nil), + (*BarEventType_Deleted_)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_test_v1_bar_j5s_proto_rawDesc, + NumEnums: 1, + NumMessages: 8, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_test_v1_bar_j5s_proto_goTypes, + DependencyIndexes: file_test_v1_bar_j5s_proto_depIdxs, + EnumInfos: file_test_v1_bar_j5s_proto_enumTypes, + MessageInfos: file_test_v1_bar_j5s_proto_msgTypes, + }.Build() + File_test_v1_bar_j5s_proto = out.File + file_test_v1_bar_j5s_proto_rawDesc = nil + file_test_v1_bar_j5s_proto_goTypes = nil + file_test_v1_bar_j5s_proto_depIdxs = nil +} diff --git a/internal/testproto/gen/test/v1/test_pb/bar.j5s_j5.pb.go b/internal/testproto/gen/test/v1/test_pb/bar.j5s_j5.pb.go index 0ebe24c..8493d3d 100644 --- a/internal/testproto/gen/test/v1/test_pb/bar.j5s_j5.pb.go +++ b/internal/testproto/gen/test/v1/test_pb/bar.j5s_j5.pb.go @@ -4,20 +4,90 @@ package test_pb import ( j5reflect "github.com/pentops/j5/lib/j5reflect" + proto "google.golang.org/protobuf/proto" ) func (msg *BarKeys) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarKeys) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarKeys) Clone() any { + return proto.Clone(msg).(*BarKeys) } func (msg *BarData) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarData) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarData) Clone() any { + return proto.Clone(msg).(*BarData) } func (msg *BarState) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarState) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarState) Clone() any { + return proto.Clone(msg).(*BarState) } func (msg *BarEventType) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarEventType) Clone() any { + return proto.Clone(msg).(*BarEventType) +} +func (msg *BarEventType_Created) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarEventType_Created) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarEventType_Created) Clone() any { + return proto.Clone(msg).(*BarEventType_Created) +} +func (msg *BarEventType_Updated) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarEventType_Updated) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarEventType_Updated) Clone() any { + return proto.Clone(msg).(*BarEventType_Updated) +} +func (msg *BarEventType_Deleted) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarEventType_Deleted) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarEventType_Deleted) Clone() any { + return proto.Clone(msg).(*BarEventType_Deleted) } func (msg *BarEvent) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarEvent) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarEvent) Clone() any { + return proto.Clone(msg).(*BarEvent) } diff --git a/internal/testproto/gen/test/v1/test_pb/bar.j5s_psm.pb.go b/internal/testproto/gen/test/v1/test_pb/bar.j5s_psm.pb.go new file mode 100644 index 0000000..f43abcb --- /dev/null +++ b/internal/testproto/gen/test/v1/test_pb/bar.j5s_psm.pb.go @@ -0,0 +1,618 @@ +// Code generated by protoc-gen-go-psm. DO NOT EDIT. + +package test_pb + +import ( + context "context" + fmt "fmt" + psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" + psm "github.com/pentops/protostate/psm" + sqrlx "github.com/pentops/sqrlx.go/sqrlx" +) + +// PSM BarPSM + +type BarPSM = psm.StateMachine[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] + +type BarPSMDB = psm.DBStateMachine[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] + +type BarPSMEventSpec = psm.EventSpec[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] + +type BarPSMHookBaton = psm.HookBaton[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] + +type BarPSMFullBaton = psm.CallbackBaton[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] + +type BarPSMEventKey = string + +const ( + BarPSMEventNil BarPSMEventKey = "" + BarPSMEventCreated BarPSMEventKey = "created" + BarPSMEventUpdated BarPSMEventKey = "updated" + BarPSMEventDeleted BarPSMEventKey = "deleted" +) + +// EXTEND BarKeys with the psm.IKeyset interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *BarKeys) PSMIsSet() bool { + return msg != nil +} + +// PSMFullName returns the full name of state machine with package prefix +func (msg *BarKeys) PSMFullName() string { + return "test.v1.bar" +} +func (msg *BarKeys) PSMKeyValues() (map[string]any, error) { + keyset := map[string]any{ + "bar_id": msg.BarId, + "bar_other_id": msg.BarOtherId, + "date_key": msg.DateKey, + } + return keyset, nil +} + +// EXTEND BarState with the psm.IState interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *BarState) PSMIsSet() bool { + return msg != nil +} + +func (msg *BarState) PSMMetadata() *psm_j5pb.StateMetadata { + if msg.Metadata == nil { + msg.Metadata = &psm_j5pb.StateMetadata{} + } + return msg.Metadata +} + +func (msg *BarState) PSMKeys() *BarKeys { + return msg.Keys +} + +func (msg *BarState) SetStatus(status BarStatus) { + msg.Status = status +} + +func (msg *BarState) SetPSMKeys(inner *BarKeys) { + msg.Keys = inner +} + +func (msg *BarState) PSMData() *BarData { + if msg.Data == nil { + msg.Data = &BarData{} + } + return msg.Data +} + +// EXTEND BarData with the psm.IStateData interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *BarData) PSMIsSet() bool { + return msg != nil +} + +// EXTEND BarEvent with the psm.IEvent interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *BarEvent) PSMIsSet() bool { + return msg != nil +} + +func (msg *BarEvent) PSMMetadata() *psm_j5pb.EventMetadata { + if msg.Metadata == nil { + msg.Metadata = &psm_j5pb.EventMetadata{} + } + return msg.Metadata +} + +func (msg *BarEvent) PSMKeys() *BarKeys { + return msg.Keys +} + +func (msg *BarEvent) SetPSMKeys(inner *BarKeys) { + msg.Keys = inner +} + +// PSMEventKey returns the BarPSMEventPSMEventKey for the event, implementing psm.IEvent +func (msg *BarEvent) PSMEventKey() BarPSMEventKey { + tt := msg.UnwrapPSMEvent() + if tt == nil { + return BarPSMEventNil + } + return tt.PSMEventKey() +} + +// UnwrapPSMEvent implements psm.IEvent, returning the inner event message +func (msg *BarEvent) UnwrapPSMEvent() BarPSMEvent { + if msg == nil { + return nil + } + if msg.Event == nil { + return nil + } + switch v := msg.Event.Type.(type) { + case *BarEventType_Created_: + return v.Created + case *BarEventType_Updated_: + return v.Updated + case *BarEventType_Deleted_: + return v.Deleted + default: + return nil + } +} + +// SetPSMEvent sets the inner event message from a concrete type, implementing psm.IEvent +func (msg *BarEvent) SetPSMEvent(inner BarPSMEvent) error { + if msg.Event == nil { + msg.Event = &BarEventType{} + } + switch v := inner.(type) { + case *BarEventType_Created: + msg.Event.Type = &BarEventType_Created_{Created: v} + case *BarEventType_Updated: + msg.Event.Type = &BarEventType_Updated_{Updated: v} + case *BarEventType_Deleted: + msg.Event.Type = &BarEventType_Deleted_{Deleted: v} + default: + return fmt.Errorf("invalid type %T for BarEventType", v) + } + return nil +} + +type BarPSMEvent interface { + psm.IInnerEvent + PSMEventKey() BarPSMEventKey +} + +// EXTEND BarEventType_Created with the BarPSMEvent interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *BarEventType_Created) PSMIsSet() bool { + return msg != nil +} + +func (*BarEventType_Created) PSMEventKey() BarPSMEventKey { + return BarPSMEventCreated +} + +// EXTEND BarEventType_Updated with the BarPSMEvent interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *BarEventType_Updated) PSMIsSet() bool { + return msg != nil +} + +func (*BarEventType_Updated) PSMEventKey() BarPSMEventKey { + return BarPSMEventUpdated +} + +// EXTEND BarEventType_Deleted with the BarPSMEvent interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *BarEventType_Deleted) PSMIsSet() bool { + return msg != nil +} + +func (*BarEventType_Deleted) PSMEventKey() BarPSMEventKey { + return BarPSMEventDeleted +} + +func BarPSMBuilder() *psm.StateMachineConfig[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] { + return &psm.StateMachineConfig[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent + ]{} +} + +// BarPSMMutation runs at the start of a transition to merge the event information into the state data object. The state object is mutable in this context. +func BarPSMMutation[SE BarPSMEvent](cb func(*BarData, SE) error) psm.TransitionMutation[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent + SE, // Specific event type for the transition +] { + return psm.TransitionMutation[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent + SE, // Specific event type for the transition + ](cb) +} + +// BarPSMLogicHook runs after the mutation is complete. This hook can trigger side effects, including chained events, which are additional events processed by the state machine. Use this for Business Logic which determines the 'next step' in processing. +func BarPSMLogicHook[ + SE BarPSMEvent, +]( + cb func( + context.Context, + BarPSMHookBaton, + *BarState, + SE, + ) error) psm.TransitionHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] { + eventType := (*new(SE)).PSMEventKey() + return psm.TransitionHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func(ctx context.Context, tx sqrlx.Transaction, baton BarPSMFullBaton, state *BarState, event *BarEvent) error { + asType, ok := any(event.UnwrapPSMEvent()).(SE) + if !ok { + name := event.ProtoReflect().Descriptor().FullName() + return fmt.Errorf("unexpected event type in transition: %s [IE] does not match [SE] (%T)", name, new(SE)) + } + return cb(ctx, baton, state, asType) + }, + EventType: eventType, + RunOnFollow: false, + } +} + +// BarPSMDataHook runs after the mutations, and can be used to update data in tables which are not controlled as the state machine, e.g. for pre-calculating fields for performance reasons. Use of this hook prevents (future) transaction optimizations, as the transaction state when the function is called must needs to match the processing state, but only for this single transition, unlike the GeneralEventDataHook. +func BarPSMDataHook[ + SE BarPSMEvent, +]( + cb func( + context.Context, + sqrlx.Transaction, + *BarState, + SE, + ) error) psm.TransitionHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] { + eventType := (*new(SE)).PSMEventKey() + return psm.TransitionHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func(ctx context.Context, tx sqrlx.Transaction, baton BarPSMFullBaton, state *BarState, event *BarEvent) error { + asType, ok := any(event.UnwrapPSMEvent()).(SE) + if !ok { + name := event.ProtoReflect().Descriptor().FullName() + return fmt.Errorf("unexpected event type in transition: %s [IE] does not match [SE] (%T)", name, new(SE)) + } + return cb(ctx, tx, state, asType) + }, + EventType: eventType, + RunOnFollow: true, + } +} + +// BarPSMLinkHook runs after the mutation and logic hook, and can be used to link the state machine to other state machines in the same database transaction +func BarPSMLinkHook[ + SE BarPSMEvent, + DK psm.IKeyset, + DIE psm.IInnerEvent, +]( + linkDestination psm.LinkDestination[DK, DIE], + cb func( + context.Context, + *BarState, + SE, + func(DK, DIE), + ) error) psm.TransitionHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] { + eventType := (*new(SE)).PSMEventKey() + wrapped := func(ctx context.Context, tx sqrlx.Transaction, state *BarState, event SE, add func(DK, DIE)) error { + return cb(ctx, state, event, add) + } + return psm.TransitionHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func(ctx context.Context, tx sqrlx.Transaction, baton BarPSMFullBaton, state *BarState, event *BarEvent) error { + return psm.RunLinkHook(ctx, linkDestination, wrapped, tx, state, event) + }, + EventType: eventType, + RunOnFollow: false, + } +} + +// BarPSMLinkDBHook like LinkHook, but has access to the current transaction for reads only (not enforced), use in place of controller logic to look up existing state. +func BarPSMLinkDBHook[ + SE BarPSMEvent, + DK psm.IKeyset, + DIE psm.IInnerEvent, +]( + linkDestination psm.LinkDestination[DK, DIE], + cb func( + context.Context, + sqrlx.Transaction, + *BarState, + SE, + func(DK, DIE), + ) error) psm.TransitionHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] { + eventType := (*new(SE)).PSMEventKey() + return psm.TransitionHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func(ctx context.Context, tx sqrlx.Transaction, baton BarPSMFullBaton, state *BarState, event *BarEvent) error { + return psm.RunLinkHook(ctx, linkDestination, cb, tx, state, event) + }, + EventType: eventType, + RunOnFollow: false, + } +} + +// BarPSMGeneralLogicHook runs once per transition at the state-machine level regardless of which transition / event is being processed. It runs exactly once per transition, with the state object in the final state after the transition but prior to processing any further events. Chained events are added to the *end* of the event queue for the transaction, and side effects are published (as always) when the transaction is committed. The function MUST be pure, i.e. It MUST NOT produce any side-effects outside of the HookBaton, and MUST NOT modify the state. +func BarPSMGeneralLogicHook( + cb func( + context.Context, + BarPSMHookBaton, + *BarState, + *BarEvent, + ) error) psm.GeneralEventHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] { + return psm.GeneralEventHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func( + ctx context.Context, + tx sqrlx.Transaction, + baton BarPSMFullBaton, + state *BarState, + event *BarEvent, + ) error { + return cb(ctx, baton, state, event) + }, + RunOnFollow: false, + } +} + +// BarPSMGeneralStateDataHook runs at the state-machine level regardless of which transition / event is being processed. It runs at-least once before committing a database transaction after multiple transitions are complete. This hook has access only to the final state after the transitions and is used to update other tables based on the resulting state. It MUST be idempotent, it may be called after injecting externally-held state data. +func BarPSMGeneralStateDataHook( + cb func( + context.Context, + sqrlx.Transaction, + *BarState, + ) error) psm.GeneralStateHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] { + return psm.GeneralStateHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func( + ctx context.Context, + tx sqrlx.Transaction, + baton BarPSMFullBaton, + state *BarState, + ) error { + return cb(ctx, tx, state) + }, + RunOnFollow: true, + } +} + +// BarPSMGeneralEventDataHook runs after each transition at the state-machine level regardless of which transition / event is being processed. It runs exactly once per transition, before any other events are processed. The presence of this hook type prevents (future) transaction optimizations, so should be used sparingly. +func BarPSMGeneralEventDataHook( + cb func( + context.Context, + sqrlx.Transaction, + *BarState, + *BarEvent, + ) error) psm.GeneralEventHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] { + return psm.GeneralEventHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func( + ctx context.Context, + tx sqrlx.Transaction, + baton BarPSMFullBaton, + state *BarState, + event *BarEvent, + ) error { + return cb(ctx, tx, state, event) + }, + RunOnFollow: true, + } +} + +// BarPSMEventPublishHook EventPublishHook runs for each transition, at least once before committing a database transaction after multiple transitions are complete. It should publish a derived version of the event using the publisher. +func BarPSMEventPublishHook( + cb func( + context.Context, + psm.Publisher, + *BarState, + *BarEvent, + ) error) psm.GeneralEventHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] { + return psm.GeneralEventHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func( + ctx context.Context, + tx sqrlx.Transaction, + baton BarPSMFullBaton, + state *BarState, + event *BarEvent, + ) error { + return cb(ctx, baton, state, event) + }, + RunOnFollow: false, + } +} + +// BarPSMUpsertPublishHook runs for each transition, at least once before committing a database transaction after multiple transitions are complete. It should publish a derived version of the event using the publisher. +func BarPSMUpsertPublishHook( + cb func( + context.Context, + psm.Publisher, + *BarState, + ) error) psm.GeneralStateHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent +] { + return psm.GeneralStateHook[ + *BarKeys, // implements psm.IKeyset + *BarState, // implements psm.IState + BarStatus, // implements psm.IStatusEnum + *BarData, // implements psm.IStateData + *BarEvent, // implements psm.IEvent + BarPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func( + ctx context.Context, + tx sqrlx.Transaction, + baton BarPSMFullBaton, + state *BarState, + ) error { + return cb(ctx, baton, state) + }, + RunOnFollow: false, + } +} + +func (event *BarEvent) EventPublishMetadata() *psm_j5pb.EventPublishMetadata { + tenantKeys := make([]*psm_j5pb.EventTenant, 0) + return &psm_j5pb.EventPublishMetadata{ + EventId: event.Metadata.EventId, + Sequence: event.Metadata.Sequence, + Timestamp: event.Metadata.Timestamp, + Cause: event.Metadata.Cause, + Auth: &psm_j5pb.PublishAuth{ + TenantKeys: tenantKeys, + }, + } +} diff --git a/internal/testproto/gen/test/v1/test_pb/bar.j5s_sugar.pb.go b/internal/testproto/gen/test/v1/test_pb/bar.j5s_sugar.pb.go new file mode 100644 index 0000000..7397083 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_pb/bar.j5s_sugar.pb.go @@ -0,0 +1,120 @@ +// Code generated by protoc-gen-go-sugar. DO NOT EDIT. + +package test_pb + +import ( + driver "database/sql/driver" + fmt "fmt" + proto "google.golang.org/protobuf/proto" +) + +// BarEventType is a oneof wrapper +type BarEventTypeKey string + +const ( + BarEvent_Created BarEventTypeKey = "created" + BarEvent_Updated BarEventTypeKey = "updated" + BarEvent_Deleted BarEventTypeKey = "deleted" +) + +func (x *BarEventType) TypeKey() (BarEventTypeKey, bool) { + switch x.Type.(type) { + case *BarEventType_Created_: + return BarEvent_Created, true + case *BarEventType_Updated_: + return BarEvent_Updated, true + case *BarEventType_Deleted_: + return BarEvent_Deleted, true + default: + return "", false + } +} + +type IsBarEventTypeWrappedType interface { + TypeKey() BarEventTypeKey + proto.Message +} + +func (x *BarEventType) Set(val IsBarEventTypeWrappedType) { + switch v := val.(type) { + case *BarEventType_Created: + x.Type = &BarEventType_Created_{Created: v} + case *BarEventType_Updated: + x.Type = &BarEventType_Updated_{Updated: v} + case *BarEventType_Deleted: + x.Type = &BarEventType_Deleted_{Deleted: v} + } +} +func (x *BarEventType) Get() IsBarEventTypeWrappedType { + switch v := x.Type.(type) { + case *BarEventType_Created_: + return v.Created + case *BarEventType_Updated_: + return v.Updated + case *BarEventType_Deleted_: + return v.Deleted + default: + return nil + } +} +func (x *BarEventType_Created) TypeKey() BarEventTypeKey { + return BarEvent_Created +} +func (x *BarEventType_Updated) TypeKey() BarEventTypeKey { + return BarEvent_Updated +} +func (x *BarEventType_Deleted) TypeKey() BarEventTypeKey { + return BarEvent_Deleted +} + +type IsBarEventType_Type = isBarEventType_Type + +// BarStatus +const ( + BarStatus_UNSPECIFIED BarStatus = 0 + BarStatus_ACTIVE BarStatus = 1 + BarStatus_DELETED BarStatus = 2 +) + +var ( + BarStatus_name_short = map[int32]string{ + 0: "UNSPECIFIED", + 1: "ACTIVE", + 2: "DELETED", + } + BarStatus_value_short = map[string]int32{ + "UNSPECIFIED": 0, + "ACTIVE": 1, + "DELETED": 2, + } + BarStatus_value_either = map[string]int32{ + "UNSPECIFIED": 0, + "BAR_STATUS_UNSPECIFIED": 0, + "ACTIVE": 1, + "BAR_STATUS_ACTIVE": 1, + "DELETED": 2, + "BAR_STATUS_DELETED": 2, + } +) + +// ShortString returns the un-prefixed string representation of the enum value +func (x BarStatus) ShortString() string { + return BarStatus_name_short[int32(x)] +} +func (x BarStatus) Value() (driver.Value, error) { + return []uint8(x.ShortString()), nil +} +func (x *BarStatus) Scan(value interface{}) error { + var strVal string + switch vt := value.(type) { + case []uint8: + strVal = string(vt) + case string: + strVal = vt + default: + return fmt.Errorf("invalid type %T", value) + } + val := BarStatus_value_either[strVal] + *x = BarStatus(val) + return nil +} diff --git a/internal/testproto/gen/test/v1/test_pb/foo.j5s.pb.go b/internal/testproto/gen/test/v1/test_pb/foo.j5s.pb.go new file mode 100644 index 0000000..47cbb18 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_pb/foo.j5s.pb.go @@ -0,0 +1,1201 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc (unknown) +// source: test/v1/foo.j5s.proto + +package test_pb + +import ( + _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" + _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" + _ "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" + psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FooStatus int32 + +const ( + FooStatus_FOO_STATUS_UNSPECIFIED FooStatus = 0 + FooStatus_FOO_STATUS_ACTIVE FooStatus = 1 + FooStatus_FOO_STATUS_DELETED FooStatus = 2 +) + +// Enum value maps for FooStatus. +var ( + FooStatus_name = map[int32]string{ + 0: "FOO_STATUS_UNSPECIFIED", + 1: "FOO_STATUS_ACTIVE", + 2: "FOO_STATUS_DELETED", + } + FooStatus_value = map[string]int32{ + "FOO_STATUS_UNSPECIFIED": 0, + "FOO_STATUS_ACTIVE": 1, + "FOO_STATUS_DELETED": 2, + } +) + +func (x FooStatus) Enum() *FooStatus { + p := new(FooStatus) + *p = x + return p +} + +func (x FooStatus) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (FooStatus) Descriptor() protoreflect.EnumDescriptor { + return file_test_v1_foo_j5s_proto_enumTypes[0].Descriptor() +} + +func (FooStatus) Type() protoreflect.EnumType { + return &file_test_v1_foo_j5s_proto_enumTypes[0] +} + +func (x FooStatus) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use FooStatus.Descriptor instead. +func (FooStatus) EnumDescriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{0} +} + +type FooKeys struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FooId string `protobuf:"bytes,1,opt,name=foo_id,json=fooId,proto3" json:"foo_id,omitempty"` + TenantId *string `protobuf:"bytes,2,opt,name=tenant_id,json=tenantId,proto3,oneof" json:"tenant_id,omitempty"` + MetaTenantId string `protobuf:"bytes,3,opt,name=meta_tenant_id,json=metaTenantId,proto3" json:"meta_tenant_id,omitempty"` +} + +func (x *FooKeys) Reset() { + *x = FooKeys{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_foo_j5s_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooKeys) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooKeys) ProtoMessage() {} + +func (x *FooKeys) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_foo_j5s_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooKeys.ProtoReflect.Descriptor instead. +func (*FooKeys) Descriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{0} +} + +func (x *FooKeys) GetFooId() string { + if x != nil { + return x.FooId + } + return "" +} + +func (x *FooKeys) GetTenantId() string { + if x != nil && x.TenantId != nil { + return *x.TenantId + } + return "" +} + +func (x *FooKeys) GetMetaTenantId() string { + if x != nil { + return x.MetaTenantId + } + return "" +} + +type FooData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Field string `protobuf:"bytes,2,opt,name=field,proto3" json:"field,omitempty"` + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` + Characteristics *FooCharacteristics `protobuf:"bytes,4,opt,name=characteristics,proto3" json:"characteristics,omitempty"` + Profiles []*FooProfile `protobuf:"bytes,5,rep,name=profiles,proto3" json:"profiles,omitempty"` +} + +func (x *FooData) Reset() { + *x = FooData{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_foo_j5s_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooData) ProtoMessage() {} + +func (x *FooData) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_foo_j5s_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooData.ProtoReflect.Descriptor instead. +func (*FooData) Descriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{1} +} + +func (x *FooData) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *FooData) GetField() string { + if x != nil { + return x.Field + } + return "" +} + +func (x *FooData) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *FooData) GetCharacteristics() *FooCharacteristics { + if x != nil { + return x.Characteristics + } + return nil +} + +func (x *FooData) GetProfiles() []*FooProfile { + if x != nil { + return x.Profiles + } + return nil +} + +type FooState struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Metadata *psm_j5pb.StateMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + Keys *FooKeys `protobuf:"bytes,2,opt,name=keys,proto3" json:"keys,omitempty"` + Data *FooData `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + Status FooStatus `protobuf:"varint,4,opt,name=status,proto3,enum=test.v1.FooStatus" json:"status,omitempty"` +} + +func (x *FooState) Reset() { + *x = FooState{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_foo_j5s_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooState) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooState) ProtoMessage() {} + +func (x *FooState) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_foo_j5s_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooState.ProtoReflect.Descriptor instead. +func (*FooState) Descriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{2} +} + +func (x *FooState) GetMetadata() *psm_j5pb.StateMetadata { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *FooState) GetKeys() *FooKeys { + if x != nil { + return x.Keys + } + return nil +} + +func (x *FooState) GetData() *FooData { + if x != nil { + return x.Data + } + return nil +} + +func (x *FooState) GetStatus() FooStatus { + if x != nil { + return x.Status + } + return FooStatus_FOO_STATUS_UNSPECIFIED +} + +type FooEventType struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Type: + // + // *FooEventType_Created_ + // *FooEventType_Updated_ + // *FooEventType_Deleted_ + Type isFooEventType_Type `protobuf_oneof:"type"` +} + +func (x *FooEventType) Reset() { + *x = FooEventType{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_foo_j5s_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooEventType) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooEventType) ProtoMessage() {} + +func (x *FooEventType) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_foo_j5s_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooEventType.ProtoReflect.Descriptor instead. +func (*FooEventType) Descriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{3} +} + +func (m *FooEventType) GetType() isFooEventType_Type { + if m != nil { + return m.Type + } + return nil +} + +func (x *FooEventType) GetCreated() *FooEventType_Created { + if x, ok := x.GetType().(*FooEventType_Created_); ok { + return x.Created + } + return nil +} + +func (x *FooEventType) GetUpdated() *FooEventType_Updated { + if x, ok := x.GetType().(*FooEventType_Updated_); ok { + return x.Updated + } + return nil +} + +func (x *FooEventType) GetDeleted() *FooEventType_Deleted { + if x, ok := x.GetType().(*FooEventType_Deleted_); ok { + return x.Deleted + } + return nil +} + +type isFooEventType_Type interface { + isFooEventType_Type() +} + +type FooEventType_Created_ struct { + Created *FooEventType_Created `protobuf:"bytes,1,opt,name=created,proto3,oneof"` +} + +type FooEventType_Updated_ struct { + Updated *FooEventType_Updated `protobuf:"bytes,2,opt,name=updated,proto3,oneof"` +} + +type FooEventType_Deleted_ struct { + Deleted *FooEventType_Deleted `protobuf:"bytes,3,opt,name=deleted,proto3,oneof"` +} + +func (*FooEventType_Created_) isFooEventType_Type() {} + +func (*FooEventType_Updated_) isFooEventType_Type() {} + +func (*FooEventType_Deleted_) isFooEventType_Type() {} + +type FooEvent struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Metadata *psm_j5pb.EventMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + Keys *FooKeys `protobuf:"bytes,2,opt,name=keys,proto3" json:"keys,omitempty"` + Event *FooEventType `protobuf:"bytes,3,opt,name=event,proto3" json:"event,omitempty"` +} + +func (x *FooEvent) Reset() { + *x = FooEvent{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_foo_j5s_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooEvent) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooEvent) ProtoMessage() {} + +func (x *FooEvent) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_foo_j5s_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooEvent.ProtoReflect.Descriptor instead. +func (*FooEvent) Descriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{4} +} + +func (x *FooEvent) GetMetadata() *psm_j5pb.EventMetadata { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *FooEvent) GetKeys() *FooKeys { + if x != nil { + return x.Keys + } + return nil +} + +func (x *FooEvent) GetEvent() *FooEventType { + if x != nil { + return x.Event + } + return nil +} + +type FooCharacteristics struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Weight int64 `protobuf:"varint,1,opt,name=weight,proto3" json:"weight,omitempty"` + Height int64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` + Length int64 `protobuf:"varint,3,opt,name=length,proto3" json:"length,omitempty"` +} + +func (x *FooCharacteristics) Reset() { + *x = FooCharacteristics{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_foo_j5s_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooCharacteristics) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooCharacteristics) ProtoMessage() {} + +func (x *FooCharacteristics) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_foo_j5s_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooCharacteristics.ProtoReflect.Descriptor instead. +func (*FooCharacteristics) Descriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{5} +} + +func (x *FooCharacteristics) GetWeight() int64 { + if x != nil { + return x.Weight + } + return 0 +} + +func (x *FooCharacteristics) GetHeight() int64 { + if x != nil { + return x.Height + } + return 0 +} + +func (x *FooCharacteristics) GetLength() int64 { + if x != nil { + return x.Length + } + return 0 +} + +type FooProfile struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Place int64 `protobuf:"varint,1,opt,name=place,proto3" json:"place,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *FooProfile) Reset() { + *x = FooProfile{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_foo_j5s_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooProfile) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooProfile) ProtoMessage() {} + +func (x *FooProfile) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_foo_j5s_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooProfile.ProtoReflect.Descriptor instead. +func (*FooProfile) Descriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{6} +} + +func (x *FooProfile) GetPlace() int64 { + if x != nil { + return x.Place + } + return 0 +} + +func (x *FooProfile) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type FooEventType_Created struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Field string `protobuf:"bytes,2,opt,name=field,proto3" json:"field,omitempty"` + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` + Weight *int64 `protobuf:"varint,4,opt,name=weight,proto3,oneof" json:"weight,omitempty"` + Height *int64 `protobuf:"varint,5,opt,name=height,proto3,oneof" json:"height,omitempty"` + Length *int64 `protobuf:"varint,6,opt,name=length,proto3,oneof" json:"length,omitempty"` + Profiles []*FooProfile `protobuf:"bytes,7,rep,name=profiles,proto3" json:"profiles,omitempty"` +} + +func (x *FooEventType_Created) Reset() { + *x = FooEventType_Created{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_foo_j5s_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooEventType_Created) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooEventType_Created) ProtoMessage() {} + +func (x *FooEventType_Created) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_foo_j5s_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooEventType_Created.ProtoReflect.Descriptor instead. +func (*FooEventType_Created) Descriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{3, 0} +} + +func (x *FooEventType_Created) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *FooEventType_Created) GetField() string { + if x != nil { + return x.Field + } + return "" +} + +func (x *FooEventType_Created) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *FooEventType_Created) GetWeight() int64 { + if x != nil && x.Weight != nil { + return *x.Weight + } + return 0 +} + +func (x *FooEventType_Created) GetHeight() int64 { + if x != nil && x.Height != nil { + return *x.Height + } + return 0 +} + +func (x *FooEventType_Created) GetLength() int64 { + if x != nil && x.Length != nil { + return *x.Length + } + return 0 +} + +func (x *FooEventType_Created) GetProfiles() []*FooProfile { + if x != nil { + return x.Profiles + } + return nil +} + +type FooEventType_Updated struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Field string `protobuf:"bytes,2,opt,name=field,proto3" json:"field,omitempty"` + Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` + Weight *int64 `protobuf:"varint,4,opt,name=weight,proto3,oneof" json:"weight,omitempty"` + Height *int64 `protobuf:"varint,5,opt,name=height,proto3,oneof" json:"height,omitempty"` + Length *int64 `protobuf:"varint,6,opt,name=length,proto3,oneof" json:"length,omitempty"` + Profiles []*FooProfile `protobuf:"bytes,7,rep,name=profiles,proto3" json:"profiles,omitempty"` + Delete bool `protobuf:"varint,8,opt,name=delete,proto3" json:"delete,omitempty"` +} + +func (x *FooEventType_Updated) Reset() { + *x = FooEventType_Updated{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_foo_j5s_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooEventType_Updated) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooEventType_Updated) ProtoMessage() {} + +func (x *FooEventType_Updated) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_foo_j5s_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooEventType_Updated.ProtoReflect.Descriptor instead. +func (*FooEventType_Updated) Descriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{3, 1} +} + +func (x *FooEventType_Updated) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *FooEventType_Updated) GetField() string { + if x != nil { + return x.Field + } + return "" +} + +func (x *FooEventType_Updated) GetDescription() string { + if x != nil && x.Description != nil { + return *x.Description + } + return "" +} + +func (x *FooEventType_Updated) GetWeight() int64 { + if x != nil && x.Weight != nil { + return *x.Weight + } + return 0 +} + +func (x *FooEventType_Updated) GetHeight() int64 { + if x != nil && x.Height != nil { + return *x.Height + } + return 0 +} + +func (x *FooEventType_Updated) GetLength() int64 { + if x != nil && x.Length != nil { + return *x.Length + } + return 0 +} + +func (x *FooEventType_Updated) GetProfiles() []*FooProfile { + if x != nil { + return x.Profiles + } + return nil +} + +func (x *FooEventType_Updated) GetDelete() bool { + if x != nil { + return x.Delete + } + return false +} + +type FooEventType_Deleted struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Delete bool `protobuf:"varint,1,opt,name=delete,proto3" json:"delete,omitempty"` +} + +func (x *FooEventType_Deleted) Reset() { + *x = FooEventType_Deleted{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_foo_j5s_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooEventType_Deleted) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooEventType_Deleted) ProtoMessage() {} + +func (x *FooEventType_Deleted) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_foo_j5s_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooEventType_Deleted.ProtoReflect.Descriptor instead. +func (*FooEventType_Deleted) Descriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{3, 2} +} + +func (x *FooEventType_Deleted) GetDelete() bool { + if x != nil { + return x.Delete + } + return false +} + +var File_test_v1_foo_j5s_proto protoreflect.FileDescriptor + +var file_test_v1_foo_j5s_proto_rawDesc = []byte{ + 0x0a, 0x15, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x6f, 0x6f, 0x2e, 0x6a, 0x35, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, + 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, + 0x35, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6a, 0x35, 0x2f, 0x6c, + 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x6a, 0x35, 0x2f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x93, 0x02, 0x0a, 0x07, 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, + 0x12, 0x43, 0x0a, 0x06, 0x66, 0x6f, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x2c, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, + 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x8a, 0xf7, + 0x98, 0xc6, 0x02, 0x0a, 0x72, 0x08, 0x1a, 0x06, 0x12, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x05, + 0x66, 0x6f, 0x6f, 0x49, 0x64, 0x12, 0x51, 0x0a, 0x09, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2f, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, + 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, + 0x08, 0x2a, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x72, + 0x08, 0x1a, 0x06, 0x12, 0x04, 0x52, 0x02, 0x08, 0x01, 0x48, 0x00, 0x52, 0x08, 0x74, 0x65, 0x6e, + 0x61, 0x6e, 0x74, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x4d, 0x0a, 0x0e, 0x6d, 0x65, 0x74, 0x61, + 0x5f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x27, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, + 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x72, + 0x08, 0x1a, 0x06, 0x12, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x54, + 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, + 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x10, 0x01, 0x42, 0x0c, 0x0a, 0x0a, + 0x5f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, 0xf8, 0x02, 0x0a, 0x07, 0x46, + 0x6f, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x12, 0x34, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x20, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, + 0x98, 0xc6, 0x02, 0x12, 0x72, 0x10, 0x0a, 0x0e, 0x52, 0x0c, 0x08, 0x01, 0x12, 0x08, 0x74, 0x73, + 0x76, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x37, 0x0a, 0x05, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x21, 0xc2, 0xff, 0x8e, + 0x02, 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x13, 0x72, 0x11, 0x0a, 0x0f, 0x52, + 0x0d, 0x08, 0x01, 0x12, 0x09, 0x74, 0x73, 0x76, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x52, 0x05, + 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x4e, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x27, 0xc2, 0xff, 0x8e, 0x02, + 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x19, 0x72, 0x17, 0x0a, 0x15, 0x52, 0x13, + 0x08, 0x01, 0x12, 0x0f, 0x74, 0x73, 0x76, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x4e, 0x0a, 0x0f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, + 0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x43, 0x68, 0x61, 0x72, + 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x42, 0x07, 0xc2, 0xff, 0x8e, + 0x02, 0x02, 0x52, 0x00, 0x52, 0x0f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, + 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, + 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x42, 0x08, 0xc2, 0xff, + 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, + 0x66, 0x6f, 0x6f, 0x10, 0x04, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb2, 0x02, 0x0a, 0x08, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, + 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, + 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x35, 0x0a, 0x04, 0x6b, 0x65, 0x79, + 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, + 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0f, 0xba, 0x48, 0x03, 0xc8, 0x01, + 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, + 0x12, 0x33, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x44, 0x61, 0x74, 0x61, + 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, + 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x5e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x32, 0xba, 0x48, 0x08, 0xc8, 0x01, + 0x01, 0x82, 0x01, 0x02, 0x10, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x8a, 0xf7, 0x98, + 0xc6, 0x02, 0x1a, 0xa2, 0x01, 0x17, 0x52, 0x15, 0x08, 0x01, 0x12, 0x11, 0x46, 0x4f, 0x4f, 0x5f, + 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x52, 0x06, 0x73, + 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, + 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x10, 0x02, 0x22, 0x8d, 0x08, 0x0a, 0x0c, 0x46, + 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x42, 0x0a, 0x07, 0x63, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x07, 0xc2, 0xff, 0x8e, + 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, + 0x42, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x42, + 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x64, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x07, + 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x1a, 0xe2, 0x02, 0x0a, 0x07, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, + 0x64, 0x12, 0x2f, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, + 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, + 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, 0x01, 0x52, 0x06, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x68, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, + 0xfa, 0x01, 0x00, 0x48, 0x02, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, + 0x12, 0x25, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, + 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, 0x03, 0x52, 0x06, 0x6c, 0x65, + 0x6e, 0x67, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x42, 0x08, + 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x73, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, + 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, + 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x1a, 0x84, 0x03, 0x0a, + 0x07, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, + 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, + 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2f, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, + 0x02, 0x03, 0xf2, 0x01, 0x00, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, + 0x00, 0x48, 0x01, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, + 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, + 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, 0x02, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, + 0x03, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x08, + 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x50, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x08, 0x70, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0x8a, 0x02, + 0x00, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, + 0x52, 0x00, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x09, 0x0a, + 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x1a, 0x34, 0x0a, 0x07, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x20, + 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x08, + 0xc2, 0xff, 0x8e, 0x02, 0x03, 0x8a, 0x02, 0x00, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, + 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, + 0x5a, 0x00, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xe6, 0x01, 0x0a, 0x08, 0x46, + 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6a, 0x35, 0x2e, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, + 0x02, 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x35, + 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0f, + 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, + 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x47, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x42, 0x1a, 0xba, 0x48, 0x03, + 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x62, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x07, + 0xaa, 0x01, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x13, + 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, + 0x6f, 0x10, 0x03, 0x22, 0xb3, 0x01, 0x0a, 0x12, 0x46, 0x6f, 0x6f, 0x43, 0x68, 0x61, 0x72, 0x61, + 0x63, 0x74, 0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x30, 0x0a, 0x06, 0x77, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x18, 0xc2, 0xff, 0x8e, 0x02, + 0x03, 0xfa, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x32, 0x08, 0x52, 0x02, 0x08, 0x01, + 0x5a, 0x02, 0x08, 0x01, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x30, 0x0a, 0x06, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, 0x18, 0xc2, 0xff, + 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x32, 0x08, 0x52, 0x02, + 0x08, 0x01, 0x5a, 0x02, 0x08, 0x01, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x30, + 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x18, + 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x32, 0x08, + 0x52, 0x02, 0x08, 0x01, 0x5a, 0x02, 0x08, 0x01, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, + 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0x7f, 0x0a, 0x0a, 0x46, 0x6f, 0x6f, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x70, 0x6c, 0x61, 0x63, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x14, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, + 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x06, 0x32, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x05, 0x70, 0x6c, + 0x61, 0x63, 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x28, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, + 0x1a, 0x72, 0x18, 0x0a, 0x16, 0x52, 0x14, 0x08, 0x01, 0x12, 0x10, 0x74, 0x73, 0x76, 0x5f, 0x70, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x2a, 0x56, 0x0a, 0x09, 0x46, 0x6f, + 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x4f, 0x4f, 0x5f, 0x53, + 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, + 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x4f, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, + 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x4f, + 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, + 0x10, 0x02, 0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, + 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, + 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, +} + +var ( + file_test_v1_foo_j5s_proto_rawDescOnce sync.Once + file_test_v1_foo_j5s_proto_rawDescData = file_test_v1_foo_j5s_proto_rawDesc +) + +func file_test_v1_foo_j5s_proto_rawDescGZIP() []byte { + file_test_v1_foo_j5s_proto_rawDescOnce.Do(func() { + file_test_v1_foo_j5s_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_v1_foo_j5s_proto_rawDescData) + }) + return file_test_v1_foo_j5s_proto_rawDescData +} + +var file_test_v1_foo_j5s_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_test_v1_foo_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_test_v1_foo_j5s_proto_goTypes = []interface{}{ + (FooStatus)(0), // 0: test.v1.FooStatus + (*FooKeys)(nil), // 1: test.v1.FooKeys + (*FooData)(nil), // 2: test.v1.FooData + (*FooState)(nil), // 3: test.v1.FooState + (*FooEventType)(nil), // 4: test.v1.FooEventType + (*FooEvent)(nil), // 5: test.v1.FooEvent + (*FooCharacteristics)(nil), // 6: test.v1.FooCharacteristics + (*FooProfile)(nil), // 7: test.v1.FooProfile + (*FooEventType_Created)(nil), // 8: test.v1.FooEventType.Created + (*FooEventType_Updated)(nil), // 9: test.v1.FooEventType.Updated + (*FooEventType_Deleted)(nil), // 10: test.v1.FooEventType.Deleted + (*psm_j5pb.StateMetadata)(nil), // 11: j5.state.v1.StateMetadata + (*psm_j5pb.EventMetadata)(nil), // 12: j5.state.v1.EventMetadata +} +var file_test_v1_foo_j5s_proto_depIdxs = []int32{ + 6, // 0: test.v1.FooData.characteristics:type_name -> test.v1.FooCharacteristics + 7, // 1: test.v1.FooData.profiles:type_name -> test.v1.FooProfile + 11, // 2: test.v1.FooState.metadata:type_name -> j5.state.v1.StateMetadata + 1, // 3: test.v1.FooState.keys:type_name -> test.v1.FooKeys + 2, // 4: test.v1.FooState.data:type_name -> test.v1.FooData + 0, // 5: test.v1.FooState.status:type_name -> test.v1.FooStatus + 8, // 6: test.v1.FooEventType.created:type_name -> test.v1.FooEventType.Created + 9, // 7: test.v1.FooEventType.updated:type_name -> test.v1.FooEventType.Updated + 10, // 8: test.v1.FooEventType.deleted:type_name -> test.v1.FooEventType.Deleted + 12, // 9: test.v1.FooEvent.metadata:type_name -> j5.state.v1.EventMetadata + 1, // 10: test.v1.FooEvent.keys:type_name -> test.v1.FooKeys + 4, // 11: test.v1.FooEvent.event:type_name -> test.v1.FooEventType + 7, // 12: test.v1.FooEventType.Created.profiles:type_name -> test.v1.FooProfile + 7, // 13: test.v1.FooEventType.Updated.profiles:type_name -> test.v1.FooProfile + 14, // [14:14] is the sub-list for method output_type + 14, // [14:14] is the sub-list for method input_type + 14, // [14:14] is the sub-list for extension type_name + 14, // [14:14] is the sub-list for extension extendee + 0, // [0:14] is the sub-list for field type_name +} + +func init() { file_test_v1_foo_j5s_proto_init() } +func file_test_v1_foo_j5s_proto_init() { + if File_test_v1_foo_j5s_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_test_v1_foo_j5s_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooKeys); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_foo_j5s_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooData); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_foo_j5s_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooState); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_foo_j5s_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooEventType); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_foo_j5s_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooEvent); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_foo_j5s_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooCharacteristics); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_foo_j5s_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooProfile); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_foo_j5s_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooEventType_Created); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_foo_j5s_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooEventType_Updated); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_foo_j5s_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooEventType_Deleted); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_test_v1_foo_j5s_proto_msgTypes[0].OneofWrappers = []interface{}{} + file_test_v1_foo_j5s_proto_msgTypes[1].OneofWrappers = []interface{}{} + file_test_v1_foo_j5s_proto_msgTypes[3].OneofWrappers = []interface{}{ + (*FooEventType_Created_)(nil), + (*FooEventType_Updated_)(nil), + (*FooEventType_Deleted_)(nil), + } + file_test_v1_foo_j5s_proto_msgTypes[7].OneofWrappers = []interface{}{} + file_test_v1_foo_j5s_proto_msgTypes[8].OneofWrappers = []interface{}{} + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_test_v1_foo_j5s_proto_rawDesc, + NumEnums: 1, + NumMessages: 10, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_test_v1_foo_j5s_proto_goTypes, + DependencyIndexes: file_test_v1_foo_j5s_proto_depIdxs, + EnumInfos: file_test_v1_foo_j5s_proto_enumTypes, + MessageInfos: file_test_v1_foo_j5s_proto_msgTypes, + }.Build() + File_test_v1_foo_j5s_proto = out.File + file_test_v1_foo_j5s_proto_rawDesc = nil + file_test_v1_foo_j5s_proto_goTypes = nil + file_test_v1_foo_j5s_proto_depIdxs = nil +} diff --git a/internal/testproto/gen/test/v1/test_pb/foo.j5s_j5.pb.go b/internal/testproto/gen/test/v1/test_pb/foo.j5s_j5.pb.go index f221194..e07ed80 100644 --- a/internal/testproto/gen/test/v1/test_pb/foo.j5s_j5.pb.go +++ b/internal/testproto/gen/test/v1/test_pb/foo.j5s_j5.pb.go @@ -4,26 +4,112 @@ package test_pb import ( j5reflect "github.com/pentops/j5/lib/j5reflect" + proto "google.golang.org/protobuf/proto" ) func (msg *FooKeys) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooKeys) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooKeys) Clone() any { + return proto.Clone(msg).(*FooKeys) } func (msg *FooData) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooData) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooData) Clone() any { + return proto.Clone(msg).(*FooData) } func (msg *FooState) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooState) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooState) Clone() any { + return proto.Clone(msg).(*FooState) } func (msg *FooEventType) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooEventType) Clone() any { + return proto.Clone(msg).(*FooEventType) +} +func (msg *FooEventType_Created) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooEventType_Created) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooEventType_Created) Clone() any { + return proto.Clone(msg).(*FooEventType_Created) +} +func (msg *FooEventType_Updated) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooEventType_Updated) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooEventType_Updated) Clone() any { + return proto.Clone(msg).(*FooEventType_Updated) +} +func (msg *FooEventType_Deleted) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooEventType_Deleted) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooEventType_Deleted) Clone() any { + return proto.Clone(msg).(*FooEventType_Deleted) } func (msg *FooEvent) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooEvent) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooEvent) Clone() any { + return proto.Clone(msg).(*FooEvent) } func (msg *FooCharacteristics) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooCharacteristics) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooCharacteristics) Clone() any { + return proto.Clone(msg).(*FooCharacteristics) } func (msg *FooProfile) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooProfile) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooProfile) Clone() any { + return proto.Clone(msg).(*FooProfile) } diff --git a/internal/testproto/gen/test/v1/test_pb/foo.j5s_psm.pb.go b/internal/testproto/gen/test/v1/test_pb/foo.j5s_psm.pb.go new file mode 100644 index 0000000..cf1c325 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_pb/foo.j5s_psm.pb.go @@ -0,0 +1,628 @@ +// Code generated by protoc-gen-go-psm. DO NOT EDIT. + +package test_pb + +import ( + context "context" + fmt "fmt" + psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" + psm "github.com/pentops/protostate/psm" + sqrlx "github.com/pentops/sqrlx.go/sqrlx" +) + +// PSM FooPSM + +type FooPSM = psm.StateMachine[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] + +type FooPSMDB = psm.DBStateMachine[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] + +type FooPSMEventSpec = psm.EventSpec[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] + +type FooPSMHookBaton = psm.HookBaton[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] + +type FooPSMFullBaton = psm.CallbackBaton[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] + +type FooPSMEventKey = string + +const ( + FooPSMEventNil FooPSMEventKey = "" + FooPSMEventCreated FooPSMEventKey = "created" + FooPSMEventUpdated FooPSMEventKey = "updated" + FooPSMEventDeleted FooPSMEventKey = "deleted" +) + +// EXTEND FooKeys with the psm.IKeyset interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *FooKeys) PSMIsSet() bool { + return msg != nil +} + +// PSMFullName returns the full name of state machine with package prefix +func (msg *FooKeys) PSMFullName() string { + return "test.v1.foo" +} +func (msg *FooKeys) PSMKeyValues() (map[string]any, error) { + keyset := map[string]any{ + "foo_id": msg.FooId, + } + if msg.TenantId != nil { + keyset["tenant_id"] = *msg.TenantId + } + if msg.MetaTenantId != "" { + keyset["meta_tenant_id"] = msg.MetaTenantId + } + return keyset, nil +} + +// EXTEND FooState with the psm.IState interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *FooState) PSMIsSet() bool { + return msg != nil +} + +func (msg *FooState) PSMMetadata() *psm_j5pb.StateMetadata { + if msg.Metadata == nil { + msg.Metadata = &psm_j5pb.StateMetadata{} + } + return msg.Metadata +} + +func (msg *FooState) PSMKeys() *FooKeys { + return msg.Keys +} + +func (msg *FooState) SetStatus(status FooStatus) { + msg.Status = status +} + +func (msg *FooState) SetPSMKeys(inner *FooKeys) { + msg.Keys = inner +} + +func (msg *FooState) PSMData() *FooData { + if msg.Data == nil { + msg.Data = &FooData{} + } + return msg.Data +} + +// EXTEND FooData with the psm.IStateData interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *FooData) PSMIsSet() bool { + return msg != nil +} + +// EXTEND FooEvent with the psm.IEvent interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *FooEvent) PSMIsSet() bool { + return msg != nil +} + +func (msg *FooEvent) PSMMetadata() *psm_j5pb.EventMetadata { + if msg.Metadata == nil { + msg.Metadata = &psm_j5pb.EventMetadata{} + } + return msg.Metadata +} + +func (msg *FooEvent) PSMKeys() *FooKeys { + return msg.Keys +} + +func (msg *FooEvent) SetPSMKeys(inner *FooKeys) { + msg.Keys = inner +} + +// PSMEventKey returns the FooPSMEventPSMEventKey for the event, implementing psm.IEvent +func (msg *FooEvent) PSMEventKey() FooPSMEventKey { + tt := msg.UnwrapPSMEvent() + if tt == nil { + return FooPSMEventNil + } + return tt.PSMEventKey() +} + +// UnwrapPSMEvent implements psm.IEvent, returning the inner event message +func (msg *FooEvent) UnwrapPSMEvent() FooPSMEvent { + if msg == nil { + return nil + } + if msg.Event == nil { + return nil + } + switch v := msg.Event.Type.(type) { + case *FooEventType_Created_: + return v.Created + case *FooEventType_Updated_: + return v.Updated + case *FooEventType_Deleted_: + return v.Deleted + default: + return nil + } +} + +// SetPSMEvent sets the inner event message from a concrete type, implementing psm.IEvent +func (msg *FooEvent) SetPSMEvent(inner FooPSMEvent) error { + if msg.Event == nil { + msg.Event = &FooEventType{} + } + switch v := inner.(type) { + case *FooEventType_Created: + msg.Event.Type = &FooEventType_Created_{Created: v} + case *FooEventType_Updated: + msg.Event.Type = &FooEventType_Updated_{Updated: v} + case *FooEventType_Deleted: + msg.Event.Type = &FooEventType_Deleted_{Deleted: v} + default: + return fmt.Errorf("invalid type %T for FooEventType", v) + } + return nil +} + +type FooPSMEvent interface { + psm.IInnerEvent + PSMEventKey() FooPSMEventKey +} + +// EXTEND FooEventType_Created with the FooPSMEvent interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *FooEventType_Created) PSMIsSet() bool { + return msg != nil +} + +func (*FooEventType_Created) PSMEventKey() FooPSMEventKey { + return FooPSMEventCreated +} + +// EXTEND FooEventType_Updated with the FooPSMEvent interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *FooEventType_Updated) PSMIsSet() bool { + return msg != nil +} + +func (*FooEventType_Updated) PSMEventKey() FooPSMEventKey { + return FooPSMEventUpdated +} + +// EXTEND FooEventType_Deleted with the FooPSMEvent interface + +// PSMIsSet is a helper for != nil, which does not work with generic parameters +func (msg *FooEventType_Deleted) PSMIsSet() bool { + return msg != nil +} + +func (*FooEventType_Deleted) PSMEventKey() FooPSMEventKey { + return FooPSMEventDeleted +} + +func FooPSMBuilder() *psm.StateMachineConfig[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] { + return &psm.StateMachineConfig[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent + ]{} +} + +// FooPSMMutation runs at the start of a transition to merge the event information into the state data object. The state object is mutable in this context. +func FooPSMMutation[SE FooPSMEvent](cb func(*FooData, SE) error) psm.TransitionMutation[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent + SE, // Specific event type for the transition +] { + return psm.TransitionMutation[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent + SE, // Specific event type for the transition + ](cb) +} + +// FooPSMLogicHook runs after the mutation is complete. This hook can trigger side effects, including chained events, which are additional events processed by the state machine. Use this for Business Logic which determines the 'next step' in processing. +func FooPSMLogicHook[ + SE FooPSMEvent, +]( + cb func( + context.Context, + FooPSMHookBaton, + *FooState, + SE, + ) error) psm.TransitionHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] { + eventType := (*new(SE)).PSMEventKey() + return psm.TransitionHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func(ctx context.Context, tx sqrlx.Transaction, baton FooPSMFullBaton, state *FooState, event *FooEvent) error { + asType, ok := any(event.UnwrapPSMEvent()).(SE) + if !ok { + name := event.ProtoReflect().Descriptor().FullName() + return fmt.Errorf("unexpected event type in transition: %s [IE] does not match [SE] (%T)", name, new(SE)) + } + return cb(ctx, baton, state, asType) + }, + EventType: eventType, + RunOnFollow: false, + } +} + +// FooPSMDataHook runs after the mutations, and can be used to update data in tables which are not controlled as the state machine, e.g. for pre-calculating fields for performance reasons. Use of this hook prevents (future) transaction optimizations, as the transaction state when the function is called must needs to match the processing state, but only for this single transition, unlike the GeneralEventDataHook. +func FooPSMDataHook[ + SE FooPSMEvent, +]( + cb func( + context.Context, + sqrlx.Transaction, + *FooState, + SE, + ) error) psm.TransitionHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] { + eventType := (*new(SE)).PSMEventKey() + return psm.TransitionHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func(ctx context.Context, tx sqrlx.Transaction, baton FooPSMFullBaton, state *FooState, event *FooEvent) error { + asType, ok := any(event.UnwrapPSMEvent()).(SE) + if !ok { + name := event.ProtoReflect().Descriptor().FullName() + return fmt.Errorf("unexpected event type in transition: %s [IE] does not match [SE] (%T)", name, new(SE)) + } + return cb(ctx, tx, state, asType) + }, + EventType: eventType, + RunOnFollow: true, + } +} + +// FooPSMLinkHook runs after the mutation and logic hook, and can be used to link the state machine to other state machines in the same database transaction +func FooPSMLinkHook[ + SE FooPSMEvent, + DK psm.IKeyset, + DIE psm.IInnerEvent, +]( + linkDestination psm.LinkDestination[DK, DIE], + cb func( + context.Context, + *FooState, + SE, + func(DK, DIE), + ) error) psm.TransitionHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] { + eventType := (*new(SE)).PSMEventKey() + wrapped := func(ctx context.Context, tx sqrlx.Transaction, state *FooState, event SE, add func(DK, DIE)) error { + return cb(ctx, state, event, add) + } + return psm.TransitionHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func(ctx context.Context, tx sqrlx.Transaction, baton FooPSMFullBaton, state *FooState, event *FooEvent) error { + return psm.RunLinkHook(ctx, linkDestination, wrapped, tx, state, event) + }, + EventType: eventType, + RunOnFollow: false, + } +} + +// FooPSMLinkDBHook like LinkHook, but has access to the current transaction for reads only (not enforced), use in place of controller logic to look up existing state. +func FooPSMLinkDBHook[ + SE FooPSMEvent, + DK psm.IKeyset, + DIE psm.IInnerEvent, +]( + linkDestination psm.LinkDestination[DK, DIE], + cb func( + context.Context, + sqrlx.Transaction, + *FooState, + SE, + func(DK, DIE), + ) error) psm.TransitionHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] { + eventType := (*new(SE)).PSMEventKey() + return psm.TransitionHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func(ctx context.Context, tx sqrlx.Transaction, baton FooPSMFullBaton, state *FooState, event *FooEvent) error { + return psm.RunLinkHook(ctx, linkDestination, cb, tx, state, event) + }, + EventType: eventType, + RunOnFollow: false, + } +} + +// FooPSMGeneralLogicHook runs once per transition at the state-machine level regardless of which transition / event is being processed. It runs exactly once per transition, with the state object in the final state after the transition but prior to processing any further events. Chained events are added to the *end* of the event queue for the transaction, and side effects are published (as always) when the transaction is committed. The function MUST be pure, i.e. It MUST NOT produce any side-effects outside of the HookBaton, and MUST NOT modify the state. +func FooPSMGeneralLogicHook( + cb func( + context.Context, + FooPSMHookBaton, + *FooState, + *FooEvent, + ) error) psm.GeneralEventHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] { + return psm.GeneralEventHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func( + ctx context.Context, + tx sqrlx.Transaction, + baton FooPSMFullBaton, + state *FooState, + event *FooEvent, + ) error { + return cb(ctx, baton, state, event) + }, + RunOnFollow: false, + } +} + +// FooPSMGeneralStateDataHook runs at the state-machine level regardless of which transition / event is being processed. It runs at-least once before committing a database transaction after multiple transitions are complete. This hook has access only to the final state after the transitions and is used to update other tables based on the resulting state. It MUST be idempotent, it may be called after injecting externally-held state data. +func FooPSMGeneralStateDataHook( + cb func( + context.Context, + sqrlx.Transaction, + *FooState, + ) error) psm.GeneralStateHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] { + return psm.GeneralStateHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func( + ctx context.Context, + tx sqrlx.Transaction, + baton FooPSMFullBaton, + state *FooState, + ) error { + return cb(ctx, tx, state) + }, + RunOnFollow: true, + } +} + +// FooPSMGeneralEventDataHook runs after each transition at the state-machine level regardless of which transition / event is being processed. It runs exactly once per transition, before any other events are processed. The presence of this hook type prevents (future) transaction optimizations, so should be used sparingly. +func FooPSMGeneralEventDataHook( + cb func( + context.Context, + sqrlx.Transaction, + *FooState, + *FooEvent, + ) error) psm.GeneralEventHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] { + return psm.GeneralEventHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func( + ctx context.Context, + tx sqrlx.Transaction, + baton FooPSMFullBaton, + state *FooState, + event *FooEvent, + ) error { + return cb(ctx, tx, state, event) + }, + RunOnFollow: true, + } +} + +// FooPSMEventPublishHook EventPublishHook runs for each transition, at least once before committing a database transaction after multiple transitions are complete. It should publish a derived version of the event using the publisher. +func FooPSMEventPublishHook( + cb func( + context.Context, + psm.Publisher, + *FooState, + *FooEvent, + ) error) psm.GeneralEventHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] { + return psm.GeneralEventHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func( + ctx context.Context, + tx sqrlx.Transaction, + baton FooPSMFullBaton, + state *FooState, + event *FooEvent, + ) error { + return cb(ctx, baton, state, event) + }, + RunOnFollow: false, + } +} + +// FooPSMUpsertPublishHook runs for each transition, at least once before committing a database transaction after multiple transitions are complete. It should publish a derived version of the event using the publisher. +func FooPSMUpsertPublishHook( + cb func( + context.Context, + psm.Publisher, + *FooState, + ) error) psm.GeneralStateHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent +] { + return psm.GeneralStateHook[ + *FooKeys, // implements psm.IKeyset + *FooState, // implements psm.IState + FooStatus, // implements psm.IStatusEnum + *FooData, // implements psm.IStateData + *FooEvent, // implements psm.IEvent + FooPSMEvent, // implements psm.IInnerEvent + ]{ + Callback: func( + ctx context.Context, + tx sqrlx.Transaction, + baton FooPSMFullBaton, + state *FooState, + ) error { + return cb(ctx, baton, state) + }, + RunOnFollow: false, + } +} + +func (event *FooEvent) EventPublishMetadata() *psm_j5pb.EventPublishMetadata { + tenantKeys := make([]*psm_j5pb.EventTenant, 0) + if event.Keys.TenantId != nil { + tenantKeys = append(tenantKeys, &psm_j5pb.EventTenant{ + TenantType: "tenant", + TenantId: *event.Keys.TenantId, + }) + } + return &psm_j5pb.EventPublishMetadata{ + EventId: event.Metadata.EventId, + Sequence: event.Metadata.Sequence, + Timestamp: event.Metadata.Timestamp, + Cause: event.Metadata.Cause, + Auth: &psm_j5pb.PublishAuth{ + TenantKeys: tenantKeys, + }, + } +} diff --git a/internal/testproto/gen/test/v1/test_pb/foo.j5s_sugar.pb.go b/internal/testproto/gen/test/v1/test_pb/foo.j5s_sugar.pb.go new file mode 100644 index 0000000..59173e5 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_pb/foo.j5s_sugar.pb.go @@ -0,0 +1,120 @@ +// Code generated by protoc-gen-go-sugar. DO NOT EDIT. + +package test_pb + +import ( + driver "database/sql/driver" + fmt "fmt" + proto "google.golang.org/protobuf/proto" +) + +// FooEventType is a oneof wrapper +type FooEventTypeKey string + +const ( + FooEvent_Created FooEventTypeKey = "created" + FooEvent_Updated FooEventTypeKey = "updated" + FooEvent_Deleted FooEventTypeKey = "deleted" +) + +func (x *FooEventType) TypeKey() (FooEventTypeKey, bool) { + switch x.Type.(type) { + case *FooEventType_Created_: + return FooEvent_Created, true + case *FooEventType_Updated_: + return FooEvent_Updated, true + case *FooEventType_Deleted_: + return FooEvent_Deleted, true + default: + return "", false + } +} + +type IsFooEventTypeWrappedType interface { + TypeKey() FooEventTypeKey + proto.Message +} + +func (x *FooEventType) Set(val IsFooEventTypeWrappedType) { + switch v := val.(type) { + case *FooEventType_Created: + x.Type = &FooEventType_Created_{Created: v} + case *FooEventType_Updated: + x.Type = &FooEventType_Updated_{Updated: v} + case *FooEventType_Deleted: + x.Type = &FooEventType_Deleted_{Deleted: v} + } +} +func (x *FooEventType) Get() IsFooEventTypeWrappedType { + switch v := x.Type.(type) { + case *FooEventType_Created_: + return v.Created + case *FooEventType_Updated_: + return v.Updated + case *FooEventType_Deleted_: + return v.Deleted + default: + return nil + } +} +func (x *FooEventType_Created) TypeKey() FooEventTypeKey { + return FooEvent_Created +} +func (x *FooEventType_Updated) TypeKey() FooEventTypeKey { + return FooEvent_Updated +} +func (x *FooEventType_Deleted) TypeKey() FooEventTypeKey { + return FooEvent_Deleted +} + +type IsFooEventType_Type = isFooEventType_Type + +// FooStatus +const ( + FooStatus_UNSPECIFIED FooStatus = 0 + FooStatus_ACTIVE FooStatus = 1 + FooStatus_DELETED FooStatus = 2 +) + +var ( + FooStatus_name_short = map[int32]string{ + 0: "UNSPECIFIED", + 1: "ACTIVE", + 2: "DELETED", + } + FooStatus_value_short = map[string]int32{ + "UNSPECIFIED": 0, + "ACTIVE": 1, + "DELETED": 2, + } + FooStatus_value_either = map[string]int32{ + "UNSPECIFIED": 0, + "FOO_STATUS_UNSPECIFIED": 0, + "ACTIVE": 1, + "FOO_STATUS_ACTIVE": 1, + "DELETED": 2, + "FOO_STATUS_DELETED": 2, + } +) + +// ShortString returns the un-prefixed string representation of the enum value +func (x FooStatus) ShortString() string { + return FooStatus_name_short[int32(x)] +} +func (x FooStatus) Value() (driver.Value, error) { + return []uint8(x.ShortString()), nil +} +func (x *FooStatus) Scan(value interface{}) error { + var strVal string + switch vt := value.(type) { + case []uint8: + strVal = string(vt) + case string: + strVal = vt + default: + return fmt.Errorf("invalid type %T", value) + } + val := FooStatus_value_either[strVal] + *x = FooStatus(val) + return nil +} diff --git a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s.pb.go b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s.pb.go new file mode 100644 index 0000000..4db2aa6 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s.pb.go @@ -0,0 +1,649 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc (unknown) +// source: test/v1/service/bar.p.j5s.proto + +package test_spb + +import ( + _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" + _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" + list_j5pb "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" + date_j5t "github.com/pentops/j5/j5types/date_j5t" + test_pb "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type BarGetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BarId string `protobuf:"bytes,1,opt,name=bar_id,json=barId,proto3" json:"bar_id,omitempty"` + BarOtherId string `protobuf:"bytes,2,opt,name=bar_other_id,json=barOtherId,proto3" json:"bar_other_id,omitempty"` + DateKey *date_j5t.Date `protobuf:"bytes,3,opt,name=date_key,json=dateKey,proto3" json:"date_key,omitempty"` +} + +func (x *BarGetRequest) Reset() { + *x = BarGetRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarGetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarGetRequest) ProtoMessage() {} + +func (x *BarGetRequest) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarGetRequest.ProtoReflect.Descriptor instead. +func (*BarGetRequest) Descriptor() ([]byte, []int) { + return file_test_v1_service_bar_p_j5s_proto_rawDescGZIP(), []int{0} +} + +func (x *BarGetRequest) GetBarId() string { + if x != nil { + return x.BarId + } + return "" +} + +func (x *BarGetRequest) GetBarOtherId() string { + if x != nil { + return x.BarOtherId + } + return "" +} + +func (x *BarGetRequest) GetDateKey() *date_j5t.Date { + if x != nil { + return x.DateKey + } + return nil +} + +type BarGetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Bar *test_pb.BarState `protobuf:"bytes,1,opt,name=bar,proto3" json:"bar,omitempty"` +} + +func (x *BarGetResponse) Reset() { + *x = BarGetResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarGetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarGetResponse) ProtoMessage() {} + +func (x *BarGetResponse) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarGetResponse.ProtoReflect.Descriptor instead. +func (*BarGetResponse) Descriptor() ([]byte, []int) { + return file_test_v1_service_bar_p_j5s_proto_rawDescGZIP(), []int{1} +} + +func (x *BarGetResponse) GetBar() *test_pb.BarState { + if x != nil { + return x.Bar + } + return nil +} + +type BarListRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Page *list_j5pb.PageRequest `protobuf:"bytes,1,opt,name=page,proto3" json:"page,omitempty"` + Query *list_j5pb.QueryRequest `protobuf:"bytes,2,opt,name=query,proto3" json:"query,omitempty"` +} + +func (x *BarListRequest) Reset() { + *x = BarListRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarListRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarListRequest) ProtoMessage() {} + +func (x *BarListRequest) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarListRequest.ProtoReflect.Descriptor instead. +func (*BarListRequest) Descriptor() ([]byte, []int) { + return file_test_v1_service_bar_p_j5s_proto_rawDescGZIP(), []int{2} +} + +func (x *BarListRequest) GetPage() *list_j5pb.PageRequest { + if x != nil { + return x.Page + } + return nil +} + +func (x *BarListRequest) GetQuery() *list_j5pb.QueryRequest { + if x != nil { + return x.Query + } + return nil +} + +type BarListResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Bar []*test_pb.BarState `protobuf:"bytes,1,rep,name=bar,proto3" json:"bar,omitempty"` + Page *list_j5pb.PageResponse `protobuf:"bytes,2,opt,name=page,proto3" json:"page,omitempty"` +} + +func (x *BarListResponse) Reset() { + *x = BarListResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarListResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarListResponse) ProtoMessage() {} + +func (x *BarListResponse) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarListResponse.ProtoReflect.Descriptor instead. +func (*BarListResponse) Descriptor() ([]byte, []int) { + return file_test_v1_service_bar_p_j5s_proto_rawDescGZIP(), []int{3} +} + +func (x *BarListResponse) GetBar() []*test_pb.BarState { + if x != nil { + return x.Bar + } + return nil +} + +func (x *BarListResponse) GetPage() *list_j5pb.PageResponse { + if x != nil { + return x.Page + } + return nil +} + +type BarEventsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BarId string `protobuf:"bytes,1,opt,name=bar_id,json=barId,proto3" json:"bar_id,omitempty"` + BarOtherId string `protobuf:"bytes,2,opt,name=bar_other_id,json=barOtherId,proto3" json:"bar_other_id,omitempty"` + DateKey *date_j5t.Date `protobuf:"bytes,3,opt,name=date_key,json=dateKey,proto3" json:"date_key,omitempty"` + Page *list_j5pb.PageRequest `protobuf:"bytes,4,opt,name=page,proto3" json:"page,omitempty"` + Query *list_j5pb.QueryRequest `protobuf:"bytes,5,opt,name=query,proto3" json:"query,omitempty"` +} + +func (x *BarEventsRequest) Reset() { + *x = BarEventsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarEventsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarEventsRequest) ProtoMessage() {} + +func (x *BarEventsRequest) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarEventsRequest.ProtoReflect.Descriptor instead. +func (*BarEventsRequest) Descriptor() ([]byte, []int) { + return file_test_v1_service_bar_p_j5s_proto_rawDescGZIP(), []int{4} +} + +func (x *BarEventsRequest) GetBarId() string { + if x != nil { + return x.BarId + } + return "" +} + +func (x *BarEventsRequest) GetBarOtherId() string { + if x != nil { + return x.BarOtherId + } + return "" +} + +func (x *BarEventsRequest) GetDateKey() *date_j5t.Date { + if x != nil { + return x.DateKey + } + return nil +} + +func (x *BarEventsRequest) GetPage() *list_j5pb.PageRequest { + if x != nil { + return x.Page + } + return nil +} + +func (x *BarEventsRequest) GetQuery() *list_j5pb.QueryRequest { + if x != nil { + return x.Query + } + return nil +} + +type BarEventsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Events []*test_pb.BarEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` + Page *list_j5pb.PageResponse `protobuf:"bytes,2,opt,name=page,proto3" json:"page,omitempty"` +} + +func (x *BarEventsResponse) Reset() { + *x = BarEventsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarEventsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarEventsResponse) ProtoMessage() {} + +func (x *BarEventsResponse) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_bar_p_j5s_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarEventsResponse.ProtoReflect.Descriptor instead. +func (*BarEventsResponse) Descriptor() ([]byte, []int) { + return file_test_v1_service_bar_p_j5s_proto_rawDescGZIP(), []int{5} +} + +func (x *BarEventsResponse) GetEvents() []*test_pb.BarEvent { + if x != nil { + return x.Events + } + return nil +} + +func (x *BarEventsResponse) GetPage() *list_j5pb.PageResponse { + if x != nil { + return x.Page + } + return nil +} + +var File_test_v1_service_bar_p_j5s_proto protoreflect.FileDescriptor + +var file_test_v1_service_bar_p_j5s_proto_rawDesc = []byte{ + 0x0a, 0x1f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x2f, 0x62, 0x61, 0x72, 0x2e, 0x70, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x0f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, + 0x35, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x6a, 0x35, 0x2f, 0x6c, + 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x16, 0x6a, 0x35, 0x2f, 0x6c, 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x71, 0x75, + 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, 0x35, 0x2f, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2f, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x61, 0x74, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, + 0x62, 0x61, 0x72, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcf, 0x01, + 0x0a, 0x0d, 0x42, 0x61, 0x72, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x33, 0x0a, 0x06, 0x62, 0x61, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x1c, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, + 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x05, 0x62, + 0x61, 0x72, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0c, 0x62, 0x61, 0x72, 0x5f, 0x6f, 0x74, 0x68, 0x65, + 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1c, 0xba, 0x48, 0x08, 0xc8, + 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, + 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x0a, 0x62, 0x61, 0x72, 0x4f, 0x74, 0x68, + 0x65, 0x72, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6a, 0x35, 0x2e, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x65, 0x42, 0x0d, + 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x07, 0x64, + 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, + 0x4d, 0x0a, 0x0e, 0x42, 0x61, 0x72, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x32, 0x0a, 0x03, 0x62, 0x61, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, + 0x52, 0x03, 0x62, 0x61, 0x72, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0x88, + 0x01, 0x0a, 0x0e, 0x42, 0x61, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, + 0x00, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, + 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0x80, 0x01, 0x0a, 0x0f, 0x42, 0x61, + 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, + 0x03, 0x62, 0x61, 0x72, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x65, 0x73, + 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x42, 0x08, 0xc2, + 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x03, 0x62, 0x61, 0x72, 0x12, 0x35, 0x0a, 0x04, + 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6a, 0x35, 0x2e, + 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x70, + 0x61, 0x67, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0xc1, 0x02, 0x0a, + 0x10, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x33, 0x0a, 0x06, 0x62, 0x61, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x1c, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, + 0x8e, 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, + 0x05, 0x62, 0x61, 0x72, 0x49, 0x64, 0x12, 0x3e, 0x0a, 0x0c, 0x62, 0x61, 0x72, 0x5f, 0x6f, 0x74, + 0x68, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x1c, 0xba, 0x48, + 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, 0x02, + 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, 0x0a, 0x62, 0x61, 0x72, 0x4f, + 0x74, 0x68, 0x65, 0x72, 0x49, 0x64, 0x12, 0x40, 0x0a, 0x08, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6b, + 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x6a, 0x35, 0x2e, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2e, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x61, 0x74, 0x65, + 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x52, + 0x07, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, + 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x37, + 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, + 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, + 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, + 0x22, 0x88, 0x01, 0x0a, 0x11, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, + 0x2e, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, + 0xaa, 0x01, 0x00, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x35, 0x0a, 0x04, 0x70, + 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, + 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x70, 0x61, + 0x67, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x32, 0xbe, 0x03, 0x0a, 0x0f, + 0x42, 0x61, 0x72, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x8d, 0x01, 0x0a, 0x06, 0x42, 0x61, 0x72, 0x47, 0x65, 0x74, 0x12, 0x1e, 0x2e, 0x74, 0x65, 0x73, + 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, 0x61, 0x72, + 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x74, 0x65, 0x73, + 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, 0x61, 0x72, + 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x42, 0xc2, 0xff, 0x8e, + 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x33, 0x12, 0x31, 0x2f, 0x74, + 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x61, 0x72, 0x2f, 0x71, 0x2f, 0x7b, 0x62, 0x61, + 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x7b, 0x62, 0x61, 0x72, 0x5f, 0x6f, 0x74, 0x68, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x7b, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x7d, 0x12, + 0x6d, 0x0a, 0x07, 0x42, 0x61, 0x72, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x1f, 0x2e, 0x74, 0x65, 0x73, + 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, 0x61, 0x72, + 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x74, 0x65, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, 0x61, + 0x72, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1f, 0xc2, + 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x10, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12, 0x0e, + 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x61, 0x72, 0x2f, 0x71, 0x12, 0x9d, + 0x01, 0x0a, 0x09, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x21, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x42, + 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x22, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x2e, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x49, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x18, 0x01, 0x82, 0xd3, + 0xe4, 0x93, 0x02, 0x3a, 0x12, 0x38, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x62, + 0x61, 0x72, 0x2f, 0x71, 0x2f, 0x7b, 0x62, 0x61, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x7b, 0x62, + 0x61, 0x72, 0x5f, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x7b, 0x64, 0x61, + 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x7d, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x0c, + 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x05, 0x0a, 0x03, 0x62, 0x61, 0x72, 0x42, 0x47, 0x5a, 0x45, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, + 0x70, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, + 0x74, 0x5f, 0x73, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_test_v1_service_bar_p_j5s_proto_rawDescOnce sync.Once + file_test_v1_service_bar_p_j5s_proto_rawDescData = file_test_v1_service_bar_p_j5s_proto_rawDesc +) + +func file_test_v1_service_bar_p_j5s_proto_rawDescGZIP() []byte { + file_test_v1_service_bar_p_j5s_proto_rawDescOnce.Do(func() { + file_test_v1_service_bar_p_j5s_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_v1_service_bar_p_j5s_proto_rawDescData) + }) + return file_test_v1_service_bar_p_j5s_proto_rawDescData +} + +var file_test_v1_service_bar_p_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_test_v1_service_bar_p_j5s_proto_goTypes = []interface{}{ + (*BarGetRequest)(nil), // 0: test.v1.service.BarGetRequest + (*BarGetResponse)(nil), // 1: test.v1.service.BarGetResponse + (*BarListRequest)(nil), // 2: test.v1.service.BarListRequest + (*BarListResponse)(nil), // 3: test.v1.service.BarListResponse + (*BarEventsRequest)(nil), // 4: test.v1.service.BarEventsRequest + (*BarEventsResponse)(nil), // 5: test.v1.service.BarEventsResponse + (*date_j5t.Date)(nil), // 6: j5.types.date.v1.Date + (*test_pb.BarState)(nil), // 7: test.v1.BarState + (*list_j5pb.PageRequest)(nil), // 8: j5.list.v1.PageRequest + (*list_j5pb.QueryRequest)(nil), // 9: j5.list.v1.QueryRequest + (*list_j5pb.PageResponse)(nil), // 10: j5.list.v1.PageResponse + (*test_pb.BarEvent)(nil), // 11: test.v1.BarEvent +} +var file_test_v1_service_bar_p_j5s_proto_depIdxs = []int32{ + 6, // 0: test.v1.service.BarGetRequest.date_key:type_name -> j5.types.date.v1.Date + 7, // 1: test.v1.service.BarGetResponse.bar:type_name -> test.v1.BarState + 8, // 2: test.v1.service.BarListRequest.page:type_name -> j5.list.v1.PageRequest + 9, // 3: test.v1.service.BarListRequest.query:type_name -> j5.list.v1.QueryRequest + 7, // 4: test.v1.service.BarListResponse.bar:type_name -> test.v1.BarState + 10, // 5: test.v1.service.BarListResponse.page:type_name -> j5.list.v1.PageResponse + 6, // 6: test.v1.service.BarEventsRequest.date_key:type_name -> j5.types.date.v1.Date + 8, // 7: test.v1.service.BarEventsRequest.page:type_name -> j5.list.v1.PageRequest + 9, // 8: test.v1.service.BarEventsRequest.query:type_name -> j5.list.v1.QueryRequest + 11, // 9: test.v1.service.BarEventsResponse.events:type_name -> test.v1.BarEvent + 10, // 10: test.v1.service.BarEventsResponse.page:type_name -> j5.list.v1.PageResponse + 0, // 11: test.v1.service.BarQueryService.BarGet:input_type -> test.v1.service.BarGetRequest + 2, // 12: test.v1.service.BarQueryService.BarList:input_type -> test.v1.service.BarListRequest + 4, // 13: test.v1.service.BarQueryService.BarEvents:input_type -> test.v1.service.BarEventsRequest + 1, // 14: test.v1.service.BarQueryService.BarGet:output_type -> test.v1.service.BarGetResponse + 3, // 15: test.v1.service.BarQueryService.BarList:output_type -> test.v1.service.BarListResponse + 5, // 16: test.v1.service.BarQueryService.BarEvents:output_type -> test.v1.service.BarEventsResponse + 14, // [14:17] is the sub-list for method output_type + 11, // [11:14] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name +} + +func init() { file_test_v1_service_bar_p_j5s_proto_init() } +func file_test_v1_service_bar_p_j5s_proto_init() { + if File_test_v1_service_bar_p_j5s_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_test_v1_service_bar_p_j5s_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarGetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_service_bar_p_j5s_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarGetResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_service_bar_p_j5s_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarListRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_service_bar_p_j5s_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarListResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_service_bar_p_j5s_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarEventsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_service_bar_p_j5s_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarEventsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_test_v1_service_bar_p_j5s_proto_rawDesc, + NumEnums: 0, + NumMessages: 6, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_test_v1_service_bar_p_j5s_proto_goTypes, + DependencyIndexes: file_test_v1_service_bar_p_j5s_proto_depIdxs, + MessageInfos: file_test_v1_service_bar_p_j5s_proto_msgTypes, + }.Build() + File_test_v1_service_bar_p_j5s_proto = out.File + file_test_v1_service_bar_p_j5s_proto_rawDesc = nil + file_test_v1_service_bar_p_j5s_proto_goTypes = nil + file_test_v1_service_bar_p_j5s_proto_depIdxs = nil +} diff --git a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_grpc.pb.go b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_grpc.pb.go new file mode 100644 index 0000000..c21a6a7 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_grpc.pb.go @@ -0,0 +1,186 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.4.0 +// - protoc (unknown) +// source: test/v1/service/bar.p.j5s.proto + +package test_spb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 + +const ( + BarQueryService_BarGet_FullMethodName = "/test.v1.service.BarQueryService/BarGet" + BarQueryService_BarList_FullMethodName = "/test.v1.service.BarQueryService/BarList" + BarQueryService_BarEvents_FullMethodName = "/test.v1.service.BarQueryService/BarEvents" +) + +// BarQueryServiceClient is the client API for BarQueryService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type BarQueryServiceClient interface { + BarGet(ctx context.Context, in *BarGetRequest, opts ...grpc.CallOption) (*BarGetResponse, error) + BarList(ctx context.Context, in *BarListRequest, opts ...grpc.CallOption) (*BarListResponse, error) + BarEvents(ctx context.Context, in *BarEventsRequest, opts ...grpc.CallOption) (*BarEventsResponse, error) +} + +type barQueryServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewBarQueryServiceClient(cc grpc.ClientConnInterface) BarQueryServiceClient { + return &barQueryServiceClient{cc} +} + +func (c *barQueryServiceClient) BarGet(ctx context.Context, in *BarGetRequest, opts ...grpc.CallOption) (*BarGetResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(BarGetResponse) + err := c.cc.Invoke(ctx, BarQueryService_BarGet_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *barQueryServiceClient) BarList(ctx context.Context, in *BarListRequest, opts ...grpc.CallOption) (*BarListResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(BarListResponse) + err := c.cc.Invoke(ctx, BarQueryService_BarList_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *barQueryServiceClient) BarEvents(ctx context.Context, in *BarEventsRequest, opts ...grpc.CallOption) (*BarEventsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(BarEventsResponse) + err := c.cc.Invoke(ctx, BarQueryService_BarEvents_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// BarQueryServiceServer is the server API for BarQueryService service. +// All implementations must embed UnimplementedBarQueryServiceServer +// for forward compatibility +type BarQueryServiceServer interface { + BarGet(context.Context, *BarGetRequest) (*BarGetResponse, error) + BarList(context.Context, *BarListRequest) (*BarListResponse, error) + BarEvents(context.Context, *BarEventsRequest) (*BarEventsResponse, error) + mustEmbedUnimplementedBarQueryServiceServer() +} + +// UnimplementedBarQueryServiceServer must be embedded to have forward compatible implementations. +type UnimplementedBarQueryServiceServer struct { +} + +func (UnimplementedBarQueryServiceServer) BarGet(context.Context, *BarGetRequest) (*BarGetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BarGet not implemented") +} +func (UnimplementedBarQueryServiceServer) BarList(context.Context, *BarListRequest) (*BarListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BarList not implemented") +} +func (UnimplementedBarQueryServiceServer) BarEvents(context.Context, *BarEventsRequest) (*BarEventsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method BarEvents not implemented") +} +func (UnimplementedBarQueryServiceServer) mustEmbedUnimplementedBarQueryServiceServer() {} + +// UnsafeBarQueryServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to BarQueryServiceServer will +// result in compilation errors. +type UnsafeBarQueryServiceServer interface { + mustEmbedUnimplementedBarQueryServiceServer() +} + +func RegisterBarQueryServiceServer(s grpc.ServiceRegistrar, srv BarQueryServiceServer) { + s.RegisterService(&BarQueryService_ServiceDesc, srv) +} + +func _BarQueryService_BarGet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BarGetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BarQueryServiceServer).BarGet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: BarQueryService_BarGet_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BarQueryServiceServer).BarGet(ctx, req.(*BarGetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BarQueryService_BarList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BarListRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BarQueryServiceServer).BarList(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: BarQueryService_BarList_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BarQueryServiceServer).BarList(ctx, req.(*BarListRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _BarQueryService_BarEvents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BarEventsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BarQueryServiceServer).BarEvents(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: BarQueryService_BarEvents_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BarQueryServiceServer).BarEvents(ctx, req.(*BarEventsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// BarQueryService_ServiceDesc is the grpc.ServiceDesc for BarQueryService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var BarQueryService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "test.v1.service.BarQueryService", + HandlerType: (*BarQueryServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "BarGet", + Handler: _BarQueryService_BarGet_Handler, + }, + { + MethodName: "BarList", + Handler: _BarQueryService_BarList_Handler, + }, + { + MethodName: "BarEvents", + Handler: _BarQueryService_BarEvents_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "test/v1/service/bar.p.j5s.proto", +} diff --git a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_j5.pb.go b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_j5.pb.go index f9e6ba3..7748444 100644 --- a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_j5.pb.go +++ b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_j5.pb.go @@ -4,23 +4,72 @@ package test_spb import ( j5reflect "github.com/pentops/j5/lib/j5reflect" + proto "google.golang.org/protobuf/proto" ) func (msg *BarGetRequest) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarGetRequest) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarGetRequest) Clone() any { + return proto.Clone(msg).(*BarGetRequest) } func (msg *BarGetResponse) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarGetResponse) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarGetResponse) Clone() any { + return proto.Clone(msg).(*BarGetResponse) } func (msg *BarListRequest) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarListRequest) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarListRequest) Clone() any { + return proto.Clone(msg).(*BarListRequest) } func (msg *BarListResponse) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarListResponse) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarListResponse) Clone() any { + return proto.Clone(msg).(*BarListResponse) } func (msg *BarEventsRequest) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarEventsRequest) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarEventsRequest) Clone() any { + return proto.Clone(msg).(*BarEventsRequest) } func (msg *BarEventsResponse) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarEventsResponse) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarEventsResponse) Clone() any { + return proto.Clone(msg).(*BarEventsResponse) } diff --git a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_psm_query.pb.go b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_psm_query.pb.go new file mode 100644 index 0000000..70f9c05 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_psm_query.pb.go @@ -0,0 +1,105 @@ +// Code generated by protoc-gen-go-psm. DO NOT EDIT. + +package test_spb + +import ( + context "context" + fmt "fmt" + j5reflect "github.com/pentops/j5/lib/j5reflect" + j5schema "github.com/pentops/j5/lib/j5schema" + psm "github.com/pentops/protostate/psm" + sqrlx "github.com/pentops/sqrlx.go/sqrlx" +) + +// State Query Service for %sBar +// QuerySet is the query set for the Bar service. + +type BarPSMQuerySet = psm.StateQuerySet + +func NewBarPSMQuerySet( + smSpec psm.QuerySpec, + options psm.StateQueryOptions, +) (*BarPSMQuerySet, error) { + return psm.BuildStateQuerySet(smSpec, options) +} + +type BarPSMQuerySpec = psm.QuerySpec + +func DefaultBarPSMQuerySpec(tableSpec psm.QueryTableSpec) BarPSMQuerySpec { + return psm.QuerySpec{ + GetMethod: &j5schema.MethodSchema{ + Request: j5schema.MustObjectSchema((&BarGetRequest{}).ProtoReflect().Descriptor()), + Response: j5schema.MustObjectSchema((&BarGetResponse{}).ProtoReflect().Descriptor()), + }, + ListMethod: &j5schema.MethodSchema{ + Request: j5schema.MustObjectSchema((&BarListRequest{}).ProtoReflect().Descriptor()), + Response: j5schema.MustObjectSchema((&BarListResponse{}).ProtoReflect().Descriptor()), + }, + ListEventsMethod: &j5schema.MethodSchema{ + Request: j5schema.MustObjectSchema((&BarEventsRequest{}).ProtoReflect().Descriptor()), + Response: j5schema.MustObjectSchema((&BarEventsResponse{}).ProtoReflect().Descriptor()), + }, + QueryTableSpec: tableSpec, + ListRequestFilter: func(reqReflect j5reflect.Object) (map[string]interface{}, error) { + req, ok := reqReflect.Interface().(*BarListRequest) + if !ok { + return nil, fmt.Errorf("expected *BarListRequest but got %T", req) + } + filter := map[string]interface{}{} + return filter, nil + }, + ListEventsRequestFilter: func(reqReflect j5reflect.Object) (map[string]interface{}, error) { + req, ok := reqReflect.Interface().(*BarEventsRequest) + if !ok { + return nil, fmt.Errorf("expected *BarEventsRequest but got %T", req) + } + filter := map[string]interface{}{} + filter["bar_id"] = req.BarId + filter["bar_other_id"] = req.BarOtherId + filter["date_key"] = req.DateKey + return filter, nil + }, + } +} + +type BarQueryServiceImpl struct { + db sqrlx.Transactor + querySet *BarPSMQuerySet + UnsafeBarQueryServiceServer +} + +var _ BarQueryServiceServer = &BarQueryServiceImpl{} + +func NewBarQueryServiceImpl(db sqrlx.Transactor, querySet *BarPSMQuerySet) *BarQueryServiceImpl { + return &BarQueryServiceImpl{ + db: db, + querySet: querySet, + } +} + +func (s *BarQueryServiceImpl) BarGet(ctx context.Context, req *BarGetRequest) (*BarGetResponse, error) { + resObject := &BarGetResponse{} + err := s.querySet.Get(ctx, s.db, req.J5Object(), resObject.J5Object()) + if err != nil { + return nil, err + } + return resObject, nil +} + +func (s *BarQueryServiceImpl) BarList(ctx context.Context, req *BarListRequest) (*BarListResponse, error) { + resObject := &BarListResponse{} + err := s.querySet.List(ctx, s.db, req.J5Object(), resObject.J5Object()) + if err != nil { + return nil, err + } + return resObject, nil +} + +func (s *BarQueryServiceImpl) BarEvents(ctx context.Context, req *BarEventsRequest) (*BarEventsResponse, error) { + resObject := &BarEventsResponse{} + err := s.querySet.ListEvents(ctx, s.db, req.J5Object(), resObject.J5Object()) + if err != nil { + return nil, err + } + return resObject, nil +} diff --git a/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_sugar.pb.go b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_sugar.pb.go new file mode 100644 index 0000000..2bfa669 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_spb/bar.p.j5s_sugar.pb.go @@ -0,0 +1,3 @@ +// Code generated by protoc-gen-go-sugar. DO NOT EDIT. + +package test_spb diff --git a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s.pb.go b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s.pb.go new file mode 100644 index 0000000..9737caa --- /dev/null +++ b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s.pb.go @@ -0,0 +1,768 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc (unknown) +// source: test/v1/service/foo.p.j5s.proto + +package test_spb + +import ( + _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" + _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" + list_j5pb "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" + test_pb "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FooGetRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FooId string `protobuf:"bytes,1,opt,name=foo_id,json=fooId,proto3" json:"foo_id,omitempty"` +} + +func (x *FooGetRequest) Reset() { + *x = FooGetRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooGetRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooGetRequest) ProtoMessage() {} + +func (x *FooGetRequest) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooGetRequest.ProtoReflect.Descriptor instead. +func (*FooGetRequest) Descriptor() ([]byte, []int) { + return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{0} +} + +func (x *FooGetRequest) GetFooId() string { + if x != nil { + return x.FooId + } + return "" +} + +type FooGetResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Foo *test_pb.FooState `protobuf:"bytes,1,opt,name=foo,proto3" json:"foo,omitempty"` + Events []*test_pb.FooEvent `protobuf:"bytes,2,rep,name=events,proto3" json:"events,omitempty"` +} + +func (x *FooGetResponse) Reset() { + *x = FooGetResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooGetResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooGetResponse) ProtoMessage() {} + +func (x *FooGetResponse) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooGetResponse.ProtoReflect.Descriptor instead. +func (*FooGetResponse) Descriptor() ([]byte, []int) { + return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{1} +} + +func (x *FooGetResponse) GetFoo() *test_pb.FooState { + if x != nil { + return x.Foo + } + return nil +} + +func (x *FooGetResponse) GetEvents() []*test_pb.FooEvent { + if x != nil { + return x.Events + } + return nil +} + +type FooListRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Page *list_j5pb.PageRequest `protobuf:"bytes,1,opt,name=page,proto3" json:"page,omitempty"` + Query *list_j5pb.QueryRequest `protobuf:"bytes,2,opt,name=query,proto3" json:"query,omitempty"` +} + +func (x *FooListRequest) Reset() { + *x = FooListRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooListRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooListRequest) ProtoMessage() {} + +func (x *FooListRequest) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooListRequest.ProtoReflect.Descriptor instead. +func (*FooListRequest) Descriptor() ([]byte, []int) { + return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{2} +} + +func (x *FooListRequest) GetPage() *list_j5pb.PageRequest { + if x != nil { + return x.Page + } + return nil +} + +func (x *FooListRequest) GetQuery() *list_j5pb.QueryRequest { + if x != nil { + return x.Query + } + return nil +} + +type FooListResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Foo []*test_pb.FooState `protobuf:"bytes,1,rep,name=foo,proto3" json:"foo,omitempty"` + Page *list_j5pb.PageResponse `protobuf:"bytes,2,opt,name=page,proto3" json:"page,omitempty"` +} + +func (x *FooListResponse) Reset() { + *x = FooListResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooListResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooListResponse) ProtoMessage() {} + +func (x *FooListResponse) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooListResponse.ProtoReflect.Descriptor instead. +func (*FooListResponse) Descriptor() ([]byte, []int) { + return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{3} +} + +func (x *FooListResponse) GetFoo() []*test_pb.FooState { + if x != nil { + return x.Foo + } + return nil +} + +func (x *FooListResponse) GetPage() *list_j5pb.PageResponse { + if x != nil { + return x.Page + } + return nil +} + +type FooEventsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + FooId string `protobuf:"bytes,1,opt,name=foo_id,json=fooId,proto3" json:"foo_id,omitempty"` + Page *list_j5pb.PageRequest `protobuf:"bytes,2,opt,name=page,proto3" json:"page,omitempty"` + Query *list_j5pb.QueryRequest `protobuf:"bytes,3,opt,name=query,proto3" json:"query,omitempty"` +} + +func (x *FooEventsRequest) Reset() { + *x = FooEventsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooEventsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooEventsRequest) ProtoMessage() {} + +func (x *FooEventsRequest) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooEventsRequest.ProtoReflect.Descriptor instead. +func (*FooEventsRequest) Descriptor() ([]byte, []int) { + return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{4} +} + +func (x *FooEventsRequest) GetFooId() string { + if x != nil { + return x.FooId + } + return "" +} + +func (x *FooEventsRequest) GetPage() *list_j5pb.PageRequest { + if x != nil { + return x.Page + } + return nil +} + +func (x *FooEventsRequest) GetQuery() *list_j5pb.QueryRequest { + if x != nil { + return x.Query + } + return nil +} + +type FooEventsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Events []*test_pb.FooEvent `protobuf:"bytes,1,rep,name=events,proto3" json:"events,omitempty"` + Page *list_j5pb.PageResponse `protobuf:"bytes,2,opt,name=page,proto3" json:"page,omitempty"` +} + +func (x *FooEventsResponse) Reset() { + *x = FooEventsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooEventsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooEventsResponse) ProtoMessage() {} + +func (x *FooEventsResponse) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooEventsResponse.ProtoReflect.Descriptor instead. +func (*FooEventsResponse) Descriptor() ([]byte, []int) { + return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{5} +} + +func (x *FooEventsResponse) GetEvents() []*test_pb.FooEvent { + if x != nil { + return x.Events + } + return nil +} + +func (x *FooEventsResponse) GetPage() *list_j5pb.PageResponse { + if x != nil { + return x.Page + } + return nil +} + +type FooSummaryRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *FooSummaryRequest) Reset() { + *x = FooSummaryRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooSummaryRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooSummaryRequest) ProtoMessage() {} + +func (x *FooSummaryRequest) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooSummaryRequest.ProtoReflect.Descriptor instead. +func (*FooSummaryRequest) Descriptor() ([]byte, []int) { + return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{6} +} + +type FooSummaryResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + CountFoos int64 `protobuf:"varint,1,opt,name=count_foos,json=countFoos,proto3" json:"count_foos,omitempty"` + TotalWeight int64 `protobuf:"varint,2,opt,name=total_weight,json=totalWeight,proto3" json:"total_weight,omitempty"` + TotalHeight int64 `protobuf:"varint,3,opt,name=total_height,json=totalHeight,proto3" json:"total_height,omitempty"` + TotalLength int64 `protobuf:"varint,4,opt,name=total_length,json=totalLength,proto3" json:"total_length,omitempty"` +} + +func (x *FooSummaryResponse) Reset() { + *x = FooSummaryResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooSummaryResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooSummaryResponse) ProtoMessage() {} + +func (x *FooSummaryResponse) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_service_foo_p_j5s_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooSummaryResponse.ProtoReflect.Descriptor instead. +func (*FooSummaryResponse) Descriptor() ([]byte, []int) { + return file_test_v1_service_foo_p_j5s_proto_rawDescGZIP(), []int{7} +} + +func (x *FooSummaryResponse) GetCountFoos() int64 { + if x != nil { + return x.CountFoos + } + return 0 +} + +func (x *FooSummaryResponse) GetTotalWeight() int64 { + if x != nil { + return x.TotalWeight + } + return 0 +} + +func (x *FooSummaryResponse) GetTotalHeight() int64 { + if x != nil { + return x.TotalHeight + } + return 0 +} + +func (x *FooSummaryResponse) GetTotalLength() int64 { + if x != nil { + return x.TotalLength + } + return 0 +} + +var File_test_v1_service_foo_p_j5s_proto protoreflect.FileDescriptor + +var file_test_v1_service_foo_p_j5s_proto_rawDesc = []byte{ + 0x0a, 0x1f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x2f, 0x66, 0x6f, 0x6f, 0x2e, 0x70, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x0f, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, + 0x35, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6a, 0x35, 0x2f, 0x6c, + 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x6a, 0x35, 0x2f, 0x6c, 0x69, 0x73, + 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x61, 0x67, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, + 0x16, 0x6a, 0x35, 0x2f, 0x6c, 0x69, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, + 0x2f, 0x66, 0x6f, 0x6f, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5d, + 0x0a, 0x0d, 0x46, 0x6f, 0x6f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x43, 0x0a, 0x06, 0x66, 0x6f, 0x6f, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x2c, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, + 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, 0x02, 0x08, 0x01, 0x8a, 0xf7, 0x98, + 0xc6, 0x02, 0x0a, 0x72, 0x08, 0x1a, 0x06, 0x12, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x05, 0x66, + 0x6f, 0x6f, 0x49, 0x64, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0x82, 0x01, + 0x0a, 0x0e, 0x46, 0x6f, 0x6f, 0x47, 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x32, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, + 0x03, 0x66, 0x6f, 0x6f, 0x12, 0x33, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, + 0x00, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, + 0x52, 0x00, 0x22, 0x88, 0x01, 0x0a, 0x0e, 0x46, 0x6f, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, + 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x07, 0xc2, 0xff, + 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6a, 0x35, 0x2e, + 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x05, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0x80, 0x01, + 0x0a, 0x0f, 0x46, 0x6f, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2d, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x03, 0x66, 0x6f, 0x6f, + 0x12, 0x35, 0x0a, 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, + 0x00, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, + 0x22, 0xcf, 0x01, 0x0a, 0x10, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x43, 0x0a, 0x06, 0x66, 0x6f, 0x6f, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x2c, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x72, 0x03, 0xb0, + 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x05, 0xb2, 0x02, 0x02, 0x08, 0x02, 0xea, 0x85, 0x8f, 0x02, + 0x02, 0x08, 0x01, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x72, 0x08, 0x1a, 0x06, 0x12, 0x04, 0x52, + 0x02, 0x08, 0x01, 0x52, 0x05, 0x66, 0x6f, 0x6f, 0x49, 0x64, 0x12, 0x34, 0x0a, 0x04, 0x70, 0x61, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x70, 0x61, 0x67, 0x65, + 0x12, 0x37, 0x0a, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x6a, 0x35, 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, + 0x52, 0x00, 0x52, 0x05, 0x71, 0x75, 0x65, 0x72, 0x79, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, + 0x52, 0x00, 0x22, 0x88, 0x01, 0x0a, 0x11, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, + 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x08, 0xc2, 0xff, 0x8e, + 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x35, 0x0a, + 0x04, 0x70, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6a, 0x35, + 0x2e, 0x6c, 0x69, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, + 0x70, 0x61, 0x67, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0x1c, 0x0a, + 0x11, 0x46, 0x6f, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0xcd, 0x01, 0x0a, 0x12, + 0x46, 0x6f, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x27, 0x0a, 0x0a, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x5f, 0x66, 0x6f, 0x6f, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, + 0x52, 0x09, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x46, 0x6f, 0x6f, 0x73, 0x12, 0x2b, 0x0a, 0x0c, 0x74, + 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x52, 0x0b, 0x74, 0x6f, 0x74, + 0x61, 0x6c, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2b, 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, + 0x6c, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, + 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x48, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x2b, 0x0a, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, + 0x02, 0x03, 0xfa, 0x01, 0x00, 0x52, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x32, 0x89, 0x03, 0x0a, 0x0f, + 0x46, 0x6f, 0x6f, 0x51, 0x75, 0x65, 0x72, 0x79, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x73, 0x0a, 0x06, 0x46, 0x6f, 0x6f, 0x47, 0x65, 0x74, 0x12, 0x1e, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0xc2, 0xff, 0x8e, 0x02, + 0x04, 0x52, 0x02, 0x08, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x19, 0x12, 0x17, 0x2f, 0x74, 0x65, + 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x6f, 0x6f, 0x2f, 0x71, 0x2f, 0x7b, 0x66, 0x6f, 0x6f, + 0x5f, 0x69, 0x64, 0x7d, 0x12, 0x6d, 0x0a, 0x07, 0x46, 0x6f, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x12, + 0x1f, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, + 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x20, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, + 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1f, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x10, 0x01, 0x82, 0xd3, 0xe4, + 0x93, 0x02, 0x10, 0x12, 0x0e, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x6f, + 0x6f, 0x2f, 0x71, 0x12, 0x83, 0x01, 0x0a, 0x09, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x12, 0x21, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2f, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, + 0x02, 0x18, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x12, 0x1e, 0x2f, 0x74, 0x65, 0x73, 0x74, + 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x6f, 0x6f, 0x2f, 0x71, 0x2f, 0x7b, 0x66, 0x6f, 0x6f, 0x5f, 0x69, + 0x64, 0x7d, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x1a, 0x0c, 0xea, 0x85, 0x8f, 0x02, 0x07, + 0x0a, 0x05, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x32, 0x81, 0x01, 0x0a, 0x0a, 0x46, 0x6f, 0x6f, 0x53, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x73, 0x0a, 0x0a, 0x46, 0x6f, 0x6f, 0x53, 0x75, 0x6d, + 0x6d, 0x61, 0x72, 0x79, 0x12, 0x22, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x75, 0x6d, 0x6d, 0x61, 0x72, + 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, + 0x76, 0x31, 0x2e, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x75, + 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1c, 0x82, + 0xd3, 0xe4, 0x93, 0x02, 0x16, 0x12, 0x14, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, + 0x66, 0x6f, 0x6f, 0x2f, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x42, 0x47, 0x5a, 0x45, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, + 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x67, 0x65, 0x6e, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, 0x74, + 0x5f, 0x73, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_test_v1_service_foo_p_j5s_proto_rawDescOnce sync.Once + file_test_v1_service_foo_p_j5s_proto_rawDescData = file_test_v1_service_foo_p_j5s_proto_rawDesc +) + +func file_test_v1_service_foo_p_j5s_proto_rawDescGZIP() []byte { + file_test_v1_service_foo_p_j5s_proto_rawDescOnce.Do(func() { + file_test_v1_service_foo_p_j5s_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_v1_service_foo_p_j5s_proto_rawDescData) + }) + return file_test_v1_service_foo_p_j5s_proto_rawDescData +} + +var file_test_v1_service_foo_p_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_test_v1_service_foo_p_j5s_proto_goTypes = []interface{}{ + (*FooGetRequest)(nil), // 0: test.v1.service.FooGetRequest + (*FooGetResponse)(nil), // 1: test.v1.service.FooGetResponse + (*FooListRequest)(nil), // 2: test.v1.service.FooListRequest + (*FooListResponse)(nil), // 3: test.v1.service.FooListResponse + (*FooEventsRequest)(nil), // 4: test.v1.service.FooEventsRequest + (*FooEventsResponse)(nil), // 5: test.v1.service.FooEventsResponse + (*FooSummaryRequest)(nil), // 6: test.v1.service.FooSummaryRequest + (*FooSummaryResponse)(nil), // 7: test.v1.service.FooSummaryResponse + (*test_pb.FooState)(nil), // 8: test.v1.FooState + (*test_pb.FooEvent)(nil), // 9: test.v1.FooEvent + (*list_j5pb.PageRequest)(nil), // 10: j5.list.v1.PageRequest + (*list_j5pb.QueryRequest)(nil), // 11: j5.list.v1.QueryRequest + (*list_j5pb.PageResponse)(nil), // 12: j5.list.v1.PageResponse +} +var file_test_v1_service_foo_p_j5s_proto_depIdxs = []int32{ + 8, // 0: test.v1.service.FooGetResponse.foo:type_name -> test.v1.FooState + 9, // 1: test.v1.service.FooGetResponse.events:type_name -> test.v1.FooEvent + 10, // 2: test.v1.service.FooListRequest.page:type_name -> j5.list.v1.PageRequest + 11, // 3: test.v1.service.FooListRequest.query:type_name -> j5.list.v1.QueryRequest + 8, // 4: test.v1.service.FooListResponse.foo:type_name -> test.v1.FooState + 12, // 5: test.v1.service.FooListResponse.page:type_name -> j5.list.v1.PageResponse + 10, // 6: test.v1.service.FooEventsRequest.page:type_name -> j5.list.v1.PageRequest + 11, // 7: test.v1.service.FooEventsRequest.query:type_name -> j5.list.v1.QueryRequest + 9, // 8: test.v1.service.FooEventsResponse.events:type_name -> test.v1.FooEvent + 12, // 9: test.v1.service.FooEventsResponse.page:type_name -> j5.list.v1.PageResponse + 0, // 10: test.v1.service.FooQueryService.FooGet:input_type -> test.v1.service.FooGetRequest + 2, // 11: test.v1.service.FooQueryService.FooList:input_type -> test.v1.service.FooListRequest + 4, // 12: test.v1.service.FooQueryService.FooEvents:input_type -> test.v1.service.FooEventsRequest + 6, // 13: test.v1.service.FooService.FooSummary:input_type -> test.v1.service.FooSummaryRequest + 1, // 14: test.v1.service.FooQueryService.FooGet:output_type -> test.v1.service.FooGetResponse + 3, // 15: test.v1.service.FooQueryService.FooList:output_type -> test.v1.service.FooListResponse + 5, // 16: test.v1.service.FooQueryService.FooEvents:output_type -> test.v1.service.FooEventsResponse + 7, // 17: test.v1.service.FooService.FooSummary:output_type -> test.v1.service.FooSummaryResponse + 14, // [14:18] is the sub-list for method output_type + 10, // [10:14] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name +} + +func init() { file_test_v1_service_foo_p_j5s_proto_init() } +func file_test_v1_service_foo_p_j5s_proto_init() { + if File_test_v1_service_foo_p_j5s_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_test_v1_service_foo_p_j5s_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooGetRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_service_foo_p_j5s_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooGetResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_service_foo_p_j5s_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooListRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_service_foo_p_j5s_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooListResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_service_foo_p_j5s_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooEventsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_service_foo_p_j5s_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooEventsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_service_foo_p_j5s_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooSummaryRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_service_foo_p_j5s_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooSummaryResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_test_v1_service_foo_p_j5s_proto_rawDesc, + NumEnums: 0, + NumMessages: 8, + NumExtensions: 0, + NumServices: 2, + }, + GoTypes: file_test_v1_service_foo_p_j5s_proto_goTypes, + DependencyIndexes: file_test_v1_service_foo_p_j5s_proto_depIdxs, + MessageInfos: file_test_v1_service_foo_p_j5s_proto_msgTypes, + }.Build() + File_test_v1_service_foo_p_j5s_proto = out.File + file_test_v1_service_foo_p_j5s_proto_rawDesc = nil + file_test_v1_service_foo_p_j5s_proto_goTypes = nil + file_test_v1_service_foo_p_j5s_proto_depIdxs = nil +} diff --git a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_grpc.pb.go b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_grpc.pb.go new file mode 100644 index 0000000..12ef3a5 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_grpc.pb.go @@ -0,0 +1,277 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.4.0 +// - protoc (unknown) +// source: test/v1/service/foo.p.j5s.proto + +package test_spb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 + +const ( + FooQueryService_FooGet_FullMethodName = "/test.v1.service.FooQueryService/FooGet" + FooQueryService_FooList_FullMethodName = "/test.v1.service.FooQueryService/FooList" + FooQueryService_FooEvents_FullMethodName = "/test.v1.service.FooQueryService/FooEvents" +) + +// FooQueryServiceClient is the client API for FooQueryService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type FooQueryServiceClient interface { + FooGet(ctx context.Context, in *FooGetRequest, opts ...grpc.CallOption) (*FooGetResponse, error) + FooList(ctx context.Context, in *FooListRequest, opts ...grpc.CallOption) (*FooListResponse, error) + FooEvents(ctx context.Context, in *FooEventsRequest, opts ...grpc.CallOption) (*FooEventsResponse, error) +} + +type fooQueryServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewFooQueryServiceClient(cc grpc.ClientConnInterface) FooQueryServiceClient { + return &fooQueryServiceClient{cc} +} + +func (c *fooQueryServiceClient) FooGet(ctx context.Context, in *FooGetRequest, opts ...grpc.CallOption) (*FooGetResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(FooGetResponse) + err := c.cc.Invoke(ctx, FooQueryService_FooGet_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fooQueryServiceClient) FooList(ctx context.Context, in *FooListRequest, opts ...grpc.CallOption) (*FooListResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(FooListResponse) + err := c.cc.Invoke(ctx, FooQueryService_FooList_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *fooQueryServiceClient) FooEvents(ctx context.Context, in *FooEventsRequest, opts ...grpc.CallOption) (*FooEventsResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(FooEventsResponse) + err := c.cc.Invoke(ctx, FooQueryService_FooEvents_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// FooQueryServiceServer is the server API for FooQueryService service. +// All implementations must embed UnimplementedFooQueryServiceServer +// for forward compatibility +type FooQueryServiceServer interface { + FooGet(context.Context, *FooGetRequest) (*FooGetResponse, error) + FooList(context.Context, *FooListRequest) (*FooListResponse, error) + FooEvents(context.Context, *FooEventsRequest) (*FooEventsResponse, error) + mustEmbedUnimplementedFooQueryServiceServer() +} + +// UnimplementedFooQueryServiceServer must be embedded to have forward compatible implementations. +type UnimplementedFooQueryServiceServer struct { +} + +func (UnimplementedFooQueryServiceServer) FooGet(context.Context, *FooGetRequest) (*FooGetResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FooGet not implemented") +} +func (UnimplementedFooQueryServiceServer) FooList(context.Context, *FooListRequest) (*FooListResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FooList not implemented") +} +func (UnimplementedFooQueryServiceServer) FooEvents(context.Context, *FooEventsRequest) (*FooEventsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FooEvents not implemented") +} +func (UnimplementedFooQueryServiceServer) mustEmbedUnimplementedFooQueryServiceServer() {} + +// UnsafeFooQueryServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to FooQueryServiceServer will +// result in compilation errors. +type UnsafeFooQueryServiceServer interface { + mustEmbedUnimplementedFooQueryServiceServer() +} + +func RegisterFooQueryServiceServer(s grpc.ServiceRegistrar, srv FooQueryServiceServer) { + s.RegisterService(&FooQueryService_ServiceDesc, srv) +} + +func _FooQueryService_FooGet_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FooGetRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FooQueryServiceServer).FooGet(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FooQueryService_FooGet_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FooQueryServiceServer).FooGet(ctx, req.(*FooGetRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FooQueryService_FooList_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FooListRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FooQueryServiceServer).FooList(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FooQueryService_FooList_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FooQueryServiceServer).FooList(ctx, req.(*FooListRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _FooQueryService_FooEvents_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FooEventsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FooQueryServiceServer).FooEvents(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FooQueryService_FooEvents_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FooQueryServiceServer).FooEvents(ctx, req.(*FooEventsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// FooQueryService_ServiceDesc is the grpc.ServiceDesc for FooQueryService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var FooQueryService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "test.v1.service.FooQueryService", + HandlerType: (*FooQueryServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "FooGet", + Handler: _FooQueryService_FooGet_Handler, + }, + { + MethodName: "FooList", + Handler: _FooQueryService_FooList_Handler, + }, + { + MethodName: "FooEvents", + Handler: _FooQueryService_FooEvents_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "test/v1/service/foo.p.j5s.proto", +} + +const ( + FooService_FooSummary_FullMethodName = "/test.v1.service.FooService/FooSummary" +) + +// FooServiceClient is the client API for FooService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type FooServiceClient interface { + FooSummary(ctx context.Context, in *FooSummaryRequest, opts ...grpc.CallOption) (*FooSummaryResponse, error) +} + +type fooServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewFooServiceClient(cc grpc.ClientConnInterface) FooServiceClient { + return &fooServiceClient{cc} +} + +func (c *fooServiceClient) FooSummary(ctx context.Context, in *FooSummaryRequest, opts ...grpc.CallOption) (*FooSummaryResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(FooSummaryResponse) + err := c.cc.Invoke(ctx, FooService_FooSummary_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// FooServiceServer is the server API for FooService service. +// All implementations must embed UnimplementedFooServiceServer +// for forward compatibility +type FooServiceServer interface { + FooSummary(context.Context, *FooSummaryRequest) (*FooSummaryResponse, error) + mustEmbedUnimplementedFooServiceServer() +} + +// UnimplementedFooServiceServer must be embedded to have forward compatible implementations. +type UnimplementedFooServiceServer struct { +} + +func (UnimplementedFooServiceServer) FooSummary(context.Context, *FooSummaryRequest) (*FooSummaryResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method FooSummary not implemented") +} +func (UnimplementedFooServiceServer) mustEmbedUnimplementedFooServiceServer() {} + +// UnsafeFooServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to FooServiceServer will +// result in compilation errors. +type UnsafeFooServiceServer interface { + mustEmbedUnimplementedFooServiceServer() +} + +func RegisterFooServiceServer(s grpc.ServiceRegistrar, srv FooServiceServer) { + s.RegisterService(&FooService_ServiceDesc, srv) +} + +func _FooService_FooSummary_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FooSummaryRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FooServiceServer).FooSummary(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FooService_FooSummary_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FooServiceServer).FooSummary(ctx, req.(*FooSummaryRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// FooService_ServiceDesc is the grpc.ServiceDesc for FooService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var FooService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "test.v1.service.FooService", + HandlerType: (*FooServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "FooSummary", + Handler: _FooService_FooSummary_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "test/v1/service/foo.p.j5s.proto", +} diff --git a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_j5.pb.go b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_j5.pb.go index d03702c..ff665ec 100644 --- a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_j5.pb.go +++ b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_j5.pb.go @@ -4,29 +4,94 @@ package test_spb import ( j5reflect "github.com/pentops/j5/lib/j5reflect" + proto "google.golang.org/protobuf/proto" ) func (msg *FooGetRequest) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooGetRequest) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooGetRequest) Clone() any { + return proto.Clone(msg).(*FooGetRequest) } func (msg *FooGetResponse) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooGetResponse) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooGetResponse) Clone() any { + return proto.Clone(msg).(*FooGetResponse) } func (msg *FooListRequest) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooListRequest) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooListRequest) Clone() any { + return proto.Clone(msg).(*FooListRequest) } func (msg *FooListResponse) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooListResponse) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooListResponse) Clone() any { + return proto.Clone(msg).(*FooListResponse) } func (msg *FooEventsRequest) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooEventsRequest) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooEventsRequest) Clone() any { + return proto.Clone(msg).(*FooEventsRequest) } func (msg *FooEventsResponse) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooEventsResponse) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooEventsResponse) Clone() any { + return proto.Clone(msg).(*FooEventsResponse) } func (msg *FooSummaryRequest) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooSummaryRequest) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooSummaryRequest) Clone() any { + return proto.Clone(msg).(*FooSummaryRequest) } func (msg *FooSummaryResponse) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooSummaryResponse) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooSummaryResponse) Clone() any { + return proto.Clone(msg).(*FooSummaryResponse) } diff --git a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_psm_query.pb.go b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_psm_query.pb.go new file mode 100644 index 0000000..5d47ac8 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_psm_query.pb.go @@ -0,0 +1,103 @@ +// Code generated by protoc-gen-go-psm. DO NOT EDIT. + +package test_spb + +import ( + context "context" + fmt "fmt" + j5reflect "github.com/pentops/j5/lib/j5reflect" + j5schema "github.com/pentops/j5/lib/j5schema" + psm "github.com/pentops/protostate/psm" + sqrlx "github.com/pentops/sqrlx.go/sqrlx" +) + +// State Query Service for %sFoo +// QuerySet is the query set for the Foo service. + +type FooPSMQuerySet = psm.StateQuerySet + +func NewFooPSMQuerySet( + smSpec psm.QuerySpec, + options psm.StateQueryOptions, +) (*FooPSMQuerySet, error) { + return psm.BuildStateQuerySet(smSpec, options) +} + +type FooPSMQuerySpec = psm.QuerySpec + +func DefaultFooPSMQuerySpec(tableSpec psm.QueryTableSpec) FooPSMQuerySpec { + return psm.QuerySpec{ + GetMethod: &j5schema.MethodSchema{ + Request: j5schema.MustObjectSchema((&FooGetRequest{}).ProtoReflect().Descriptor()), + Response: j5schema.MustObjectSchema((&FooGetResponse{}).ProtoReflect().Descriptor()), + }, + ListMethod: &j5schema.MethodSchema{ + Request: j5schema.MustObjectSchema((&FooListRequest{}).ProtoReflect().Descriptor()), + Response: j5schema.MustObjectSchema((&FooListResponse{}).ProtoReflect().Descriptor()), + }, + ListEventsMethod: &j5schema.MethodSchema{ + Request: j5schema.MustObjectSchema((&FooEventsRequest{}).ProtoReflect().Descriptor()), + Response: j5schema.MustObjectSchema((&FooEventsResponse{}).ProtoReflect().Descriptor()), + }, + QueryTableSpec: tableSpec, + ListRequestFilter: func(reqReflect j5reflect.Object) (map[string]interface{}, error) { + req, ok := reqReflect.Interface().(*FooListRequest) + if !ok { + return nil, fmt.Errorf("expected *FooListRequest but got %T", req) + } + filter := map[string]interface{}{} + return filter, nil + }, + ListEventsRequestFilter: func(reqReflect j5reflect.Object) (map[string]interface{}, error) { + req, ok := reqReflect.Interface().(*FooEventsRequest) + if !ok { + return nil, fmt.Errorf("expected *FooEventsRequest but got %T", req) + } + filter := map[string]interface{}{} + filter["foo_id"] = req.FooId + return filter, nil + }, + } +} + +type FooQueryServiceImpl struct { + db sqrlx.Transactor + querySet *FooPSMQuerySet + UnsafeFooQueryServiceServer +} + +var _ FooQueryServiceServer = &FooQueryServiceImpl{} + +func NewFooQueryServiceImpl(db sqrlx.Transactor, querySet *FooPSMQuerySet) *FooQueryServiceImpl { + return &FooQueryServiceImpl{ + db: db, + querySet: querySet, + } +} + +func (s *FooQueryServiceImpl) FooGet(ctx context.Context, req *FooGetRequest) (*FooGetResponse, error) { + resObject := &FooGetResponse{} + err := s.querySet.Get(ctx, s.db, req.J5Object(), resObject.J5Object()) + if err != nil { + return nil, err + } + return resObject, nil +} + +func (s *FooQueryServiceImpl) FooList(ctx context.Context, req *FooListRequest) (*FooListResponse, error) { + resObject := &FooListResponse{} + err := s.querySet.List(ctx, s.db, req.J5Object(), resObject.J5Object()) + if err != nil { + return nil, err + } + return resObject, nil +} + +func (s *FooQueryServiceImpl) FooEvents(ctx context.Context, req *FooEventsRequest) (*FooEventsResponse, error) { + resObject := &FooEventsResponse{} + err := s.querySet.ListEvents(ctx, s.db, req.J5Object(), resObject.J5Object()) + if err != nil { + return nil, err + } + return resObject, nil +} diff --git a/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_sugar.pb.go b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_sugar.pb.go new file mode 100644 index 0000000..2bfa669 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_spb/foo.p.j5s_sugar.pb.go @@ -0,0 +1,3 @@ +// Code generated by protoc-gen-go-sugar. DO NOT EDIT. + +package test_spb diff --git a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s.pb.go b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s.pb.go new file mode 100644 index 0000000..8c05cf5 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s.pb.go @@ -0,0 +1,235 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc (unknown) +// source: test/v1/topic/bar.p.j5s.proto + +package test_tpb + +import ( + _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" + _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" + _ "github.com/pentops/j5/gen/j5/messaging/v1/messaging_j5pb" + psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" + test_pb "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type BarEventMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Metadata *psm_j5pb.EventPublishMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + Keys *test_pb.BarKeys `protobuf:"bytes,2,opt,name=keys,proto3" json:"keys,omitempty"` + Event *test_pb.BarEventType `protobuf:"bytes,3,opt,name=event,proto3" json:"event,omitempty"` + Data *test_pb.BarData `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` + Status test_pb.BarStatus `protobuf:"varint,5,opt,name=status,proto3,enum=test.v1.BarStatus" json:"status,omitempty"` +} + +func (x *BarEventMessage) Reset() { + *x = BarEventMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_topic_bar_p_j5s_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BarEventMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BarEventMessage) ProtoMessage() {} + +func (x *BarEventMessage) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_topic_bar_p_j5s_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use BarEventMessage.ProtoReflect.Descriptor instead. +func (*BarEventMessage) Descriptor() ([]byte, []int) { + return file_test_v1_topic_bar_p_j5s_proto_rawDescGZIP(), []int{0} +} + +func (x *BarEventMessage) GetMetadata() *psm_j5pb.EventPublishMetadata { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *BarEventMessage) GetKeys() *test_pb.BarKeys { + if x != nil { + return x.Keys + } + return nil +} + +func (x *BarEventMessage) GetEvent() *test_pb.BarEventType { + if x != nil { + return x.Event + } + return nil +} + +func (x *BarEventMessage) GetData() *test_pb.BarData { + if x != nil { + return x.Data + } + return nil +} + +func (x *BarEventMessage) GetStatus() test_pb.BarStatus { + if x != nil { + return x.Status + } + return test_pb.BarStatus(0) +} + +var File_test_v1_topic_bar_p_j5s_proto protoreflect.FileDescriptor + +var file_test_v1_topic_bar_p_j5s_proto_rawDesc = []byte{ + 0x0a, 0x1d, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x2f, + 0x62, 0x61, 0x72, 0x2e, 0x70, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x0d, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x1a, 0x1b, + 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, + 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, 0x35, 0x2f, 0x65, 0x78, 0x74, + 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x6a, 0x35, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x69, 0x6e, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x6a, 0x35, 0x2f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x61, + 0x72, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xce, 0x02, 0x0a, 0x0f, + 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x4c, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, + 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x33, 0x0a, + 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0d, 0xba, + 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x6b, 0x65, + 0x79, 0x73, 0x12, 0x3a, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, + 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x62, 0x00, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x33, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x44, 0x61, 0x74, 0x61, 0x42, 0x0d, + 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x12, 0x3e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, + 0x72, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x12, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, + 0x01, 0x02, 0x10, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x32, 0x79, 0x0a, 0x0f, + 0x42, 0x61, 0x72, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, + 0x42, 0x0a, 0x08, 0x42, 0x61, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x74, 0x65, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x42, 0x61, 0x72, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x1a, 0x22, 0xda, 0xa2, 0xf5, 0xe4, 0x02, 0x1c, 0x0a, 0x0b, 0x62, 0x61, 0x72, + 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x6a, 0x0d, 0x0a, 0x0b, 0x74, 0x65, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x42, 0x61, 0x72, 0x42, 0x47, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, + 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x70, 0x62, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_test_v1_topic_bar_p_j5s_proto_rawDescOnce sync.Once + file_test_v1_topic_bar_p_j5s_proto_rawDescData = file_test_v1_topic_bar_p_j5s_proto_rawDesc +) + +func file_test_v1_topic_bar_p_j5s_proto_rawDescGZIP() []byte { + file_test_v1_topic_bar_p_j5s_proto_rawDescOnce.Do(func() { + file_test_v1_topic_bar_p_j5s_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_v1_topic_bar_p_j5s_proto_rawDescData) + }) + return file_test_v1_topic_bar_p_j5s_proto_rawDescData +} + +var file_test_v1_topic_bar_p_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_test_v1_topic_bar_p_j5s_proto_goTypes = []interface{}{ + (*BarEventMessage)(nil), // 0: test.v1.topic.BarEventMessage + (*psm_j5pb.EventPublishMetadata)(nil), // 1: j5.state.v1.EventPublishMetadata + (*test_pb.BarKeys)(nil), // 2: test.v1.BarKeys + (*test_pb.BarEventType)(nil), // 3: test.v1.BarEventType + (*test_pb.BarData)(nil), // 4: test.v1.BarData + (test_pb.BarStatus)(0), // 5: test.v1.BarStatus + (*emptypb.Empty)(nil), // 6: google.protobuf.Empty +} +var file_test_v1_topic_bar_p_j5s_proto_depIdxs = []int32{ + 1, // 0: test.v1.topic.BarEventMessage.metadata:type_name -> j5.state.v1.EventPublishMetadata + 2, // 1: test.v1.topic.BarEventMessage.keys:type_name -> test.v1.BarKeys + 3, // 2: test.v1.topic.BarEventMessage.event:type_name -> test.v1.BarEventType + 4, // 3: test.v1.topic.BarEventMessage.data:type_name -> test.v1.BarData + 5, // 4: test.v1.topic.BarEventMessage.status:type_name -> test.v1.BarStatus + 0, // 5: test.v1.topic.BarPublishTopic.BarEvent:input_type -> test.v1.topic.BarEventMessage + 6, // 6: test.v1.topic.BarPublishTopic.BarEvent:output_type -> google.protobuf.Empty + 6, // [6:7] is the sub-list for method output_type + 5, // [5:6] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_test_v1_topic_bar_p_j5s_proto_init() } +func file_test_v1_topic_bar_p_j5s_proto_init() { + if File_test_v1_topic_bar_p_j5s_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_test_v1_topic_bar_p_j5s_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BarEventMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_test_v1_topic_bar_p_j5s_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_test_v1_topic_bar_p_j5s_proto_goTypes, + DependencyIndexes: file_test_v1_topic_bar_p_j5s_proto_depIdxs, + MessageInfos: file_test_v1_topic_bar_p_j5s_proto_msgTypes, + }.Build() + File_test_v1_topic_bar_p_j5s_proto = out.File + file_test_v1_topic_bar_p_j5s_proto_rawDesc = nil + file_test_v1_topic_bar_p_j5s_proto_goTypes = nil + file_test_v1_topic_bar_p_j5s_proto_depIdxs = nil +} diff --git a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_grpc.pb.go b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_grpc.pb.go new file mode 100644 index 0000000..c375433 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_grpc.pb.go @@ -0,0 +1,111 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.4.0 +// - protoc (unknown) +// source: test/v1/topic/bar.p.j5s.proto + +package test_tpb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 + +const ( + BarPublishTopic_BarEvent_FullMethodName = "/test.v1.topic.BarPublishTopic/BarEvent" +) + +// BarPublishTopicClient is the client API for BarPublishTopic service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type BarPublishTopicClient interface { + BarEvent(ctx context.Context, in *BarEventMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) +} + +type barPublishTopicClient struct { + cc grpc.ClientConnInterface +} + +func NewBarPublishTopicClient(cc grpc.ClientConnInterface) BarPublishTopicClient { + return &barPublishTopicClient{cc} +} + +func (c *barPublishTopicClient) BarEvent(ctx context.Context, in *BarEventMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, BarPublishTopic_BarEvent_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// BarPublishTopicServer is the server API for BarPublishTopic service. +// All implementations must embed UnimplementedBarPublishTopicServer +// for forward compatibility +type BarPublishTopicServer interface { + BarEvent(context.Context, *BarEventMessage) (*emptypb.Empty, error) + mustEmbedUnimplementedBarPublishTopicServer() +} + +// UnimplementedBarPublishTopicServer must be embedded to have forward compatible implementations. +type UnimplementedBarPublishTopicServer struct { +} + +func (UnimplementedBarPublishTopicServer) BarEvent(context.Context, *BarEventMessage) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method BarEvent not implemented") +} +func (UnimplementedBarPublishTopicServer) mustEmbedUnimplementedBarPublishTopicServer() {} + +// UnsafeBarPublishTopicServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to BarPublishTopicServer will +// result in compilation errors. +type UnsafeBarPublishTopicServer interface { + mustEmbedUnimplementedBarPublishTopicServer() +} + +func RegisterBarPublishTopicServer(s grpc.ServiceRegistrar, srv BarPublishTopicServer) { + s.RegisterService(&BarPublishTopic_ServiceDesc, srv) +} + +func _BarPublishTopic_BarEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(BarEventMessage) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(BarPublishTopicServer).BarEvent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: BarPublishTopic_BarEvent_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(BarPublishTopicServer).BarEvent(ctx, req.(*BarEventMessage)) + } + return interceptor(ctx, in, info, handler) +} + +// BarPublishTopic_ServiceDesc is the grpc.ServiceDesc for BarPublishTopic service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var BarPublishTopic_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "test.v1.topic.BarPublishTopic", + HandlerType: (*BarPublishTopicServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "BarEvent", + Handler: _BarPublishTopic_BarEvent_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "test/v1/topic/bar.p.j5s.proto", +} diff --git a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_j5.pb.go b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_j5.pb.go index d1aaad2..8022834 100644 --- a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_j5.pb.go +++ b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_j5.pb.go @@ -4,8 +4,17 @@ package test_tpb import ( j5reflect "github.com/pentops/j5/lib/j5reflect" + proto "google.golang.org/protobuf/proto" ) func (msg *BarEventMessage) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *BarEventMessage) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *BarEventMessage) Clone() any { + return proto.Clone(msg).(*BarEventMessage) } diff --git a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_o5_messaging.pb.go b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_o5_messaging.pb.go new file mode 100644 index 0000000..bdc4966 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_o5_messaging.pb.go @@ -0,0 +1,95 @@ +// Code generated by protoc-gen-go-o5-messaging. DO NOT EDIT. +// versions: +// - protoc-gen-go-o5-messaging 0.0.0 +// source: test/v1/topic/bar.p.j5s.proto + +package test_tpb + +import ( + context "context" + messaging_pb "github.com/pentops/o5-messaging/gen/o5/messaging/v1/messaging_pb" + o5msg "github.com/pentops/o5-messaging/o5msg" +) + +// Service: BarPublishTopic +// Method: BarEvent + +func (msg *BarEventMessage) O5MessageHeader() o5msg.Header { + header := o5msg.Header{ + GrpcService: "test.v1.topic.BarPublishTopic", + GrpcMethod: "BarEvent", + Headers: map[string]string{}, + DestinationTopic: "bar_publish", + } + header.Extension = &messaging_pb.Message_Event_{ + Event: &messaging_pb.Message_Event{ + EntityName: "test.v1.Bar", + }, + } + return header +} + +type BarPublishTopicTxSender[C any] struct { + sender o5msg.TxSender[C] +} + +func NewBarPublishTopicTxSender[C any](sender o5msg.TxSender[C]) *BarPublishTopicTxSender[C] { + sender.Register(o5msg.TopicDescriptor{ + Service: "test.v1.topic.BarPublishTopic", + Methods: []o5msg.MethodDescriptor{ + { + Name: "BarEvent", + Message: (*BarEventMessage).ProtoReflect(nil).Descriptor(), + }, + }, + }) + return &BarPublishTopicTxSender[C]{sender: sender} +} + +type BarPublishTopicCollector[C any] struct { + collector o5msg.Collector[C] +} + +func NewBarPublishTopicCollector[C any](collector o5msg.Collector[C]) *BarPublishTopicCollector[C] { + collector.Register(o5msg.TopicDescriptor{ + Service: "test.v1.topic.BarPublishTopic", + Methods: []o5msg.MethodDescriptor{ + { + Name: "BarEvent", + Message: (*BarEventMessage).ProtoReflect(nil).Descriptor(), + }, + }, + }) + return &BarPublishTopicCollector[C]{collector: collector} +} + +type BarPublishTopicPublisher struct { + publisher o5msg.Publisher +} + +func NewBarPublishTopicPublisher(publisher o5msg.Publisher) *BarPublishTopicPublisher { + publisher.Register(o5msg.TopicDescriptor{ + Service: "test.v1.topic.BarPublishTopic", + Methods: []o5msg.MethodDescriptor{ + { + Name: "BarEvent", + Message: (*BarEventMessage).ProtoReflect(nil).Descriptor(), + }, + }, + }) + return &BarPublishTopicPublisher{publisher: publisher} +} + +// Method: BarEvent + +func (send BarPublishTopicTxSender[C]) BarEvent(ctx context.Context, sendContext C, msg *BarEventMessage) error { + return send.sender.Send(ctx, sendContext, msg) +} + +func (collect BarPublishTopicCollector[C]) BarEvent(sendContext C, msg *BarEventMessage) { + collect.collector.Collect(sendContext, msg) +} + +func (publish BarPublishTopicPublisher) BarEvent(ctx context.Context, msg *BarEventMessage) error { + return publish.publisher.Publish(ctx, msg) +} diff --git a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_psm_publish.pb.go b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_psm_publish.pb.go new file mode 100644 index 0000000..282581d --- /dev/null +++ b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_psm_publish.pb.go @@ -0,0 +1,35 @@ +// Code generated by protoc-gen-go-psm. DO NOT EDIT. + +package test_tpb + +import ( + context "context" + test_pb "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" + psm "github.com/pentops/protostate/psm" +) + +// Publish Toipc for test.v1.Bar +func PublishBar() psm.GeneralEventHook[ + *test_pb.BarKeys, // implements psm.IKeyset + *test_pb.BarState, // implements psm.IState + test_pb.BarStatus, // implements psm.IStatusEnum + *test_pb.BarData, // implements psm.IStateData + *test_pb.BarEvent, // implements psm.IEvent + test_pb.BarPSMEvent, // implements psm.IInnerEvent +] { + return test_pb.BarPSMEventPublishHook(func( + ctx context.Context, + publisher psm.Publisher, + state *test_pb.BarState, + event *test_pb.BarEvent, + ) error { + publisher.Publish(&BarEventMessage{ + Metadata: event.EventPublishMetadata(), + Keys: event.Keys, + Event: event.Event, + Data: state.Data, + Status: state.Status, + }) + return nil + }) +} diff --git a/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_sugar.pb.go b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_sugar.pb.go new file mode 100644 index 0000000..5c3223e --- /dev/null +++ b/internal/testproto/gen/test/v1/test_tpb/bar.p.j5s_sugar.pb.go @@ -0,0 +1,3 @@ +// Code generated by protoc-gen-go-sugar. DO NOT EDIT. + +package test_tpb diff --git a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s.pb.go b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s.pb.go new file mode 100644 index 0000000..b9db8e0 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s.pb.go @@ -0,0 +1,235 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.32.0 +// protoc (unknown) +// source: test/v1/topic/foo.p.j5s.proto + +package test_tpb + +import ( + _ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate" + _ "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" + _ "github.com/pentops/j5/gen/j5/messaging/v1/messaging_j5pb" + psm_j5pb "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" + test_pb "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + emptypb "google.golang.org/protobuf/types/known/emptypb" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type FooEventMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Metadata *psm_j5pb.EventPublishMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + Keys *test_pb.FooKeys `protobuf:"bytes,2,opt,name=keys,proto3" json:"keys,omitempty"` + Event *test_pb.FooEventType `protobuf:"bytes,3,opt,name=event,proto3" json:"event,omitempty"` + Data *test_pb.FooData `protobuf:"bytes,4,opt,name=data,proto3" json:"data,omitempty"` + Status test_pb.FooStatus `protobuf:"varint,5,opt,name=status,proto3,enum=test.v1.FooStatus" json:"status,omitempty"` +} + +func (x *FooEventMessage) Reset() { + *x = FooEventMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_topic_foo_p_j5s_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooEventMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooEventMessage) ProtoMessage() {} + +func (x *FooEventMessage) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_topic_foo_p_j5s_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooEventMessage.ProtoReflect.Descriptor instead. +func (*FooEventMessage) Descriptor() ([]byte, []int) { + return file_test_v1_topic_foo_p_j5s_proto_rawDescGZIP(), []int{0} +} + +func (x *FooEventMessage) GetMetadata() *psm_j5pb.EventPublishMetadata { + if x != nil { + return x.Metadata + } + return nil +} + +func (x *FooEventMessage) GetKeys() *test_pb.FooKeys { + if x != nil { + return x.Keys + } + return nil +} + +func (x *FooEventMessage) GetEvent() *test_pb.FooEventType { + if x != nil { + return x.Event + } + return nil +} + +func (x *FooEventMessage) GetData() *test_pb.FooData { + if x != nil { + return x.Data + } + return nil +} + +func (x *FooEventMessage) GetStatus() test_pb.FooStatus { + if x != nil { + return x.Status + } + return test_pb.FooStatus(0) +} + +var File_test_v1_topic_foo_p_j5s_proto protoreflect.FileDescriptor + +var file_test_v1_topic_foo_p_j5s_proto_rawDesc = []byte{ + 0x0a, 0x1d, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x2f, + 0x66, 0x6f, 0x6f, 0x2e, 0x70, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, + 0x0d, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x1a, 0x1b, + 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, + 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, + 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x6a, 0x35, 0x2f, 0x65, 0x78, 0x74, + 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x21, 0x6a, 0x35, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x69, 0x6e, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1a, 0x6a, 0x35, 0x2f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x15, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x66, 0x6f, + 0x6f, 0x2e, 0x6a, 0x35, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xce, 0x02, 0x0a, 0x0f, + 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, + 0x4c, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x21, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x4d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, + 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x33, 0x0a, + 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0d, 0xba, + 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x6b, 0x65, + 0x79, 0x73, 0x12, 0x3a, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, + 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x62, 0x00, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x33, + 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x42, 0x0d, + 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x64, + 0x61, 0x74, 0x61, 0x12, 0x3e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, + 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x12, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, + 0x01, 0x02, 0x10, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, + 0x74, 0x75, 0x73, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x32, 0x79, 0x0a, 0x0f, + 0x46, 0x6f, 0x6f, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x54, 0x6f, 0x70, 0x69, 0x63, 0x12, + 0x42, 0x0a, 0x08, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x1e, 0x2e, 0x74, 0x65, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x2e, 0x46, 0x6f, 0x6f, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x1a, 0x16, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x1a, 0x22, 0xda, 0xa2, 0xf5, 0xe4, 0x02, 0x1c, 0x0a, 0x0b, 0x66, 0x6f, 0x6f, + 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x6a, 0x0d, 0x0a, 0x0b, 0x74, 0x65, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x42, 0x47, 0x5a, 0x45, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, + 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x74, 0x70, 0x62, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_test_v1_topic_foo_p_j5s_proto_rawDescOnce sync.Once + file_test_v1_topic_foo_p_j5s_proto_rawDescData = file_test_v1_topic_foo_p_j5s_proto_rawDesc +) + +func file_test_v1_topic_foo_p_j5s_proto_rawDescGZIP() []byte { + file_test_v1_topic_foo_p_j5s_proto_rawDescOnce.Do(func() { + file_test_v1_topic_foo_p_j5s_proto_rawDescData = protoimpl.X.CompressGZIP(file_test_v1_topic_foo_p_j5s_proto_rawDescData) + }) + return file_test_v1_topic_foo_p_j5s_proto_rawDescData +} + +var file_test_v1_topic_foo_p_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_test_v1_topic_foo_p_j5s_proto_goTypes = []interface{}{ + (*FooEventMessage)(nil), // 0: test.v1.topic.FooEventMessage + (*psm_j5pb.EventPublishMetadata)(nil), // 1: j5.state.v1.EventPublishMetadata + (*test_pb.FooKeys)(nil), // 2: test.v1.FooKeys + (*test_pb.FooEventType)(nil), // 3: test.v1.FooEventType + (*test_pb.FooData)(nil), // 4: test.v1.FooData + (test_pb.FooStatus)(0), // 5: test.v1.FooStatus + (*emptypb.Empty)(nil), // 6: google.protobuf.Empty +} +var file_test_v1_topic_foo_p_j5s_proto_depIdxs = []int32{ + 1, // 0: test.v1.topic.FooEventMessage.metadata:type_name -> j5.state.v1.EventPublishMetadata + 2, // 1: test.v1.topic.FooEventMessage.keys:type_name -> test.v1.FooKeys + 3, // 2: test.v1.topic.FooEventMessage.event:type_name -> test.v1.FooEventType + 4, // 3: test.v1.topic.FooEventMessage.data:type_name -> test.v1.FooData + 5, // 4: test.v1.topic.FooEventMessage.status:type_name -> test.v1.FooStatus + 0, // 5: test.v1.topic.FooPublishTopic.FooEvent:input_type -> test.v1.topic.FooEventMessage + 6, // 6: test.v1.topic.FooPublishTopic.FooEvent:output_type -> google.protobuf.Empty + 6, // [6:7] is the sub-list for method output_type + 5, // [5:6] is the sub-list for method input_type + 5, // [5:5] is the sub-list for extension type_name + 5, // [5:5] is the sub-list for extension extendee + 0, // [0:5] is the sub-list for field type_name +} + +func init() { file_test_v1_topic_foo_p_j5s_proto_init() } +func file_test_v1_topic_foo_p_j5s_proto_init() { + if File_test_v1_topic_foo_p_j5s_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_test_v1_topic_foo_p_j5s_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooEventMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_test_v1_topic_foo_p_j5s_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_test_v1_topic_foo_p_j5s_proto_goTypes, + DependencyIndexes: file_test_v1_topic_foo_p_j5s_proto_depIdxs, + MessageInfos: file_test_v1_topic_foo_p_j5s_proto_msgTypes, + }.Build() + File_test_v1_topic_foo_p_j5s_proto = out.File + file_test_v1_topic_foo_p_j5s_proto_rawDesc = nil + file_test_v1_topic_foo_p_j5s_proto_goTypes = nil + file_test_v1_topic_foo_p_j5s_proto_depIdxs = nil +} diff --git a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_grpc.pb.go b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_grpc.pb.go new file mode 100644 index 0000000..c0bc391 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_grpc.pb.go @@ -0,0 +1,111 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.4.0 +// - protoc (unknown) +// source: test/v1/topic/foo.p.j5s.proto + +package test_tpb + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + emptypb "google.golang.org/protobuf/types/known/emptypb" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.62.0 or later. +const _ = grpc.SupportPackageIsVersion8 + +const ( + FooPublishTopic_FooEvent_FullMethodName = "/test.v1.topic.FooPublishTopic/FooEvent" +) + +// FooPublishTopicClient is the client API for FooPublishTopic service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type FooPublishTopicClient interface { + FooEvent(ctx context.Context, in *FooEventMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) +} + +type fooPublishTopicClient struct { + cc grpc.ClientConnInterface +} + +func NewFooPublishTopicClient(cc grpc.ClientConnInterface) FooPublishTopicClient { + return &fooPublishTopicClient{cc} +} + +func (c *fooPublishTopicClient) FooEvent(ctx context.Context, in *FooEventMessage, opts ...grpc.CallOption) (*emptypb.Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(emptypb.Empty) + err := c.cc.Invoke(ctx, FooPublishTopic_FooEvent_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// FooPublishTopicServer is the server API for FooPublishTopic service. +// All implementations must embed UnimplementedFooPublishTopicServer +// for forward compatibility +type FooPublishTopicServer interface { + FooEvent(context.Context, *FooEventMessage) (*emptypb.Empty, error) + mustEmbedUnimplementedFooPublishTopicServer() +} + +// UnimplementedFooPublishTopicServer must be embedded to have forward compatible implementations. +type UnimplementedFooPublishTopicServer struct { +} + +func (UnimplementedFooPublishTopicServer) FooEvent(context.Context, *FooEventMessage) (*emptypb.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method FooEvent not implemented") +} +func (UnimplementedFooPublishTopicServer) mustEmbedUnimplementedFooPublishTopicServer() {} + +// UnsafeFooPublishTopicServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to FooPublishTopicServer will +// result in compilation errors. +type UnsafeFooPublishTopicServer interface { + mustEmbedUnimplementedFooPublishTopicServer() +} + +func RegisterFooPublishTopicServer(s grpc.ServiceRegistrar, srv FooPublishTopicServer) { + s.RegisterService(&FooPublishTopic_ServiceDesc, srv) +} + +func _FooPublishTopic_FooEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(FooEventMessage) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(FooPublishTopicServer).FooEvent(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: FooPublishTopic_FooEvent_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(FooPublishTopicServer).FooEvent(ctx, req.(*FooEventMessage)) + } + return interceptor(ctx, in, info, handler) +} + +// FooPublishTopic_ServiceDesc is the grpc.ServiceDesc for FooPublishTopic service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var FooPublishTopic_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "test.v1.topic.FooPublishTopic", + HandlerType: (*FooPublishTopicServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "FooEvent", + Handler: _FooPublishTopic_FooEvent_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "test/v1/topic/foo.p.j5s.proto", +} diff --git a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_j5.pb.go b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_j5.pb.go index 6b54772..017453b 100644 --- a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_j5.pb.go +++ b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_j5.pb.go @@ -4,8 +4,17 @@ package test_tpb import ( j5reflect "github.com/pentops/j5/lib/j5reflect" + proto "google.golang.org/protobuf/proto" ) func (msg *FooEventMessage) J5Reflect() j5reflect.Root { - return j5reflect.MustReflect(msg) + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooEventMessage) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooEventMessage) Clone() any { + return proto.Clone(msg).(*FooEventMessage) } diff --git a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_o5_messaging.pb.go b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_o5_messaging.pb.go new file mode 100644 index 0000000..47a9326 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_o5_messaging.pb.go @@ -0,0 +1,95 @@ +// Code generated by protoc-gen-go-o5-messaging. DO NOT EDIT. +// versions: +// - protoc-gen-go-o5-messaging 0.0.0 +// source: test/v1/topic/foo.p.j5s.proto + +package test_tpb + +import ( + context "context" + messaging_pb "github.com/pentops/o5-messaging/gen/o5/messaging/v1/messaging_pb" + o5msg "github.com/pentops/o5-messaging/o5msg" +) + +// Service: FooPublishTopic +// Method: FooEvent + +func (msg *FooEventMessage) O5MessageHeader() o5msg.Header { + header := o5msg.Header{ + GrpcService: "test.v1.topic.FooPublishTopic", + GrpcMethod: "FooEvent", + Headers: map[string]string{}, + DestinationTopic: "foo_publish", + } + header.Extension = &messaging_pb.Message_Event_{ + Event: &messaging_pb.Message_Event{ + EntityName: "test.v1.Foo", + }, + } + return header +} + +type FooPublishTopicTxSender[C any] struct { + sender o5msg.TxSender[C] +} + +func NewFooPublishTopicTxSender[C any](sender o5msg.TxSender[C]) *FooPublishTopicTxSender[C] { + sender.Register(o5msg.TopicDescriptor{ + Service: "test.v1.topic.FooPublishTopic", + Methods: []o5msg.MethodDescriptor{ + { + Name: "FooEvent", + Message: (*FooEventMessage).ProtoReflect(nil).Descriptor(), + }, + }, + }) + return &FooPublishTopicTxSender[C]{sender: sender} +} + +type FooPublishTopicCollector[C any] struct { + collector o5msg.Collector[C] +} + +func NewFooPublishTopicCollector[C any](collector o5msg.Collector[C]) *FooPublishTopicCollector[C] { + collector.Register(o5msg.TopicDescriptor{ + Service: "test.v1.topic.FooPublishTopic", + Methods: []o5msg.MethodDescriptor{ + { + Name: "FooEvent", + Message: (*FooEventMessage).ProtoReflect(nil).Descriptor(), + }, + }, + }) + return &FooPublishTopicCollector[C]{collector: collector} +} + +type FooPublishTopicPublisher struct { + publisher o5msg.Publisher +} + +func NewFooPublishTopicPublisher(publisher o5msg.Publisher) *FooPublishTopicPublisher { + publisher.Register(o5msg.TopicDescriptor{ + Service: "test.v1.topic.FooPublishTopic", + Methods: []o5msg.MethodDescriptor{ + { + Name: "FooEvent", + Message: (*FooEventMessage).ProtoReflect(nil).Descriptor(), + }, + }, + }) + return &FooPublishTopicPublisher{publisher: publisher} +} + +// Method: FooEvent + +func (send FooPublishTopicTxSender[C]) FooEvent(ctx context.Context, sendContext C, msg *FooEventMessage) error { + return send.sender.Send(ctx, sendContext, msg) +} + +func (collect FooPublishTopicCollector[C]) FooEvent(sendContext C, msg *FooEventMessage) { + collect.collector.Collect(sendContext, msg) +} + +func (publish FooPublishTopicPublisher) FooEvent(ctx context.Context, msg *FooEventMessage) error { + return publish.publisher.Publish(ctx, msg) +} diff --git a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_psm_publish.pb.go b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_psm_publish.pb.go new file mode 100644 index 0000000..9e18254 --- /dev/null +++ b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_psm_publish.pb.go @@ -0,0 +1,35 @@ +// Code generated by protoc-gen-go-psm. DO NOT EDIT. + +package test_tpb + +import ( + context "context" + test_pb "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" + psm "github.com/pentops/protostate/psm" +) + +// Publish Toipc for test.v1.Foo +func PublishFoo() psm.GeneralEventHook[ + *test_pb.FooKeys, // implements psm.IKeyset + *test_pb.FooState, // implements psm.IState + test_pb.FooStatus, // implements psm.IStatusEnum + *test_pb.FooData, // implements psm.IStateData + *test_pb.FooEvent, // implements psm.IEvent + test_pb.FooPSMEvent, // implements psm.IInnerEvent +] { + return test_pb.FooPSMEventPublishHook(func( + ctx context.Context, + publisher psm.Publisher, + state *test_pb.FooState, + event *test_pb.FooEvent, + ) error { + publisher.Publish(&FooEventMessage{ + Metadata: event.EventPublishMetadata(), + Keys: event.Keys, + Event: event.Event, + Data: state.Data, + Status: state.Status, + }) + return nil + }) +} diff --git a/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_sugar.pb.go b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_sugar.pb.go new file mode 100644 index 0000000..5c3223e --- /dev/null +++ b/internal/testproto/gen/test/v1/test_tpb/foo.p.j5s_sugar.pb.go @@ -0,0 +1,3 @@ +// Code generated by protoc-gen-go-sugar. DO NOT EDIT. + +package test_tpb diff --git a/pquery/filter.go b/pquery/filter.go index abfe113..5e58efa 100644 --- a/pquery/filter.go +++ b/pquery/filter.go @@ -13,13 +13,12 @@ import ( "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" "github.com/pentops/j5/lib/id62" "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/protostate/internal/pgstore" "github.com/shopspring/decimal" "google.golang.org/protobuf/types/known/timestamppb" ) type filterSpec struct { - *pgstore.NestedField + *NestedField filterVals []any } @@ -235,7 +234,7 @@ func filtersForField(field *j5schema.ObjectProperty) ([]any, error) { func buildDefaultFilters(columnName string, message *j5schema.ObjectSchema) ([]filterSpec, error) { var filters []filterSpec - err := pgstore.WalkPathNodes(message, func(path pgstore.Path) error { + err := WalkPathNodes(message, func(path Path) error { field := path.LeafField() if field == nil { return nil @@ -248,7 +247,7 @@ func buildDefaultFilters(columnName string, message *j5schema.ObjectSchema) ([]f if len(vals) > 0 { filters = append(filters, filterSpec{ - NestedField: &pgstore.NestedField{ + NestedField: &NestedField{ Path: path, RootColumn: columnName, }, @@ -264,19 +263,19 @@ func buildDefaultFilters(columnName string, message *j5schema.ObjectSchema) ([]f return filters, nil } -func (ll *Lister[REQ, RES]) buildDynamicFilter(tableAlias string, filters []*list_j5pb.Filter) ([]sq.Sqlizer, error) { +func (ll *ListReflectionSet) buildDynamicFilter(tableAlias string, filters []*list_j5pb.Filter) ([]sq.Sqlizer, error) { out := []sq.Sqlizer{} for i := range filters { switch filters[i].GetType().(type) { case *list_j5pb.Filter_Field: - pathSpec := pgstore.ParseJSONPathSpec(filters[i].GetField().GetName()) - spec, err := pgstore.NewJSONPath(ll.arrayObject, pathSpec) + pathSpec := ParseJSONPathSpec(filters[i].GetField().GetName()) + spec, err := NewJSONPath(ll.arrayObject, pathSpec) if err != nil { return nil, fmt.Errorf("dynamic filter: find field: %w", err) } - biggerSpec := &pgstore.NestedField{ + biggerSpec := &NestedField{ Path: *spec, RootColumn: ll.dataColumn, } @@ -311,7 +310,7 @@ func (ll *Lister[REQ, RES]) buildDynamicFilter(tableAlias string, filters []*lis return out, nil } -func (ll *Lister[REQ, RES]) buildDynamicFilterField(tableAlias string, spec *pgstore.NestedField, filter *list_j5pb.Filter) (sq.Sqlizer, error) { +func (ll *ListReflectionSet) buildDynamicFilterField(tableAlias string, spec *NestedField, filter *list_j5pb.Filter) (sq.Sqlizer, error) { var out sq.And if filter.GetField() == nil { @@ -322,6 +321,15 @@ func (ll *Lister[REQ, RES]) buildDynamicFilterField(tableAlias string, spec *pgs case *list_j5pb.FieldType_Value: val := ft.Value + switch schema := spec.Path.LeafField().Schema.(type) { + case *j5schema.EnumField: + option := schema.Schema().OptionByName(val) + if option == nil { + return nil, fmt.Errorf("enum value '%s' not found in field '%s'", val, spec.Path.LeafField().JSONName) + } + val = option.Name() // Use the name of the option, not the value + } + out = sq.And{sq.Expr( fmt.Sprintf("jsonb_path_query_array(%s.%s, '%s') @> ?", tableAlias, @@ -349,29 +357,6 @@ func (ll *Lister[REQ, RES]) buildDynamicFilterField(tableAlias string, spec *pgs return out, nil } -func (ll *Lister[REQ, RES]) buildDynamicFilterOneof(tableAlias string, ospec *pgstore.NestedField, filter *list_j5pb.Filter) (sq.Sqlizer, error) { - var out sq.And - - if filter.GetField() == nil { - return nil, fmt.Errorf("dynamic filter: field is nil") - } - - switch ft := filter.GetField().GetType().Type.(type) { - case *list_j5pb.FieldType_Value: - val := ft.Value - - // Val is used directly here instead of passed in as an expression - // parameter. It has been sanitized by validation against the oneof - // field names. - exprStr := fmt.Sprintf("jsonb_array_length(jsonb_path_query_array(%s.%s, '%s ?? (exists(@.%s))')) > 0", tableAlias, ospec.RootColumn, ospec.Path.JSONPathQuery(), val) - out = sq.And{sq.Expr(exprStr)} - case *list_j5pb.FieldType_Range: - return nil, fmt.Errorf("oneofs cannot be filtered by range") - } - - return out, nil -} - func validateQueryRequestFilters(message *j5schema.ObjectSchema, filters []*list_j5pb.Filter) error { for i := range filters { switch filters[i].GetType().(type) { @@ -389,30 +374,25 @@ func validateQueryRequestFilters(message *j5schema.ObjectSchema, filters []*list func validateQueryRequestFilterField(message *j5schema.ObjectSchema, filterField *list_j5pb.Field) error { - jsonPath := pgstore.ParseJSONPathSpec(filterField.GetName()) - spec, err := pgstore.NewJSONPath(message, jsonPath) + jsonPath := ParseJSONPathSpec(filterField.GetName()) + spec, err := NewJSONPath(message, jsonPath) if err != nil { return fmt.Errorf("find field: %w", err) } - fieldSpec := spec.LeafField() - if !schemaIsFilterable(fieldSpec) { - return fmt.Errorf("requested filter field '%s' is not filterable", filterField.Name) - } - switch filterField.Type.Type.(type) { case *list_j5pb.FieldType_Value: - err := validateFilterFieldValue(fieldSpec, filterField.Type.GetValue()) + err := validateFilterFieldValue(spec, filterField.Type.GetValue()) if err != nil { return fmt.Errorf("filter value: %w", err) } case *list_j5pb.FieldType_Range: - err := validateFilterFieldValue(fieldSpec, filterField.Type.GetRange().GetMin()) + err := validateFilterFieldValue(spec, filterField.Type.GetRange().GetMin()) if err != nil { return fmt.Errorf("filter min value: %w", err) } - err = validateFilterFieldValue(fieldSpec, filterField.Type.GetRange().GetMax()) + err = validateFilterFieldValue(spec, filterField.Type.GetRange().GetMax()) if err != nil { return fmt.Errorf("filter max value: %w", err) } @@ -421,45 +401,13 @@ func validateQueryRequestFilterField(message *j5schema.ObjectSchema, filterField return nil } -func schemaIsFilterable(prop *j5schema.ObjectProperty) bool { - - switch bigSchema := prop.Schema.(type) { - case *j5schema.EnumField: - return bigSchema.ListRules != nil && bigSchema.ListRules.Filtering != nil && bigSchema.ListRules.Filtering.Filterable - case *j5schema.OneofField: - return bigSchema.ListRules != nil && bigSchema.ListRules.Filtering != nil && bigSchema.ListRules.Filtering.Filterable - case *j5schema.ScalarSchema: - j5Field := bigSchema.ToJ5Field() - switch st := j5Field.Type.(type) { - case *schema_j5pb.Field_Float: - return st.Float.ListRules != nil && st.Float.ListRules.Filtering != nil && st.Float.ListRules.Filtering.Filterable - case *schema_j5pb.Field_Integer: - return st.Integer.ListRules != nil && st.Integer.ListRules.Filtering != nil && st.Integer.ListRules.Filtering.Filterable - case *schema_j5pb.Field_Bool: - return st.Bool.ListRules != nil && st.Bool.ListRules.Filtering != nil && st.Bool.ListRules.Filtering.Filterable - case *schema_j5pb.Field_Key: - return st.Key.ListRules != nil && st.Key.ListRules.Filtering != nil && st.Key.ListRules.Filtering.Filterable - case *schema_j5pb.Field_Timestamp: - return st.Timestamp.ListRules != nil && st.Timestamp.ListRules.Filtering != nil && st.Timestamp.ListRules.Filtering.Filterable - case *schema_j5pb.Field_Date: - return st.Date.ListRules != nil && st.Date.ListRules.Filtering != nil && st.Date.ListRules.Filtering.Filterable - case *schema_j5pb.Field_Decimal: - return st.Decimal.ListRules != nil && st.Decimal.ListRules.Filtering != nil && st.Decimal.ListRules.Filtering.Filterable - default: - // If we don't know the type, we assume it's not filterable - return false - } - default: - // If we don't know the type, we assume it's not filterable - return false - } -} - -func validateFilterFieldValue(prop *j5schema.ObjectProperty, value string) error { +func validateFilterFieldValue(path *Path, value string) error { if value == "" { return nil } + prop := path.LeafField() + switch bigType := prop.Schema.(type) { case *j5schema.EnumField: schema := bigType.Schema() @@ -471,7 +419,7 @@ func validateFilterFieldValue(prop *j5schema.ObjectProperty, value string) error } case *j5schema.OneofField: if bigType.ListRules == nil || bigType.ListRules.Filtering == nil || !bigType.ListRules.Filtering.Filterable { - return fmt.Errorf("oneof field '%s' is not filterable", prop.JSONName) + return fmt.Errorf("oneof field %q is not filterable", prop.FullName()) } oneof := bigType.OneofSchema() if oneof.Properties.ByJSONName(value) == nil { @@ -485,17 +433,18 @@ func validateFilterFieldValue(prop *j5schema.ObjectProperty, value string) error if st.Float.ListRules == nil || st.Float.ListRules.Filtering == nil || !st.Float.ListRules.Filtering.Filterable { return fmt.Errorf("float field '%s' is not filterable", prop.JSONName) } - if st.Float.Format == schema_j5pb.FloatField_FORMAT_FLOAT32 { + switch st.Float.Format { + case schema_j5pb.FloatField_FORMAT_FLOAT32: _, err := strconv.ParseFloat(value, 32) if err != nil { return fmt.Errorf("parsing float32 value '%s' for field '%s': %w", value, prop.JSONName, err) } - } else if st.Float.Format == schema_j5pb.FloatField_FORMAT_FLOAT64 { + case schema_j5pb.FloatField_FORMAT_FLOAT64: _, err := strconv.ParseFloat(value, 64) if err != nil { return fmt.Errorf("parsing float64 value '%s' for field '%s': %w", value, prop.JSONName, err) } - } else { + default: return fmt.Errorf("unknown float format '%s' for field '%s'", st.Float.Format, prop.JSONName) } case *schema_j5pb.Field_Integer: diff --git a/pquery/getter.go b/pquery/getter.go index de8da3a..779ca38 100644 --- a/pquery/getter.go +++ b/pquery/getter.go @@ -27,16 +27,15 @@ type GetResponse interface { j5reflect.Object } -type GetSpec[ - REQ GetRequest, - RES GetResponse, -] struct { +type GetSpec struct { + Method *j5schema.MethodSchema + TableName string DataColumn string Auth AuthProvider AuthJoin []*LeftJoin - PrimaryKey func(REQ) (map[string]any, error) + PrimaryKey func(j5reflect.Object) (map[string]any, error) StateResponseField string @@ -98,15 +97,14 @@ func (gc GetJoinSpec) validate() error { return nil } -type Getter[ - REQ GetRequest, - RES j5reflect.Object, -] struct { +type Getter struct { + method *j5schema.MethodSchema + stateField *j5schema.ObjectProperty dataColumn string tableName string - primaryKey func(REQ) (map[string]any, error) + primaryKey func(j5reflect.Object) (map[string]any, error) auth AuthProvider authJoin []*LeftJoin @@ -124,14 +122,14 @@ type getJoin struct { on JoinFields } -func NewGetter[ - REQ GetRequest, - RES GetResponse, -](spec GetSpec[REQ, RES]) (*Getter[REQ, RES], error) { - descriptors := newMethodDescriptor[REQ, RES]() - resDesc := descriptors.response +func NewGetter(spec GetSpec) (*Getter, error) { + + if spec.Method == nil { + return nil, fmt.Errorf("missing Method") + } - sc := &Getter[REQ, RES]{ + sc := &Getter{ + method: spec.Method, dataColumn: spec.DataColumn, tableName: spec.TableName, primaryKey: spec.PrimaryKey, @@ -145,7 +143,7 @@ func NewGetter[ defaultState = true spec.StateResponseField = "state" } - sc.stateField = resDesc.Properties.ByJSONName(spec.StateResponseField) + sc.stateField = spec.Method.Response.Properties.ByJSONName(spec.StateResponseField) if sc.stateField == nil { if defaultState { return nil, fmt.Errorf("no 'state' field in proto message - did you mean to override StateResponseField?") @@ -163,7 +161,7 @@ func NewGetter[ return nil, fmt.Errorf("invalid join spec: %w", err) } - joinField := resDesc.Properties.ByJSONName(spec.Join.FieldInParent) + joinField := spec.Method.Response.Properties.ByJSONName(spec.Join.FieldInParent) if joinField == nil { return nil, fmt.Errorf("field %s not found in response message", spec.Join.FieldInParent) } @@ -185,11 +183,16 @@ func NewGetter[ return sc, nil } -func (gc *Getter[REQ, RES]) SetQueryLogger(logger QueryLogger) { +func (gc *Getter) SetQueryLogger(logger QueryLogger) { gc.queryLogger = logger } -func (gc *Getter[REQ, RES]) Get(ctx context.Context, db Transactor, reqMsg REQ, resMsg RES) error { +func (gc *Getter) Get(ctx context.Context, db Transactor, reqMsg, resMsg j5reflect.Object) error { + + err := assertObjectsMatch(gc.method, reqMsg, resMsg) + if err != nil { + return err + } as := newAliasSet() rootAlias := as.Next(gc.tableName) diff --git a/pquery/lister.go b/pquery/lister.go index 89885db..c9f7fe4 100644 --- a/pquery/lister.go +++ b/pquery/lister.go @@ -6,30 +6,24 @@ import ( "encoding/base64" "encoding/json" "fmt" - "regexp" "strings" "time" - "unicode" sq "github.com/elgris/sqrl" "github.com/elgris/sqrl/pg" "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" + "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" + "github.com/pentops/j5/j5types/date_j5t" "github.com/pentops/j5/lib/j5codec" "github.com/pentops/j5/lib/j5reflect" "github.com/pentops/j5/lib/j5schema" "github.com/pentops/j5/lib/j5validate" "github.com/pentops/log.go/log" - "github.com/pentops/protostate/internal/pgstore" "github.com/pentops/sqrlx.go/sqrlx" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/dynamicpb" - "google.golang.org/protobuf/types/known/timestamppb" ) -var dateRegex = regexp.MustCompile(`^\d{4}(-\d{2}(-\d{2})?)?$`) - type ListRequest interface { j5reflect.Object } @@ -53,14 +47,14 @@ type TableSpec struct { // ProtoField represents a field within a the root data. type ProtoField struct { // path from the root object to this field - pathInRoot pgstore.JSONPathSpec + pathInRoot JSONPathSpec // optional column name when the field is also stored as a scalar directly valueColumn *string } -func NewProtoField(protoPath string, columnName *string) ProtoField { - pp := pgstore.ParseJSONPathSpec(protoPath) +func NewJSONField(protoPath string, columnName *string) ProtoField { + pp := ParseJSONPathSpec(protoPath) return ProtoField{ valueColumn: columnName, pathInRoot: pp, @@ -72,17 +66,20 @@ type Column struct { // The point within the root element which is stored in the column. An empty // path means this stores the root element, - MountPoint *pgstore.Path + MountPoint *Path } -type ListSpec[REQ ListRequest, RES ListResponse] struct { +type ListSpec struct { + Method *j5schema.MethodSchema + TableSpec - RequestFilter func(REQ) (map[string]any, error) + RequestFilter func(j5reflect.Object) (map[string]any, error) } type QueryLogger func(sqrlx.Sqlizer) type ListReflectionSet struct { + method *j5schema.MethodSchema defaultPageSize uint64 arrayField *j5schema.ObjectProperty @@ -106,18 +103,20 @@ type ListReflectionSet struct { dataColumn string } -func BuildListReflection(req *j5schema.ObjectSchema, res *j5schema.ObjectSchema, table TableSpec) (*ListReflectionSet, error) { - return buildListReflection(req, res, table) +func BuildListReflection(method *j5schema.MethodSchema, table TableSpec) (*ListReflectionSet, error) { + return buildListReflection(method, table) } -func buildListReflection(req *j5schema.ObjectSchema, res *j5schema.ObjectSchema, table TableSpec) (*ListReflectionSet, error) { +func buildListReflection(method *j5schema.MethodSchema, table TableSpec) (*ListReflectionSet, error) { + var err error ll := ListReflectionSet{ + method: method, defaultPageSize: uint64(20), dataColumn: table.DataColumn, } - for _, field := range res.Properties { + for _, field := range method.Response.Properties { switch ft := field.Schema.(type) { case *j5schema.ObjectField: @@ -147,11 +146,11 @@ func buildListReflection(req *j5schema.ObjectSchema, res *j5schema.ObjectSchema, } if ll.arrayField == nil { - return nil, fmt.Errorf("no repeated field in response, %s must have a repeated message", res.FullName()) + return nil, fmt.Errorf("no repeated field in response, %s must have a repeated message", method.Response.FullName()) } if ll.pageResponseField == nil { - return nil, fmt.Errorf("no page field in response, %s must have a j5.list.v1.PageResponse", res.FullName()) + return nil, fmt.Errorf("no page field in response, %s must have a j5.list.v1.PageResponse", method.Response.FullName()) } err = validateListAnnotations(ll.arrayObject) @@ -164,13 +163,13 @@ func buildListReflection(req *j5schema.ObjectSchema, res *j5schema.ObjectSchema, return nil, fmt.Errorf("default sorts: %w", err) } - ll.tieBreakerFields, err = buildTieBreakerFields(ll.dataColumn, req, ll.arrayObject, table.FallbackSortColumns) + ll.tieBreakerFields, err = buildTieBreakerFields(ll.dataColumn, method.Request, ll.arrayObject, table.FallbackSortColumns) if err != nil { return nil, fmt.Errorf("tie breaker fields: %w", err) } if len(ll.defaultSortFields) == 0 && len(ll.tieBreakerFields) == 0 { - return nil, fmt.Errorf("no default sort field found, %s must have at least one field annotated as default sort, or specify a tie breaker in %s", ll.arrayField.FullName(), req.FullName()) + return nil, fmt.Errorf("no default sort field found, %s must have at least one field annotated as default sort, or specify a tie breaker in %s", ll.arrayField.FullName(), method.Request.FullName()) } f, err := buildDefaultFilters(ll.dataColumn, ll.arrayObject) @@ -185,7 +184,7 @@ func buildListReflection(req *j5schema.ObjectSchema, res *j5schema.ObjectSchema, return nil, fmt.Errorf("build tsv column map: %w", err) } - for _, field := range req.Properties { + for _, field := range method.Request.Properties { switch ft := field.Schema.(type) { case *j5schema.ObjectField: switch ft.ObjectSchema().FullName() { @@ -206,11 +205,11 @@ func buildListReflection(req *j5schema.ObjectSchema, res *j5schema.ObjectSchema, } if ll.pageRequestField == nil { - return nil, fmt.Errorf("no page field in request, %s must have a j5.list.v1.PageRequest", req.FullName()) + return nil, fmt.Errorf("no page field in request, %s must have a j5.list.v1.PageRequest", method.Request.FullName()) } if ll.queryRequestField == nil { - return nil, fmt.Errorf("no query field in request, %s must have a j5.list.v1.QueryRequest", req.FullName()) + return nil, fmt.Errorf("no query field in request, %s must have a j5.list.v1.QueryRequest", method.Request.FullName()) } arraySchema := ll.arrayField.Schema.(*j5schema.ArrayField) @@ -223,7 +222,7 @@ func buildListReflection(req *j5schema.ObjectSchema, res *j5schema.ObjectSchema, return &ll, nil } -type Lister[REQ ListRequest, RES ListResponse] struct { +type Lister struct { ListReflectionSet tableName string @@ -233,24 +232,19 @@ type Lister[REQ ListRequest, RES ListResponse] struct { auth AuthProvider authJoin []*LeftJoin - requestFilter func(REQ) (map[string]any, error) + requestFilter func(j5reflect.Object) (map[string]any, error) validator *j5validate.Validator } -func NewLister[ - REQ ListRequest, - RES ListResponse, -](spec ListSpec[REQ, RES]) (*Lister[REQ, RES], error) { - ll := &Lister[REQ, RES]{ +func NewLister(spec ListSpec) (*Lister, error) { + ll := &Lister{ tableName: spec.TableName, auth: spec.Auth, authJoin: spec.AuthJoin, } - descriptors := newMethodDescriptor[REQ, RES]() - - listFields, err := buildListReflection(descriptors.request, descriptors.response, spec.TableSpec) + listFields, err := buildListReflection(spec.Method, spec.TableSpec) if err != nil { return nil, err } @@ -263,11 +257,11 @@ func NewLister[ return ll, nil } -func (ll *Lister[REQ, RES]) SetQueryLogger(logger QueryLogger) { +func (ll *Lister) SetQueryLogger(logger QueryLogger) { ll.queryLogger = logger } -func (ll *Lister[REQ, RES]) List(ctx context.Context, db Transactor, req, res j5reflect.Object) error { +func (ll *Lister) List(ctx context.Context, db Transactor, req, res j5reflect.Object) error { if err := ll.validator.Validate(req); err != nil { return fmt.Errorf("validating request %s: %w", req.SchemaName(), err) } @@ -321,14 +315,14 @@ func (ll *Lister[REQ, RES]) List(ctx context.Context, db Transactor, req, res j5 if err != nil { return err } - list, ok := listField.AsArrayOfContainer() + list, ok := listField.AsArrayOfObject() if !ok { return fmt.Errorf("field %s in response is not an array", ll.arrayField.FullName()) } var nextToken string for idx, rowBytes := range jsonRows { - rowMessage, _ := list.NewContainerElement() + rowMessage, _ := list.NewObjectElement() err := j5codec.Global.JSONToReflect(rowBytes, rowMessage) if err != nil { @@ -341,11 +335,21 @@ func (ll *Lister[REQ, RES]) List(ctx context.Context, db Transactor, req, res j5 // the sorting and filtering of the query and either encode them // directly, or encode a subset of the message as required. - nextToken = base64.StdEncoding.EncodeToString(rowBytes) + nextToken, err = encodePageToken(rowMessage, selectQuery.sortFields) + if err != nil { + return fmt.Errorf("encode page token: %w", err) + } break } } + // TODO: This drops the pagination token from the list. + // Consider adding support to J5Reflect to create the object element without + // attaching it to the array + if list.Length() > int(pageSize) { + list.Truncate(int(pageSize)) + } + if nextToken != "" { pageRes, err := res.GetOrCreateValue(ll.pageResponseField.JSONName) if err != nil { @@ -376,14 +380,11 @@ func (ll *Lister[REQ, RES]) List(ctx context.Context, db Transactor, req, res j5 func encodePageToken(rowMessage j5reflect.Object, sortFields []sortSpec) (string, error) { vals := map[string]any{} for _, sortField := range sortFields { - fieldVal, ok, err := sortField.Path.GetValue(rowMessage) + fieldVal, _, err := sortField.Path.GetValue(rowMessage) if err != nil { return "", fmt.Errorf("sort field %s: %w", sortField.errorName(), err) } - if !ok { - vals[sortField.Path.IDPath()] = nil - continue - } + fmt.Printf("Set Sort Field %s to %v\n", sortField.errorName(), fieldVal) vals[sortField.Path.IDPath()] = fieldVal } @@ -400,7 +401,10 @@ func decodePageToken(token string, sortFields []sortSpec) (map[string]any, error if err != nil { return nil, fmt.Errorf("decode page token: %w", err) } - if err := json.Unmarshal(jsonBytes, &vals); err != nil { + dec := json.NewDecoder(strings.NewReader(string(jsonBytes))) + dec.UseNumber() + err = dec.Decode(&vals) + if err != nil { return nil, fmt.Errorf("unmarshal page token: %w", err) } if len(vals) != len(sortFields) { @@ -422,6 +426,10 @@ func fieldAs[T any](obj j5reflect.Object, path ...string) (val T, ok bool, err e return val, false, fmt.Errorf("field %s in %s not an object", path, obj.SchemaName()) } + if !objField.IsSet() { + return val, false, nil + } + val, ok = objField.ProtoReflect().Interface().(T) if !ok { return val, false, fmt.Errorf("field %s in %s not of type %T", path, obj.SchemaName(), (*T)(nil)) @@ -431,7 +439,18 @@ func fieldAs[T any](obj j5reflect.Object, path ...string) (val T, ok bool, err e } -func (ll *Lister[REQ, RES]) BuildQuery(ctx context.Context, req j5reflect.Object, res j5reflect.Object) (*sq.SelectBuilder, error) { +type Query struct { + *sq.SelectBuilder + sortFields []sortSpec +} + +func (ll *Lister) BuildQuery(ctx context.Context, req j5reflect.Object, res j5reflect.Object) (*Query, error) { + + err := assertObjectsMatch(ll.method, req, res) + if err != nil { + return nil, err + } + as := newAliasSet() tableAlias := as.Next(ll.tableName) @@ -443,12 +462,7 @@ func (ll *Lister[REQ, RES]) BuildQuery(ctx context.Context, req j5reflect.Object filterFields := []sq.Sqlizer{} if ll.requestFilter != nil { - reqVal, ok := req.ProtoReflect().Interface().(REQ) - if !ok { - return nil, fmt.Errorf("request is not of type %T", (*REQ)(nil)) - } - - filter, err := ll.requestFilter(reqVal) + filter, err := ll.requestFilter(req) if err != nil { return nil, err } @@ -567,135 +581,218 @@ func (ll *Lister[REQ, RES]) BuildQuery(ctx context.Context, req j5reflect.Object selectQuery.Limit(pageSize + 1) reqPage, ok, err := fieldAs[*list_j5pb.PageRequest](req, ll.pageRequestField.JSONName) + if err != nil { + return nil, fmt.Errorf("get page request field: %w", err) + } if ok && reqPage.GetToken() != "" { - pageFields, err := decodePageToken(reqPage.GetToken(), sortFields) + + filter, err := ll.addPageFilter(reqPage.GetToken(), sortFields, tableAlias) if err != nil { - return nil, fmt.Errorf("decode page token: %w", err) + return nil, err } + selectQuery.Where(filter) - lhsFields := make([]string, 0, len(sortFields)) - rhsValues := make([]any, 0, len(sortFields)) - rhsPlaceholders := make([]string, 0, len(sortFields)) + } - for _, sortField := range sortFields { - rowSelecter := sortField.Selector(tableAlias) - valuePlaceholder := "?" + return &Query{ + SelectBuilder: selectQuery, + sortFields: sortFields, + }, nil +} - dbVal, ok := pageFields[sortField.Path.IDPath()] - if !ok { - return nil, fmt.Errorf("page token missing value for sort field %s", sortField.errorName()) - } +func (ll *Lister) addPageFilter(token string, sortFields []sortSpec, tableAlias string) (sq.Sqlizer, error) { - switch subType := dbVal.(type) { - case *dynamicpb.Message: - name := subType.Descriptor().FullName() - msgBytes, err := proto.Marshal(subType) + lhsFields := make([]string, 0, len(sortFields)) + rhsValues := make([]any, 0, len(sortFields)) + rhsPlaceholders := make([]string, 0, len(sortFields)) + + pageFields, err := decodePageToken(token, sortFields) + if err != nil { + return nil, fmt.Errorf("decode page token: %w", err) + } + + for _, sortField := range sortFields { + rowSelecter := sortField.Selector(tableAlias) + valuePlaceholder := "?" + + dbVal, ok := pageFields[sortField.Path.IDPath()] + if !ok { + return nil, fmt.Errorf("page token missing value for sort field %s", sortField.errorName()) + } + + switch schema := sortField.Path.leafField.Schema.(type) { + //case *j5schema.EnumField: + + case *j5schema.ScalarSchema: + + ft := schema.ToJ5Field() + + switch ft := ft.Type.(type) { + case *schema_j5pb.Field_String_, *schema_j5pb.Field_Key: + dbVal, ok = dbVal.(string) + if !ok { + return nil, fmt.Errorf("sort field %s is a string, but value is not a string", sortField.errorName()) + } + if sortField.desc { + // String fields aren't valid for sorting, they can only be used + // for the tie-breaker so the order itself is not important, only + // that it is consistent + return nil, fmt.Errorf("sort field %s is a string, strings cannot be sorted DESC", sortField.errorName()) + } + + case *schema_j5pb.Field_Integer: + number, ok := dbVal.(json.Number) + if !ok { + stringVal, ok := dbVal.(string) + if !ok { + return nil, fmt.Errorf("sort field %s is an integer, but value is %+v", sortField.errorName(), dbVal) + } + number = json.Number(stringVal) + } + int64val, err := number.Int64() if err != nil { - return nil, fmt.Errorf("marshal %s: %w", name, err) + return nil, fmt.Errorf("sort field %s is an integer, but value is not a valid integer: %w", sortField.errorName(), err) } - switch name { - case "google.protobuf.Timestamp": - ts := timestamppb.Timestamp{} - if err := proto.Unmarshal(msgBytes, &ts); err != nil { - return nil, fmt.Errorf("unmarshal %s: %w", name, err) + switch ft.Integer.Format { + case schema_j5pb.IntegerField_FORMAT_INT32, schema_j5pb.IntegerField_FORMAT_UINT32: + + if sortField.desc { + int64val = int64val * -1 + rowSelecter = fmt.Sprintf("-1 * (%s)::integer", rowSelecter) } - intVal := ts.AsTime().Round(time.Microsecond).UnixMicro() - // Go rounds half-up. - // Postgres is undocumented, but can only handle - // microseconds. - rowSelecter = fmt.Sprintf("(EXTRACT(epoch FROM (%s)::timestamp) * 1000000)::bigint", rowSelecter) + + case schema_j5pb.IntegerField_FORMAT_INT64, schema_j5pb.IntegerField_FORMAT_UINT64: + if sortField.desc { - intVal = intVal * -1 - rowSelecter = fmt.Sprintf("-1 * %s", rowSelecter) + int64val = int64val * -1 + rowSelecter = fmt.Sprintf("-1 * (%s)::bigint", rowSelecter) } - dbVal = intVal + default: - return nil, fmt.Errorf("sort field %s is a message of type %s", sortField.errorName(), name) + panic(fmt.Sprintf("unknown integer format %s for field %s", ft.Integer.Format, sortField.errorName())) + } + dbVal = int64val + + case *schema_j5pb.Field_Float: + number, ok := dbVal.(json.Number) + if !ok { + stringVal, ok := dbVal.(string) + if !ok { + return nil, fmt.Errorf("sort field %s is a float, but value is not a number", sortField.errorName()) + } + number = json.Number(stringVal) } + float64val, err := number.Float64() + if err != nil { + return nil, fmt.Errorf("sort field %s is a float, but value is not a valid float: %w", sortField.errorName(), err) + } + switch ft.Float.Format { + case schema_j5pb.FloatField_FORMAT_FLOAT32: + if sortField.desc { + float64val = float64val * -1 + rowSelecter = fmt.Sprintf("-1 * (%s)::real", rowSelecter) + } - case string: - dbVal = subType - if sortField.desc { - // String fields aren't valid for sorting, they can only be used - // for the tie-breaker so the order itself is not important, only - // that it is consistent - return nil, fmt.Errorf("sort field %s is a string, strings cannot be sorted DESC", sortField.errorName()) + case schema_j5pb.FloatField_FORMAT_FLOAT64: + if sortField.desc { + float64val = float64val * -1 + rowSelecter = fmt.Sprintf("-1 * (%s)::double precision", rowSelecter) + } } + dbVal = float64val - case int64: + case *schema_j5pb.Field_Bool: if sortField.desc { - dbVal = dbVal.(int64) * -1 - rowSelecter = fmt.Sprintf("-1 * (%s)::bigint", rowSelecter) + dbVal = !dbVal.(bool) + rowSelecter = fmt.Sprintf("NOT (%s)::boolean", rowSelecter) } - case int32: - if sortField.desc { - dbVal = dbVal.(int32) * -1 - rowSelecter = fmt.Sprintf("-1 * (%s)::integer", rowSelecter) + + case *schema_j5pb.Field_Timestamp: + + stringVal, ok := dbVal.(string) + if !ok { + return nil, fmt.Errorf("sort field %s is a timestamp, but value is not a string", sortField.errorName()) } - case float32: - if sortField.desc { - dbVal = dbVal.(float32) * -1 - rowSelecter = fmt.Sprintf("-1 * (%s)::real", rowSelecter) + ts, err := time.Parse(time.RFC3339, stringVal) + if err != nil { + return nil, fmt.Errorf("sort field %s is a timestamp, but value is not a valid timestamp: %w", sortField.errorName(), err) } - case float64: + + intVal := ts.Round(time.Microsecond).UnixMicro() + // Go rounds half-up. + // Postgres is undocumented, but can only handle + // microseconds. + rowSelecter = fmt.Sprintf("(EXTRACT(epoch FROM (%s)::timestamp) * 1000000)::bigint", rowSelecter) if sortField.desc { - dbVal = dbVal.(float64) * -1 - rowSelecter = fmt.Sprintf("-1 * (%s)::double precision", rowSelecter) + intVal = intVal * -1 + rowSelecter = fmt.Sprintf("-1 * %s", rowSelecter) } - case bool: - if sortField.desc { - dbVal = !dbVal.(bool) - rowSelecter = fmt.Sprintf("NOT (%s)::boolean", rowSelecter) + dbVal = intVal + + case *schema_j5pb.Field_Date: + stringVal, ok := dbVal.(string) + if !ok { + return nil, fmt.Errorf("sort field %s is a date, but value is not a string", sortField.errorName()) + } + + date, err := date_j5t.DateFromString(stringVal) + if err != nil { + return nil, fmt.Errorf("sort field %s is a date, but value is not a valid date: %w", sortField.errorName(), err) } + intVal := date.AsTime(time.UTC).Round(time.Microsecond).Unix() - // TODO: Reversals for the other types that are sortable + rowSelecter = fmt.Sprintf("(EXTRACT(epoch FROM (%s)::date))::bigint", rowSelecter) + if sortField.desc { + intVal = intVal * -1 + rowSelecter = fmt.Sprintf("-1 * %s", rowSelecter) + } + dbVal = intVal - default: - return nil, fmt.Errorf("sort field %s is of type %T", sortField.errorName(), dbVal) } - lhsFields = append(lhsFields, rowSelecter) - rhsValues = append(rhsValues, dbVal) - rhsPlaceholders = append(rhsPlaceholders, valuePlaceholder) + default: + return nil, fmt.Errorf("sort field %s is not a scalar or enum", sortField.errorName()) } - // From https://www.postgresql.org/docs/current/functions-comparisons.html#ROW-WISE-COMPARISON - // >> for the <, <=, > and >= cases, the row elements are compared left-to-right, stopping as soon - // >> as an unequal or null pair of elements is found. If either of this pair of elements is null, - // >> the result of the row comparison is unknown (null); otherwise comparison of this pair of elements - // >> determines the result. For example, ROW(1,2,NULL) < ROW(1,3,0) yields true, not null, because the - // >> third pair of elements are not considered. - // - // This means that we can use the row comparison with the same fields as - // the sort fields to exclude the exact record we want, rather than the - // filter being applied to all fields equally which takes out valid - // records. - // `(1, 30) >= (1, 20)` is true, so is `1 >= 1 AND 30 >= 20` - // `(2, 10) >= (1, 20)` is also true, but `2 >= 1 AND 10 >= 20` is false - // Since the tuple comparison starts from the left and stops at the first term. - // - // The downside is that we have to negate the values to sort in reverse - // order, as we don't get an operator per term. This gets strange for - // some data types and will create some crazy indexes. - // - // TODO: Optimize the cases when the order is ASC and therefore we don't - // need to flip, but also the cases where we can just reverse the whole - // comparison and reverse all flips to simplify, noting again that it - // does not actually matter in which order the string field is sorted... - // or don't because indexes. - - selectQuery = selectQuery.Where( - fmt.Sprintf("(%s) >= (%s)", - strings.Join(lhsFields, ","), - strings.Join(rhsPlaceholders, ","), - ), rhsValues...) - } - - return selectQuery, nil -} + lhsFields = append(lhsFields, rowSelecter) + rhsValues = append(rhsValues, dbVal) + rhsPlaceholders = append(rhsPlaceholders, valuePlaceholder) + } + + // From https://www.postgresql.org/docs/current/functions-comparisons.html#ROW-WISE-COMPARISON + // >> for the <, <=, > and >= cases, the row elements are compared left-to-right, stopping as soon + // >> as an unequal or null pair of elements is found. If either of this pair of elements is null, + // >> the result of the row comparison is unknown (null); otherwise comparison of this pair of elements + // >> determines the result. For example, ROW(1,2,NULL) < ROW(1,3,0) yields true, not null, because the + // >> third pair of elements are not considered. + // + // This means that we can use the row comparison with the same fields as + // the sort fields to exclude the exact record we want, rather than the + // filter being applied to all fields equally which takes out valid + // records. + // `(1, 30) >= (1, 20)` is true, so is `1 >= 1 AND 30 >= 20` + // `(2, 10) >= (1, 20)` is also true, but `2 >= 1 AND 10 >= 20` is false + // Since the tuple comparison starts from the left and stops at the first term. + // + // The downside is that we have to negate the values to sort in reverse + // order, as we don't get an operator per term. This gets strange for + // some data types and will create some crazy indexes. + // + // TODO: Optimize the cases when the order is ASC and therefore we don't + // need to flip, but also the cases where we can just reverse the whole + // comparison and reverse all flips to simplify, noting again that it + // does not actually matter in which order the string field is sorted... + // or don't because indexes. + + return sq.Expr( + fmt.Sprintf("(%s) >= (%s)", + strings.Join(lhsFields, ","), + strings.Join(rhsPlaceholders, ","), + ), rhsValues...), nil -func (ll *Lister[REQ, RES]) getPageSize(req j5reflect.Object) (uint64, error) { +} +func (ll *Lister) getPageSize(req j5reflect.Object) (uint64, error) { pageField, ok, err := req.GetField(ll.pageRequestField.JSONName, "pageSize") if err != nil { return 0, fmt.Errorf("get page request field %s: %w", ll.pageRequestField.JSONName, err) @@ -715,25 +812,14 @@ func (ll *Lister[REQ, RES]) getPageSize(req j5reflect.Object) (uint64, error) { int64Val, ok := intVal.(int64) if !ok { - return 0, fmt.Errorf("field %s in request is not an int64", ll.pageRequestField.JSONName) + return 0, fmt.Errorf("field %s in request is not an int64, got %T", ll.pageRequestField.JSONName, intVal) } - return uint64(int64Val), nil -} - -func camelToSnake(jsonName string) string { - var out strings.Builder - for i, r := range jsonName { - if unicode.IsUpper(r) { - if i > 0 { - out.WriteRune('_') - } - out.WriteRune(unicode.ToLower(r)) - } else { - out.WriteRune(r) - } + returnVal := uint64(int64Val) + if returnVal > ll.defaultPageSize { + return 0, fmt.Errorf("page size %d exceeds maximum of %d", val, ll.defaultPageSize) } - return out.String() + return returnVal, nil } func validateListAnnotations(object *j5schema.ObjectSchema) error { @@ -752,7 +838,7 @@ func validateListAnnotations(object *j5schema.ObjectSchema) error { return nil } -func (ll *Lister[REQ, RES]) validateQueryRequest(query *list_j5pb.QueryRequest) error { +func (ll *Lister) validateQueryRequest(query *list_j5pb.QueryRequest) error { err := validateQueryRequestSorts(ll.arrayObject, query.GetSorts()) if err != nil { return fmt.Errorf("sort validation: %w", err) diff --git a/pquery/lister_test.go b/pquery/lister_test.go index 7767ade..1cc9540 100644 --- a/pquery/lister_test.go +++ b/pquery/lister_test.go @@ -6,8 +6,8 @@ import ( "github.com/pentops/flowtest/prototest" "github.com/pentops/golib/gl" + "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/protostate/internal/pgstore" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/reflect/protoreflect" @@ -57,14 +57,12 @@ type J5Proto struct { proto.Message } -func TestBuildListReflection(t *testing.T) { - - type tableMod func(t testing.TB, spec *TableSpec, req, res protoreflect.MessageDescriptor) +type tableMod func(t testing.TB, spec *TableSpec, req, res protoreflect.MessageDescriptor) - build := func(t testing.TB, input string, spec tableMod) (*ListReflectionSet, error) { - t.Helper() - pdf := prototest.DescriptorsFromSource(t, map[string]string{ - "test.proto": ` +func testListReflection(t testing.TB, input string, spec tableMod) (*ListReflectionSet, error) { + t.Helper() + pdf := prototest.DescriptorsFromSource(t, map[string]string{ + "test.proto": ` syntax = "proto3"; package test; @@ -83,32 +81,41 @@ func TestBuildListReflection(t *testing.T) { } ` + input, - }) - - requestDesc := pdf.MessageByName(t, "test.FooListRequest") - responseDesc := pdf.MessageByName(t, "test.FooListResponse") - table := &TableSpec{} - if spec != nil { - spec(t, table, requestDesc, responseDesc) - } + }) - schemaSet := j5schema.NewSchemaCache() - requestObj, err := schemaSet.Schema(requestDesc) - if err != nil { - return nil, err - } - responseObj, err := schemaSet.Schema(responseDesc) - if err != nil { - return nil, err - } + requestDesc := pdf.MessageByName(t, "test.FooListRequest") + responseDesc := pdf.MessageByName(t, "test.FooListResponse") + table := &TableSpec{ + DataColumn: "data", + } + if spec != nil { + spec(t, table, requestDesc, responseDesc) + } - return buildListReflection(requestObj.(*j5schema.ObjectSchema), responseObj.(*j5schema.ObjectSchema), *table) + schemaSet := j5schema.NewSchemaCache() + requestObj, err := schemaSet.Schema(requestDesc) + if err != nil { + return nil, err + } + responseObj, err := schemaSet.Schema(responseDesc) + if err != nil { + return nil, err + } + ms := &j5schema.MethodSchema{ + Request: requestObj.(*j5schema.ObjectSchema), + Response: responseObj.(*j5schema.ObjectSchema), } + return buildListReflection(ms, *table) +} + +func TestBuildListReflection(t *testing.T) { + runHappy := func(name string, input string, spec tableMod, callback func(*testing.T, *ListReflectionSet)) { + t.Helper() t.Run(name, func(t *testing.T) { t.Helper() - set, err := build(t, input, spec) + set, err := testListReflection(t, input, spec) if err != nil { t.Fatal(err) @@ -120,7 +127,7 @@ func TestBuildListReflection(t *testing.T) { t.Helper() t.Run(name, func(t *testing.T) { t.Helper() - _, err := build(t, input, spec) + _, err := testListReflection(t, input, spec) if err == nil { t.Fatal("expected error") } @@ -162,6 +169,59 @@ func TestBuildListReflection(t *testing.T) { assert.Equal(t, uint64(20), lr.defaultPageSize) }) + runHappy("dynamic filter", ` + message FooListRequest { + j5.list.v1.PageRequest page = 1; + j5.list.v1.QueryRequest query = 2; + option (j5.list.v1.list_request) = { + sort_tiebreaker: ["val"] + }; + } + + message FooListResponse { + repeated Foo foos = 1; + j5.list.v1.PageResponse page = 2; + } + + message Foo { + int64 val = 1 [(j5.list.v1.field).int64.filtering.filterable = true]; + } + `, nil, func(t *testing.T, lr *ListReflectionSet) { + + statements, err := lr.buildDynamicFilter("ALIAS", []*list_j5pb.Filter{{ + Type: &list_j5pb.Filter_Field{ + Field: &list_j5pb.Field{ + Name: "val", + Type: &list_j5pb.FieldType{ + Type: &list_j5pb.FieldType_Value{ + Value: "e10", + }, + }, + }, + }, + }}) + + if err != nil { + t.Fatal(err) + } + + if len(statements) != 1 { + t.Fatal("expected one statement, got", len(statements)) + } + statement := statements[0] + + txt, params, err := statement.ToSql() + if err != nil { + t.Fatal(err) + } + t.Logf("SQL: %s, Params: %v", txt, params) + want := "(jsonb_path_query_array(ALIAS.data, '$.val') @> ?::jsonb)" + if txt != want { + t.Errorf("SQL mismatch:\n got: %s\nwant: %s", txt, want) + } + + }) + runHappy("override default page size by validation", ` message FooListRequest { j5.list.v1.PageRequest page = 1; @@ -197,7 +257,7 @@ func TestBuildListReflection(t *testing.T) { func(t testing.TB, table *TableSpec, req, res protoreflect.MessageDescriptor) { table.FallbackSortColumns = []ProtoField{{ valueColumn: gl.Ptr("id"), - pathInRoot: pgstore.JSONPathSpec{"id"}, + pathInRoot: JSONPathSpec{"id"}, }} }, func(t *testing.T, lr *ListReflectionSet) { diff --git a/pquery/method.go b/pquery/method.go new file mode 100644 index 0000000..b3b6dbb --- /dev/null +++ b/pquery/method.go @@ -0,0 +1,30 @@ +package pquery + +import ( + "fmt" + + "github.com/pentops/j5/lib/j5reflect" + "github.com/pentops/j5/lib/j5schema" +) + +func assertSchemaMatch(wantSchema *j5schema.ObjectSchema, gotObject j5reflect.Object) error { + wantSchemaName := wantSchema.FullName() + gotSchemaName := gotObject.SchemaName() + if wantSchemaName != gotSchemaName { + return fmt.Errorf("provided object is type %s, expects %s", + gotSchemaName, + wantSchemaName, + ) + } + return nil +} +func assertObjectsMatch(ms *j5schema.MethodSchema, req, res j5reflect.Object) error { + if err := assertSchemaMatch(ms.Request, req); err != nil { + return fmt.Errorf("request object: %w", err) + } + if err := assertSchemaMatch(ms.Response, res); err != nil { + return fmt.Errorf("response object: %w", err) + } + + return nil +} diff --git a/internal/pgstore/path.go b/pquery/path.go similarity index 85% rename from internal/pgstore/path.go rename to pquery/path.go index 789efcc..9b86944 100644 --- a/internal/pgstore/path.go +++ b/pquery/path.go @@ -1,4 +1,4 @@ -package pgstore +package pquery import ( "fmt" @@ -117,6 +117,14 @@ func (pp Path) pathNodeNames() string { return strings.Join(names, ".") } +func (pp *Path) pathParts() []string { + parts := make([]string, 0, len(pp.path)) + for _, node := range pp.path { + parts = append(parts, node.field.JSONName) + } + return parts +} + func (pp *Path) JSONBArrowPath() string { elements := make([]string, 0, len(pp.path)) end, path := pp.path[len(pp.path)-1], pp.path[:len(pp.path)-1] @@ -163,6 +171,11 @@ func (pp *Path) JSONPathQuery() string { } } + if pp.leafOneof != nil { + // Can't use quotes, must escape the ! + elements = append(elements, `.\!type`) + } + return strings.Join(elements, "") } @@ -246,13 +259,13 @@ func objectProperty(obj hasPropertySet, pathElem string, pt pathType) (*j5schema case clientPath: prop := obj.ClientProperties().ByJSONName(pathElem) if prop == nil { - return nil, fmt.Errorf("property %s not found in object %s", pathElem, obj.FullName()) + return nil, fmt.Errorf("client property %q not found in %s", pathElem, obj.FullName()) } return prop, nil case outerPath: prop := obj.AllProperties().ByJSONName(pathElem) if prop == nil { - return nil, fmt.Errorf("property %s not found in object %s", pathElem, obj.FullName()) + return nil, fmt.Errorf("property %q not found in %s", pathElem, obj.FullName()) } return prop, nil @@ -277,7 +290,6 @@ func newPath(rootMessage *j5schema.ObjectSchema, fieldPath []string, pathType pa walkMessage = rootMessage var pathElem string walkPath := fieldPath - var nodes []pathNode for { pathElem, walkPath = walkPath[0], walkPath[1:] @@ -288,20 +300,19 @@ func newPath(rootMessage *j5schema.ObjectSchema, fieldPath []string, pathType pa if err != nil { return nil, err } - node = pathNode{ name: pathElem, field: field, } case *j5schema.OneofSchema: - // Very Special Edge Case: Oneof wrapper types allow the client to - // filter based on the type of the oneof. So the oneof can be at the - // end of the path, and the field can be the oneof wrapper type. - if len(walkPath) == 0 && pathElem == "type" { - pathSpec.path = append(pathSpec.path, nodes...) + + if len(walkPath) == 0 && pathElem == "!type" { + // Very Special Edge Case: Oneof wrapper types allow the client to + // filter based on the type of the oneof. So the oneof can be at the + // end of the path, and the field can be the oneof wrapper type. pathSpec.leafOneof = walkMessage - break + return pathSpec, nil } field, err := objectProperty(walkMessage, pathElem, pathType) @@ -313,12 +324,15 @@ func newPath(rootMessage *j5schema.ObjectSchema, fieldPath []string, pathType pa name: pathElem, field: field, } + default: + return nil, fmt.Errorf("field %q in %s.%s is not an object or oneof, no such field %q", pathElem, rootMessage.FullName(), strings.Join(fieldPath, "."), strings.Join(walkPath, ".")) } pathSpec.path = append(pathSpec.path, node) + pathSpec.leafField = node.field + if len(walkPath) == 0 { - pathSpec.leafField = node.field - break + return pathSpec, nil } switch nextType := node.field.Schema.(type) { @@ -328,20 +342,23 @@ func newPath(rootMessage *j5schema.ObjectSchema, fieldPath []string, pathType pa walkMessage = nextType.OneofSchema() case j5schema.RootSchema: walkMessage = nextType + case *j5schema.ArrayField: + items := nextType.ItemSchema + switch items := items.(type) { + case *j5schema.ObjectField: + walkMessage = items.ObjectSchema() + case *j5schema.OneofField: + walkMessage = items.OneofSchema() + default: + return nil, fmt.Errorf("array field %q in %s.%s is an array, but not of an object or oneof, no such field %q", pathElem, rootMessage.FullName(), strings.Join(fieldPath, "."), strings.Join(walkPath, ".")) + } + case *j5schema.MapField: + return nil, fmt.Errorf("map fields not supported in path %q", strings.Join(fieldPath, ".")) default: - return nil, fmt.Errorf("field %s is not a message, but path elements remain", pathElem) + return nil, fmt.Errorf("field %q in %s.%s is a scalar, no such field %q", pathElem, rootMessage.FullName(), strings.Join(fieldPath, "."), strings.Join(walkPath, ".")) } } - return pathSpec, nil -} - -func (pp *Path) pathParts() []string { - parts := make([]string, 0, len(pp.path)) - for _, node := range pp.path { - parts = append(parts, node.field.JSONName) - } - return parts } func (pp *Path) GetValue(msg j5reflect.Object) (any, bool, error) { @@ -349,13 +366,10 @@ func (pp *Path) GetValue(msg j5reflect.Object) (any, bool, error) { return nil, false, fmt.Errorf("empty path") } - lastField, ok, err := msg.GetField(pp.pathParts()...) + lastField, _, err := msg.GetField(pp.pathParts()...) if err != nil { return nil, false, fmt.Errorf("getting field %s: %w", pp.pathNodeNames(), err) } - if !ok { - return nil, false, nil - } if pp.leafOneof != nil { // this is a type filter. diff --git a/internal/pgstore/path_test.go b/pquery/path_test.go similarity index 93% rename from internal/pgstore/path_test.go rename to pquery/path_test.go index 6e6ee73..a21e5f0 100644 --- a/internal/pgstore/path_test.go +++ b/pquery/path_test.go @@ -1,4 +1,4 @@ -package pgstore +package pquery import ( "testing" @@ -57,7 +57,7 @@ func TestFindFieldSpec(t *testing.T) { {path: JSONPathSpec{"id"}}, {path: JSONPathSpec{"profile", "weight"}}, {path: JSONPathSpec{"profile", "type"}}, - {path: JSONPathSpec{"profile", "type", "type"}, isOneof: true}, + {path: JSONPathSpec{"profile", "type", "!type"}, isOneof: true}, {path: JSONPathSpec{"profile", "type", "card", "size"}}, } @@ -72,6 +72,9 @@ func TestFindFieldSpec(t *testing.T) { if spec.leafOneof == nil { t.Fatal("missing oneof field") } + if spec.leafField == nil { + t.Fatal("missing leaf field in oneof.type") + } } else { if spec.leafField == nil { t.Fatal("missing leaf field") diff --git a/pquery/query.go b/pquery/query.go index 278ed87..3252bb5 100644 --- a/pquery/query.go +++ b/pquery/query.go @@ -4,8 +4,6 @@ import ( "context" "fmt" - "github.com/pentops/j5/lib/j5reflect" - "github.com/pentops/j5/lib/j5schema" "github.com/pentops/sqrlx.go/sqrlx" ) @@ -41,18 +39,3 @@ type LeftJoin struct { TableName string On JoinFields } - -// MethodDescriptor is the RequestResponse pair in the gRPC Method -type methodDescriptor[REQ j5reflect.Object, RES j5reflect.Object] struct { - request *j5schema.ObjectSchema - response *j5schema.ObjectSchema -} - -func newMethodDescriptor[REQ j5reflect.Object, RES j5reflect.Object]() *methodDescriptor[REQ, RES] { - req := *new(REQ) - res := *new(RES) - return &methodDescriptor[REQ, RES]{ - request: req.ObjectSchema(), - response: res.ObjectSchema(), - } -} diff --git a/pquery/search.go b/pquery/search.go index e42b1eb..a9aebad 100644 --- a/pquery/search.go +++ b/pquery/search.go @@ -3,12 +3,12 @@ package pquery import ( "fmt" "regexp" + "strings" sq "github.com/elgris/sqrl" "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/protostate/internal/pgstore" ) /* @@ -141,7 +141,7 @@ func validateSearchAnnotationsField(ids map[string]string, field *j5schema.Objec func validateQueryRequestSearches(message *j5schema.ObjectSchema, searches []*list_j5pb.Search) error { for _, search := range searches { - spec, err := pgstore.NewJSONPath(message, pgstore.ParseJSONPathSpec(search.GetField())) + spec, err := NewJSONPath(message, ParseJSONPathSpec(search.GetField())) if err != nil { return fmt.Errorf("field spec: %w", err) } @@ -178,10 +178,35 @@ func validateSearchAnnotationsField(ids map[string]string, field *j5schema.Objec */ var rePgUnsafe = regexp.MustCompile(`[^a-zA-Z0-9_]`) +func TSVColumns(message *j5schema.ObjectSchema) ([]*TSVColumn, error) { + return tsvColumns(message) +} + func buildTsvColumnMap(message *j5schema.ObjectSchema) (map[string]string, error) { out := make(map[string]string) + paths, err := tsvColumns(message) + if err != nil { + return nil, fmt.Errorf("build tsv column map: %w", err) + } - err := pgstore.WalkPathNodes(message, func(path pgstore.Path) error { + for _, path := range paths { + out[path.IDPath()] = path.ColumnName + } + + return out, nil +} + +type TSVColumn struct { + Path + ColumnName string +} + +func tsvColumns(message *j5schema.ObjectSchema) ([]*TSVColumn, error) { + + usedColNames := make(map[string]struct{}) + out := []*TSVColumn{} + + err := WalkPathNodes(message, func(path Path) error { field := path.LeafField() switch bigSchema := field.Schema.(type) { @@ -195,7 +220,22 @@ func buildTsvColumnMap(message *j5schema.ObjectSchema) (map[string]string, error idPath := path.IDPath() columnName := rePgUnsafe.ReplaceAllString(idPath, "_") - out[idPath] = fmt.Sprintf("tsv_%s", columnName) + columnName = strings.ToLower(columnName) + if _, exists := usedColNames[columnName]; exists { + // the character set isn't sufficient to guarantee + // uniqueness when dropping the casing, e.g., `foo_d` + // becomes `fooD`, which becomes `food`, and `food` itself + // could also be valid. + // It's unlikely to come up, but better throw here than + // behave unexpectedly later. + return fmt.Errorf("duplicate tsv column name %q for path %q", columnName, idPath) + } + usedColNames[columnName] = struct{}{} + + out = append(out, &TSVColumn{ + Path: path, + ColumnName: fmt.Sprintf("tsv_%s", columnName), + }) } } @@ -208,7 +248,7 @@ func buildTsvColumnMap(message *j5schema.ObjectSchema) (map[string]string, error return out, nil } -func (ll *Lister[REQ, RES]) buildDynamicSearches(tableAlias string, searches []*list_j5pb.Search) ([]sq.Sqlizer, error) { +func (ll *Lister) buildDynamicSearches(tableAlias string, searches []*list_j5pb.Search) ([]sq.Sqlizer, error) { out := []sq.Sqlizer{} for i := range searches { diff --git a/pquery/search_test.go b/pquery/search_test.go index 688396d..a063dba 100644 --- a/pquery/search_test.go +++ b/pquery/search_test.go @@ -21,7 +21,6 @@ func TestBuildTsvColumnMap(t *testing.T) { string unoptioned_field = 1; string optioned_field = 2 [(j5.list.v1.field).string.open_text.searching = { searchable: true, - field_identifier: "optioned_field" }]; Bar bar = 3; } @@ -30,13 +29,13 @@ func TestBuildTsvColumnMap(t *testing.T) { string unoptioned_field = 1; string optioned_field = 2 [(j5.list.v1.field).string.open_text.searching = { searchable: true, - field_identifier: "bar_optioned_field" }]; } `}) fooDesc := descFiles.MessageByName(t, "test.Foo") - fooObj, err := j5schema.Global.ObjectSchema(fooDesc) + schemaSet := j5schema.NewSchemaCache() + fooObj, err := schemaSet.ObjectSchema(fooDesc) if err != nil { t.Fatalf("failed to get schema for Foo: %v", err) } @@ -47,9 +46,21 @@ func TestBuildTsvColumnMap(t *testing.T) { } assert.Len(t, columnMap, 2) + want := map[string]string{ + "optionedField": "tsv_optionedfield", + "bar.optionedField": "tsv_bar_optionedfield", + } for f, c := range columnMap { t.Log("field: ", f, "\tcolumn: ", c) + if want[f] != c { + t.Errorf("expected column %s to map to field %s, but got %s", f, want[f], c) + } + delete(want, f) + } + for k, v := range want { + t.Errorf("expected column %s to map to field %s, but it was not found", v, k) } + } /* diff --git a/pquery/sort.go b/pquery/sort.go index f7a3c04..9a35f0e 100644 --- a/pquery/sort.go +++ b/pquery/sort.go @@ -6,11 +6,10 @@ import ( "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/protostate/internal/pgstore" ) type sortSpec struct { - *pgstore.NestedField + *NestedField desc bool } @@ -23,13 +22,13 @@ func buildTieBreakerFields(dataColumn string, req *j5schema.ObjectSchema, arrayF if req.ListRequest != nil && len(req.ListRequest.SortTiebreaker) > 0 { tieBreakerFields := make([]sortSpec, 0, len(req.ListRequest.SortTiebreaker)) for _, tieBreaker := range req.ListRequest.SortTiebreaker { - spec, err := pgstore.NewOuterPath(arrayField, tieBreaker) + spec, err := NewJSONPath(arrayField, tieBreaker) if err != nil { return nil, fmt.Errorf("field %s in annotated sort tiebreaker for %s: %w", tieBreaker, req.FullName(), err) } tieBreakerFields = append(tieBreakerFields, sortSpec{ - NestedField: &pgstore.NestedField{ + NestedField: &NestedField{ RootColumn: dataColumn, Path: *spec, }, @@ -47,13 +46,13 @@ func buildTieBreakerFields(dataColumn string, req *j5schema.ObjectSchema, arrayF tieBreakerFields := make([]sortSpec, 0, len(fallback)) for _, tieBreaker := range fallback { - path, err := pgstore.NewOuterPath(arrayField, tieBreaker.pathInRoot) + path, err := NewJSONPath(arrayField, tieBreaker.pathInRoot) if err != nil { return nil, fmt.Errorf("field %s in fallback sort tiebreaker for %s: %w", tieBreaker.pathInRoot, req.FullName(), err) } tieBreakerFields = append(tieBreakerFields, sortSpec{ - NestedField: &pgstore.NestedField{ + NestedField: &NestedField{ Path: *path, RootColumn: dataColumn, ValueColumn: tieBreaker.valueColumn, @@ -102,7 +101,7 @@ func getFieldSorting(field *j5schema.ObjectProperty) *list_j5pb.SortingConstrain func buildDefaultSorts(columnName string, message *j5schema.ObjectSchema) ([]sortSpec, error) { var defaultSortFields []sortSpec - err := pgstore.WalkPathNodes(message, func(path pgstore.Path) error { + err := WalkPathNodes(message, func(path Path) error { field := path.LeafField() if field == nil { return nil // oneof or something @@ -114,7 +113,7 @@ func buildDefaultSorts(columnName string, message *j5schema.ObjectSchema) ([]sor } if sortConstraint.DefaultSort { defaultSortFields = append(defaultSortFields, sortSpec{ - NestedField: &pgstore.NestedField{ + NestedField: &NestedField{ RootColumn: columnName, Path: path, }, @@ -130,17 +129,17 @@ func buildDefaultSorts(columnName string, message *j5schema.ObjectSchema) ([]sor return defaultSortFields, nil } -func (ll *Lister[REQ, RES]) buildDynamicSortSpec(sorts []*list_j5pb.Sort) ([]sortSpec, error) { +func (ll *Lister) buildDynamicSortSpec(sorts []*list_j5pb.Sort) ([]sortSpec, error) { results := []sortSpec{} direction := "" for _, sort := range sorts { - pathSpec := pgstore.ParseJSONPathSpec(sort.Field) - spec, err := pgstore.NewJSONPath(ll.arrayObject, pathSpec) + pathSpec := ParseJSONPathSpec(sort.Field) + spec, err := NewJSONPath(ll.arrayObject, pathSpec) if err != nil { return nil, fmt.Errorf("dynamic filter: find field: %w", err) } - biggerSpec := &pgstore.NestedField{ + biggerSpec := &NestedField{ Path: *spec, RootColumn: ll.dataColumn, } @@ -167,73 +166,10 @@ func (ll *Lister[REQ, RES]) buildDynamicSortSpec(sorts []*list_j5pb.Sort) ([]sor return results, nil } -func isSortingAnnotated(opts *list_j5pb.FieldConstraint) bool { - annotated := false - - if opts != nil { - switch opts.Type.(type) { - case *list_j5pb.FieldConstraint_Double: - if opts.GetDouble().Sorting != nil { - annotated = true - } - case *list_j5pb.FieldConstraint_Fixed32: - if opts.GetFixed32().Sorting != nil { - annotated = true - } - case *list_j5pb.FieldConstraint_Fixed64: - if opts.GetFixed64().Sorting != nil { - annotated = true - } - case *list_j5pb.FieldConstraint_Float: - if opts.GetFloat().Sorting != nil { - annotated = true - } - case *list_j5pb.FieldConstraint_Int32: - if opts.GetInt32().Sorting != nil { - annotated = true - } - case *list_j5pb.FieldConstraint_Int64: - if opts.GetInt64().Sorting != nil { - annotated = true - } - case *list_j5pb.FieldConstraint_Sfixed32: - if opts.GetSfixed32().Sorting != nil { - annotated = true - } - case *list_j5pb.FieldConstraint_Sfixed64: - if opts.GetSfixed64().Sorting != nil { - annotated = true - } - case *list_j5pb.FieldConstraint_Sint32: - if opts.GetSint32().Sorting != nil { - annotated = true - } - case *list_j5pb.FieldConstraint_Sint64: - if opts.GetSint64().Sorting != nil { - annotated = true - } - case *list_j5pb.FieldConstraint_Uint32: - if opts.GetUint32().Sorting != nil { - annotated = true - } - case *list_j5pb.FieldConstraint_Uint64: - if opts.GetUint64().Sorting != nil { - annotated = true - } - case *list_j5pb.FieldConstraint_Timestamp: - if opts.GetTimestamp().Sorting != nil { - annotated = true - } - } - } - - return annotated -} - func validateQueryRequestSorts(message *j5schema.ObjectSchema, sorts []*list_j5pb.Sort) error { for _, sort := range sorts { - pathSpec := pgstore.ParseJSONPathSpec(sort.Field) - spec, err := pgstore.NewJSONPath(message, pathSpec) + pathSpec := ParseJSONPathSpec(sort.Field) + spec, err := NewJSONPath(message, pathSpec) if err != nil { return fmt.Errorf("find field %s: %w", sort.Field, err) } diff --git a/pquery/types.go b/pquery/types.go new file mode 100644 index 0000000..b839636 --- /dev/null +++ b/pquery/types.go @@ -0,0 +1 @@ +package pquery diff --git a/psm/builder.go b/psm/builder.go index 24a5274..066fe5a 100644 --- a/psm/builder.go +++ b/psm/builder.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/pentops/j5/lib/j5schema" "github.com/pentops/sqrlx.go/sqrlx" ) @@ -65,17 +66,34 @@ func (smc *StateMachineConfig[K, S, ST, SD, E, IE]) InitialStateFunc(cbFunc func return smc } +func (smc *StateMachineConfig[K, S, ST, SD, E, IE]) schemas() (*j5schema.ObjectSchema, *j5schema.ObjectSchema, error) { + state, ok := newJ5Message[S]().J5Reflect().RootSchema() + if !ok { + return nil, nil, fmt.Errorf("invalid state type %T, must be a j5 root schema", new(S)) + } + stateObject, ok := state.(*j5schema.ObjectSchema) + if !ok { + return nil, nil, fmt.Errorf("invalid state type %T, must be a j5 object schema", state) + } + event, ok := newJ5Message[E]().J5Reflect().RootSchema() + if !ok { + return nil, nil, fmt.Errorf("invalid event type %T, must be a j5 root schema", new(E)) + } + eventObject, ok := event.(*j5schema.ObjectSchema) + if !ok { + return nil, nil, fmt.Errorf("invalid event type %T, must be a j5 object schema", event) + } + return stateObject, eventObject, nil + +} + func (smc *StateMachineConfig[K, S, ST, SD, E, IE]) apply() error { if smc.tableMap == nil { - state, ok := (*new(S)).J5Reflect().RootSchema() - if !ok { - return fmt.Errorf("invalid state type %T, must be a j5 root schema", new(S)) - } - event, ok := (*new(E)).J5Reflect().RootSchema() - if !ok { - return fmt.Errorf("invalid event type %T, must be a j5 root schema", new(E)) + stateObject, eventObject, err := smc.schemas() + if err != nil { + return err } - tableMap, err := tableMapFromStateAndEvent(state, event) + tableMap, err := tableMapFromStateAndEvent(stateObject, eventObject) if err != nil { return err } @@ -101,13 +119,9 @@ func (smc *StateMachineConfig[K, S, ST, SD, E, IE]) BuildQueryTableSpec() (*Quer return nil, err } - state, ok := (*new(S)).J5Reflect().RootSchema() - if !ok { - return nil, fmt.Errorf("invalid state type %T, must be a j5 root schema", new(S)) - } - event, ok := (*new(E)).J5Reflect().RootSchema() - if !ok { - return nil, fmt.Errorf("invalid event type %T, must be a j5 root schema", new(E)) + state, event, err := smc.schemas() + if err != nil { + return nil, err } return &QueryTableSpec{ diff --git a/psm/gen_interfaces.go b/psm/gen_interfaces.go index 6e222d5..c3f5af8 100644 --- a/psm/gen_interfaces.go +++ b/psm/gen_interfaces.go @@ -9,6 +9,7 @@ import ( "github.com/pentops/j5/lib/j5reflect" "github.com/pentops/o5-messaging/o5msg" "github.com/pentops/sqrlx.go/sqrlx" + "google.golang.org/protobuf/proto" ) /* @@ -44,6 +45,13 @@ SE is set to a single type for each transition. type J5Message interface { J5Reflect() j5reflect.Root + Clone() any // returns the same type + proto.Message +} + +func newJ5Message[T J5Message]() T { + val := (*new(T)).ProtoReflect().New().Interface().(T) + return val } // IGenericProtoMessage is the base extensions shared by all message entities in the PSM generated code diff --git a/psm/run.go b/psm/run.go index fd88a15..137b59b 100644 --- a/psm/run.go +++ b/psm/run.go @@ -8,7 +8,6 @@ import ( "github.com/pentops/log.go/log" "github.com/pentops/o5-messaging/outbox" "github.com/pentops/sqrlx.go/sqrlx" - "google.golang.org/protobuf/proto" ) type captureStateType int @@ -28,7 +27,7 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) runEvent( ) (*S, error) { if err := sm.validateEvent(event); err != nil { - return nil, fmt.Errorf("validating event %s: %w", event.ProtoReflect().Descriptor().FullName(), err) + return nil, err } typeKey := event.UnwrapPSMEvent().PSMEventKey() @@ -63,7 +62,7 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) runEvent( var returnState *S switch captureState { case captureInitialState: - rsVal := proto.Clone(state).(S) + rsVal := state.Clone().(S) //proto.Clone(state).(S) returnState = &rsVal case captureFinalState: returnState = &state @@ -78,7 +77,7 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) runEvent( } for _, se := range baton.sideEffects { - err = sm.validator.Validate(se.msg) + err = sm.protoValidator.Validate(se.msg) if err != nil { return nil, fmt.Errorf("validate side effect: %s %w", se.msg.ProtoReflect().Descriptor().FullName(), err) } @@ -187,7 +186,7 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) storeAfterMutation( return fmt.Errorf("state machine transitioned to zero status") } - err := sm.validator.Validate(state) + err := sm.validator.Validate(state.J5Reflect()) if err != nil { return err } diff --git a/psm/state_query.go b/psm/state_query.go index eee1231..00d8b57 100644 --- a/psm/state_query.go +++ b/psm/state_query.go @@ -4,11 +4,9 @@ import ( "context" "fmt" - "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" "github.com/pentops/j5/lib/j5reflect" "github.com/pentops/j5/lib/j5schema" "github.com/pentops/protostate/pquery" - "google.golang.org/protobuf/reflect/protoreflect" ) // QueryTableSpec the TableMap with descriptors for the messages, without using @@ -22,18 +20,32 @@ type QueryTableSpec struct { // QuerySpec is the configuration for the query service side of the state // machine. Can be partially derived from the state machine table spec, but // contains types relating to the query service so cannot be fully derived. -type QuerySpec[ - GetREQ pquery.GetRequest, - GetRES pquery.GetResponse, - ListREQ pquery.ListRequest, - ListRES pquery.ListResponse, - ListEventsREQ pquery.ListRequest, - ListEventsRES pquery.ListResponse, -] struct { +type QuerySpec struct { QueryTableSpec - ListRequestFilter func(ListREQ) (map[string]any, error) - ListEventsRequestFilter func(ListEventsREQ) (map[string]any, error) + GetMethod *j5schema.MethodSchema + ListMethod *j5schema.MethodSchema + ListEventsMethod *j5schema.MethodSchema + + ListRequestFilter func(j5reflect.Object) (map[string]any, error) + ListEventsRequestFilter func(j5reflect.Object) (map[string]any, error) +} + +func (qs *QuerySpec) Validate() error { + err := qs.QueryTableSpec.Validate() + if err != nil { + return fmt.Errorf("validate QueryTableSpec: %w", err) + } + if qs.GetMethod == nil { + return fmt.Errorf("missing GetMethod in QuerySpec") + } + if qs.ListMethod == nil { + return fmt.Errorf("missing ListMethod in QuerySpec") + } + if qs.ListEventsMethod == nil { + return fmt.Errorf("missing ListEventsMethod in QuerySpec") + } + return nil } // StateQuerySet is a shortcut for manually specifying three different query @@ -41,50 +53,27 @@ type QuerySpec[ // 1. A getter for a single state // 2. A lister for the main state // 3. A lister for the events of the main state -type StateQuerySet[ - GetREQ pquery.GetRequest, - GetRES pquery.GetResponse, - ListREQ pquery.ListRequest, - ListRES pquery.ListResponse, - ListEventsREQ pquery.ListRequest, - ListEventsRES pquery.ListResponse, -] struct { - Getter *pquery.Getter[GetREQ, GetRES] - MainLister *pquery.Lister[ListREQ, ListRES] - EventLister *pquery.Lister[ListEventsREQ, ListEventsRES] +type StateQuerySet struct { + Getter *pquery.Getter + MainLister *pquery.Lister + EventLister *pquery.Lister } -func (gc *StateQuerySet[ - GetREQ, GetRES, - ListREQ, ListRES, - ListEventsREQ, ListEventsRES, -]) SetQueryLogger(logger pquery.QueryLogger) { +func (gc *StateQuerySet) SetQueryLogger(logger pquery.QueryLogger) { gc.Getter.SetQueryLogger(logger) gc.MainLister.SetQueryLogger(logger) gc.EventLister.SetQueryLogger(logger) } -func (gc *StateQuerySet[ - GetREQ, GetRES, - ListREQ, ListRES, - ListEventsREQ, ListEventsRES, -]) Get(ctx context.Context, db Transactor, reqMsg GetREQ, resMsg GetRES) error { +func (gc *StateQuerySet) Get(ctx context.Context, db Transactor, reqMsg, resMsg j5reflect.Object) error { return gc.Getter.Get(ctx, db, reqMsg, resMsg) } -func (gc *StateQuerySet[ - GetREQ, GetRES, - ListREQ, ListRES, - ListEventsREQ, ListEventsRES, -]) List(ctx context.Context, db Transactor, reqMsg *j5reflect.Object, resMsg *j5reflect.Object) error { +func (gc *StateQuerySet) List(ctx context.Context, db Transactor, reqMsg, resMsg j5reflect.Object) error { return gc.MainLister.List(ctx, db, reqMsg, resMsg) } -func (gc *StateQuerySet[ - GetREQ, GetRES, - ListREQ, ListRES, - ListEventsREQ, ListEventsRES, -]) ListEvents(ctx context.Context, db Transactor, reqMsg *j5reflect.Object, resMsg *j5reflect.Object) error { +func (gc *StateQuerySet) ListEvents(ctx context.Context, db Transactor, reqMsg, resMsg j5reflect.Object) error { return gc.EventLister.List(ctx, db, reqMsg, resMsg) } @@ -104,57 +93,30 @@ func (f TenantFilterProviderFunc) GetRequiredTenantKeys(ctx context.Context) (ma return f(ctx) } -func BuildStateQuerySet[ - GetREQ pquery.GetRequest, - GetRES pquery.GetResponse, - ListREQ pquery.ListRequest, - ListRES pquery.ListResponse, - ListEventsREQ pquery.ListRequest, - ListEventsRES pquery.ListResponse, -]( - smSpec QuerySpec[GetREQ, GetRES, ListREQ, ListRES, ListEventsREQ, ListEventsRES], +func BuildStateQuerySet( + smSpec QuerySpec, options StateQueryOptions, -) (*StateQuerySet[GetREQ, GetRES, ListREQ, ListRES, ListEventsREQ, ListEventsRES], error) { - - eventJoinMap := pquery.JoinFields{} - requestReflect := (*new(GetREQ)).ProtoReflect().Descriptor() +) (*StateQuerySet, error) { - unmappedRequestFields := map[protoreflect.Name]protoreflect.FieldDescriptor{} - reqFields := requestReflect.Fields() - for i := range reqFields.Len() { - field := requestReflect.Fields().Get(i) - unmappedRequestFields[field.Name()] = field + if err := smSpec.Validate(); err != nil { + return nil, fmt.Errorf("validate state machine spec: %w", err) } - pkFields := map[string]func(req protoreflect.Message) any{} //protoreflect.FieldDescriptor{} - for _, keyColumn := range smSpec.KeyColumns { - matchingRequestField, ok := unmappedRequestFields[keyColumn.ProtoName] - if ok { - delete(unmappedRequestFields, keyColumn.ProtoName) - var callback func(req protoreflect.Message) any - switch keyColumn.Schema.Type.(type) { - case *schema_j5pb.Field_String_, *schema_j5pb.Field_Key: - callback = func(req protoreflect.Message) any { - return req.Get(matchingRequestField).String() - } - case *schema_j5pb.Field_Date: - callback = func(req protoreflect.Message) any { - return req.Get(matchingRequestField).Message().Interface() - } - default: - return nil, fmt.Errorf("unsupported key type '%T' for column '%s'", keyColumn.Schema.Type, keyColumn.ColumnName) - } + requestReflect := smSpec.GetMethod.Request - pkFields[keyColumn.ColumnName] = callback - } + unmappedRequestFields := map[string]*j5schema.ObjectProperty{} + for _, field := range requestReflect.Properties { + unmappedRequestFields[field.JSONName] = field + } - if keyColumn.Primary { - eventJoinMap = append(eventJoinMap, pquery.JoinField{ - RootColumn: keyColumn.ColumnName, - JoinColumn: keyColumn.ColumnName, - }) + getPkFields := []KeyColumn{} + for _, keyColumn := range smSpec.KeyColumns { + _, ok := unmappedRequestFields[keyColumn.JSONFieldName] + if !ok { + continue } - + delete(unmappedRequestFields, keyColumn.JSONFieldName) + getPkFields = append(getPkFields, keyColumn) } if len(unmappedRequestFields) > 0 { @@ -165,7 +127,8 @@ func BuildStateQuerySet[ return nil, fmt.Errorf("unmapped fields in Get request: %v", fieldNames) } - getSpec := pquery.GetSpec[GetREQ, GetRES]{ + getSpec := pquery.GetSpec{ + Method: smSpec.GetMethod, TableName: smSpec.State.TableName, DataColumn: smSpec.State.Root.ColumnName, } @@ -178,33 +141,77 @@ func BuildStateQuerySet[ getSpec.Auth = options.Auth } - getSpec.PrimaryKey = func(req GetREQ) (map[string]any, error) { - refl := req.ProtoReflect() + getSpec.PrimaryKey = func(req j5reflect.Object) (map[string]any, error) { out := map[string]any{} - for k, v := range pkFields { - out[k] = v(refl) + for _, col := range getPkFields { + value, ok, err := req.GetField(col.JSONFieldName) + if err != nil { + return nil, fmt.Errorf("get primary key field '%s': %w", col.JSONFieldName, err) + } + if !ok { + return nil, fmt.Errorf("missing primary key field '%s'", col.JSONFieldName) + } + scalarValue, ok := value.AsScalar() + if !ok { + return nil, fmt.Errorf("primary key field '%s' is not a scalar value", col.JSONFieldName) + } + out[col.ColumnName], err = scalarValue.ToGoValue() + if err != nil { + return nil, fmt.Errorf("convert primary key field '%s' to Go value: %w", col.JSONFieldName, err) + } } return out, nil } - var eventsInGet protoreflect.Name + var eventsInGet *j5schema.ObjectProperty - getResponseReflect := (*new(GetRES)).ProtoReflect().Descriptor() - for i := range getResponseReflect.Fields().Len() { - field := getResponseReflect.Fields().Get(i) - msg := field.Message() - if msg == nil { - continue + getResponseReflect := smSpec.GetMethod.Response + for _, field := range getResponseReflect.Properties { + switch ft := field.Schema.(type) { + case *j5schema.ObjectField: + name := ft.ObjectSchema().FullName() + if name == smSpec.StateType.FullName() { + if getSpec.StateResponseField != "" { + return nil, fmt.Errorf("multiple state fields in Get response: %s and %s", getSpec.StateResponseField, field.FullName()) + } + getSpec.StateResponseField = field.JSONName + } else { + return nil, fmt.Errorf("unexpected object field in Get response: %s", name) + } + + case *j5schema.ArrayField: + itemSchema, ok := ft.ItemSchema.(*j5schema.ObjectField) + if !ok { + return nil, fmt.Errorf("expected array field in Get response, got: %T", ft.ItemSchema) + } + + name := itemSchema.ObjectSchema().FullName() + if name == smSpec.EventType.FullName() { + if eventsInGet != nil { + return nil, fmt.Errorf("multiple event fields in Get response: %s and %s", eventsInGet.FullName(), field.FullName()) + } + eventsInGet = field + } else { + return nil, fmt.Errorf("unexpected array field in Get response: %s", name) + } + + default: + return nil, fmt.Errorf("unexpected field in Get response: %s", field.FullName()) } + } - if msg.FullName() == smSpec.EventType.FullName() { - eventsInGet = field.Name() - } else if msg.FullName() == smSpec.StateType.FullName() { - getSpec.StateResponseField = field.Name() + eventJoinMap := pquery.JoinFields{} + for _, keyColumn := range smSpec.KeyColumns { + if keyColumn.Primary { + eventJoinMap = append(eventJoinMap, pquery.JoinField{ + RootColumn: keyColumn.ColumnName, + JoinColumn: keyColumn.ColumnName, + }) } + } - if eventsInGet != "" { + if eventsInGet != nil { if smSpec.Event.TableName == "" { return nil, fmt.Errorf("missing EventTable in state spec for %s", smSpec.State.TableName) } @@ -214,7 +221,7 @@ func BuildStateQuerySet[ getSpec.Join = &pquery.GetJoinSpec{ TableName: smSpec.Event.TableName, DataColumn: smSpec.Event.Root.ColumnName, - FieldInParent: eventsInGet, + FieldInParent: eventsInGet.JSONName, On: eventJoinMap, } } @@ -230,13 +237,14 @@ func BuildStateQuerySet[ if !field.Primary { continue } - keyPath := fmt.Sprintf("keys.%s", field.ProtoName) + keyPath := field.JSONFieldName statePrimaryKeys = append(statePrimaryKeys, - pquery.NewProtoField(keyPath, &field.ColumnName), + pquery.NewJSONField(keyPath, &field.ColumnName), ) } - listSpec := pquery.ListSpec[ListREQ, ListRES]{ + listSpec := pquery.ListSpec{ + Method: smSpec.ListMethod, TableSpec: pquery.TableSpec{ TableName: smSpec.State.TableName, DataColumn: smSpec.State.Root.ColumnName, @@ -252,7 +260,7 @@ func BuildStateQuerySet[ return nil, fmt.Errorf("build main lister for state query '%s': %w", smSpec.State.TableName, err) } - querySet := &StateQuerySet[GetREQ, GetRES, ListREQ, ListRES, ListEventsREQ, ListEventsRES]{ + querySet := &StateQuerySet{ Getter: getter, MainLister: lister, } @@ -261,14 +269,15 @@ func BuildStateQuerySet[ return querySet, nil } - eventListSpec := pquery.ListSpec[ListEventsREQ, ListEventsRES]{ + eventListSpec := pquery.ListSpec{ + Method: smSpec.ListEventsMethod, TableSpec: pquery.TableSpec{ TableName: smSpec.Event.TableName, DataColumn: smSpec.Event.Root.ColumnName, Auth: getSpec.Auth, AuthJoin: getSpec.AuthJoin, FallbackSortColumns: []pquery.ProtoField{ - pquery.NewProtoField("metadata.event_id", &smSpec.Event.ID.ColumnName), + pquery.NewJSONField("metadata.eventId", &smSpec.Event.ID.ColumnName), }, }, RequestFilter: smSpec.ListEventsRequestFilter, diff --git a/psm/statemachine.go b/psm/statemachine.go index 8e1acda..c154814 100644 --- a/psm/statemachine.go +++ b/psm/statemachine.go @@ -5,9 +5,13 @@ import ( "database/sql" "errors" "fmt" - "github.com/pentops/j5/j5types/date_j5t" "time" + "github.com/pentops/j5/j5types/date_j5t" + "github.com/pentops/j5/lib/j5reflect" + "github.com/pentops/j5/lib/j5schema" + "github.com/pentops/j5/lib/j5validate" + "buf.build/go/protovalidate" sq "github.com/elgris/sqrl" "github.com/google/uuid" @@ -17,8 +21,7 @@ import ( "github.com/pentops/sqrlx.go/sqrlx" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "google.golang.org/protobuf/encoding/protojson" - "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -29,11 +32,6 @@ type Transactor interface { Transact(context.Context, *sqrlx.TxOptions, sqrlx.Callback) error } -// DEPRECATED: This does nothing. -func MustSystemActor(id string) struct{} { - return struct{}{} -} - // StateMachine is a database wrapper around the eventer. Using sane defaults // with overrides for table configuration. type StateMachine[ @@ -52,7 +50,11 @@ type StateMachine[ tableMap *TableMap - validator protovalidate.Validator + stateSchema *j5schema.ObjectSchema + eventSchema *j5schema.ObjectSchema + + validator *j5validate.Validator + protoValidator protovalidate.Validator } func NewStateMachine[ @@ -65,12 +67,14 @@ func NewStateMachine[ ]( cb *StateMachineConfig[K, S, ST, SD, E, IE], ) (*StateMachine[K, S, ST, SD, E, IE], error) { + stateSchema, eventSchema, err := cb.schemas() + if err != nil { + return nil, fmt.Errorf("schemas: %w", err) + } if cb.tableMap == nil { - tableMap, err := tableMapFromStateAndEvent( - (*new(S)).ProtoReflect().Descriptor(), - (*new(E)).ProtoReflect().Descriptor(), - ) + + tableMap, err := tableMapFromStateAndEvent(stateSchema, eventSchema) if err != nil { return nil, err } @@ -81,19 +85,27 @@ func NewStateMachine[ return nil, err } + pv, err := protovalidate.New() + if err != nil { + return nil, fmt.Errorf("failed to initialize protovalidate: %w", err) + } + return &StateMachine[K, S, ST, SD, E, IE]{ keyValueFunc: cb.keyValues, initialStateFunc: cb.initialStateFunc, tableMap: cb.tableMap, - //SystemActor: cb.systemActor, + stateSchema: stateSchema, + eventSchema: eventSchema, + validator: j5validate.NewValidator(), + protoValidator: pv, }, nil } func (sm StateMachine[K, S, ST, SD, E, IE]) StateTableSpec() QueryTableSpec { return QueryTableSpec{ TableMap: *sm.tableMap, - EventType: (*new(E)).ProtoReflect().Descriptor(), - StateType: (*new(S)).ProtoReflect().Descriptor(), + EventType: sm.eventSchema, + StateType: sm.stateSchema, } } @@ -207,7 +219,8 @@ func (sm *DBStateMachine[K, S, ST, SD, E, IE]) FollowEvents(ctx context.Context, } func (sm *StateMachine[K, S, ST, SD, E, IE]) getCurrentState(ctx context.Context, tx sqrlx.Transaction, keys K) (S, error) { - state := (*new(S)).ProtoReflect().New().Interface().(S) + state := newJ5Message[S]() + stateRefl := state.J5Reflect() selectQuery := sq. Select(sm.tableMap.State.Root.ColumnName). @@ -227,7 +240,7 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) getCurrentState(ctx context.Context var stateJSON []byte err = tx.SelectRow(ctx, selectQuery).Scan(&stateJSON) if errors.Is(err, sql.ErrNoRows) { - state.SetPSMKeys(proto.Clone(keys).(K)) + state.SetPSMKeys(keys.Clone().(K)) if len(allKeys.missingRequired) > 0 { return state, fmt.Errorf("missing required key(s) %v in initial event", allKeys.missingRequired) @@ -241,7 +254,7 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) getCurrentState(ctx context.Context return state, fmt.Errorf("selecting current state (%s): %w", qq, err) } - if err := protojson.Unmarshal(stateJSON, state); err != nil { + if err := dbconvert.UnmarshalJ5(stateJSON, stateRefl); err != nil { return state, err } @@ -319,12 +332,12 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) store( event E, ) error { - stateDBValue, err := dbconvert.MarshalProto(state) + stateDBValue, err := dbconvert.MarshalJ5(state.J5Reflect()) if err != nil { return fmt.Errorf("state field: %w", err) } - eventDBValue, err := dbconvert.MarshalProto(event) + eventDBValue, err := dbconvert.MarshalJ5(event.J5Reflect()) if err != nil { return fmt.Errorf("event field: %w", err) } @@ -425,12 +438,12 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) followEventDeduplicate(ctx context. return false, fmt.Errorf("selecting event for deduplication: %w", err) } - existing := (*new(E)).ProtoReflect().New().Interface().(E) - if err := protojson.Unmarshal(eventData, existing); err != nil { + existing := newJ5Message[E]() + if err := dbconvert.UnmarshalJ5(eventData, existing.J5Reflect()); err != nil { return true, fmt.Errorf("unmarshalling event: %w", err) } - if !proto.Equal(existing, event) { + if !j5reflect.DeepEqual(existing.J5Reflect(), event.J5Reflect()) { return true, fmt.Errorf("event %s already exists with different data", existing.PSMMetadata().EventId) } @@ -442,7 +455,7 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) followEvents(ctx context.Context, t for _, event := range events { if err := sm.validateEvent(event); err != nil { - return fmt.Errorf("validating event %s: %w", event.ProtoReflect().Descriptor().FullName(), err) + return fmt.Errorf("validating event: %w", err) } exists, err := sm.followEventDeduplicate(ctx, tx, event) @@ -470,7 +483,7 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) followEvents(ctx context.Context, t err = sm.followEvent(ctx, tx, state, event) if err != nil { - return fmt.Errorf("run event %s (%s): %w", event.PSMMetadata().EventId, event.UnwrapPSMEvent().PSMEventKey(), err) + return fmt.Errorf("follow event %s (%s): %w", event.PSMMetadata().EventId, event.UnwrapPSMEvent().PSMEventKey(), err) } } @@ -508,7 +521,7 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) runInitialEvent(ctx context.Context // RunEvent modifies state in place returnState, err := sm.runEvent(ctx, tx, state, prepared, captureFinalState) if err != nil { - return state, fmt.Errorf("input event %s: %w", eventSpec.Event.PSMEventKey(), err) + return state, fmt.Errorf("run event %s: %w", eventSpec.Event.PSMEventKey(), err) } return *returnState, nil @@ -549,7 +562,7 @@ func assertPresentKeysMatch[K IKeyset](existing, event K) error { func (sm *StateMachine[K, S, ST, SD, E, IE]) runTx(ctx context.Context, tx sqrlx.Transaction, outerEvent *EventSpec[K, S, ST, SD, E, IE]) (S, error) { if err := outerEvent.validateAndPrepare(); err != nil { - return *new(S), fmt.Errorf("event %s: %w", outerEvent.Event.ProtoReflect().Descriptor().FullName(), err) + return *new(S), err } if outerEvent.EventID == "" { @@ -595,9 +608,8 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) runTx(ctx context.Context, tx sqrlx // RunEvent modifies state in place returnState, err := sm.runEvent(ctx, tx, state, prepared, captureInitialState) // return the state after the first transition - if err != nil { - return state, fmt.Errorf("input event %s: %w", outerEvent.Event.PSMEventKey(), err) + return state, fmt.Errorf("run event %s: %w", outerEvent.Event.PSMEventKey(), err) } return *returnState, nil @@ -622,22 +634,25 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) firstEventUniqueCheck(ctx context.C return s, false, fmt.Errorf("selecting event: %w", err) } - existing := (*new(E)).ProtoReflect().New().Interface().(E) + existing := newJ5Message[E]() - if err := protojson.Unmarshal(eventData, existing); err != nil { + if err := dbconvert.UnmarshalJ5(eventData, existing.J5Reflect()); err != nil { return s, false, fmt.Errorf("unmarshalling event: %w", err) } - if !proto.Equal(existing.UnwrapPSMEvent(), data) { + existingContent := existing.UnwrapPSMEvent() + + if !j5reflect.DeepEqual(existingContent.J5Reflect(), data.J5Reflect()) { + log.WithFields(ctx, "existing", prototext.Format(existingContent), "new", prototext.Format(data)).Info("event data does not match existing event data") return s, false, ErrDuplicateEventID } - state := (*new(S)).ProtoReflect().New() - if err := protojson.Unmarshal(stateData, state.Interface()); err != nil { + state := newJ5Message[S]() + if err := dbconvert.UnmarshalJ5(stateData, state.J5Reflect()); err != nil { return s, false, fmt.Errorf("unmarshalling state: %w", err) } - return state.Interface().(S), true, nil + return state, true, nil } func (sm *StateMachine[K, S, ST, SD, E, IE]) eventsMustBeUnique(ctx context.Context, tx sqrlx.Transaction, events ...*EventSpec[K, S, ST, SD, E, IE]) error { @@ -663,19 +678,23 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) eventsMustBeUnique(ctx context.Cont func (sm *StateMachine[K, S, ST, SD, E, IE]) validateEvent(event E) error { if sm.validator == nil { - v, err := protovalidate.New() - if err != nil { - fmt.Println("failed to initialize validator:", err) - } + v := j5validate.NewValidator() sm.validator = v } - return sm.validator.Validate(event) + err := sm.validator.Validate(event.J5Reflect()) + if err != nil { + txt := prototext.Format(event) + fmt.Printf("Event validation failed: %s\n%s\n", err.Error(), txt) + + return fmt.Errorf("validate event: %w", err) + } + return nil } func (sm *StateMachine[K, S, ST, SD, E, IE]) prepareEvent(state S, spec *EventSpec[K, S, ST, SD, E, IE]) (built E, err error) { - built = (*new(E)).ProtoReflect().New().Interface().(E) + built = newJ5Message[E]() if err := built.SetPSMEvent(spec.Event); err != nil { return built, fmt.Errorf("set event: %w", err) } diff --git a/psm/table_map.go b/psm/table_map.go index b12f3ba..ea7b9ab 100644 --- a/psm/table_map.go +++ b/psm/table_map.go @@ -6,12 +6,9 @@ import ( "unicode" "github.com/iancoleman/strcase" - "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/protostate/internal/pgstore" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" + "github.com/pentops/protostate/pquery" ) type TableMap struct { @@ -82,14 +79,17 @@ type EventTableSpec struct { type StateTableSpec struct { TableName string + KeyField *j5schema.ObjectProperty + // The entire state message, as a JSONB Root *FieldSpec } type KeyColumn struct { ColumnName string - ProtoField protoreflect.FieldNumber - ProtoName protoreflect.Name + //ProtoField protoreflect.FieldNumber + //ProtoName protoreflect.Name + JSONFieldName string Primary bool Required bool @@ -105,7 +105,7 @@ type KeyField struct { ColumnName *string // Optional, stores in the table as a column. Primary bool Unique bool - Path *pgstore.Path + Path *pquery.Path } func safeTableName(name string) string { @@ -130,24 +130,23 @@ func BuildQueryTableSpec(stateMessage, eventMessage *j5schema.ObjectSchema) (Que }, nil } -var globalSchemaCache = j5schema.NewSchemaCache() - -func buildDefaultTableMap(keyMessage protoreflect.MessageDescriptor) (*TableMap, error) { - stateObjectAnnotation, ok := proto.GetExtension(keyMessage.Options(), ext_j5pb.E_Psm).(*ext_j5pb.PSMOptions) - if !ok || stateObjectAnnotation == nil { - return nil, fmt.Errorf("message %s has no PSM Key field", keyMessage.Name()) +func buildDefaultTableMap(stateKeyField *j5schema.ObjectProperty, keyMessage *j5schema.ObjectSchema) (*TableMap, error) { + if keyMessage.Entity == nil { + return nil, fmt.Errorf("key message %s has no PSM Entity annotation", keyMessage.Name()) } + entity := keyMessage.Entity tm := &TableMap{ State: StateTableSpec{ - TableName: safeTableName(stateObjectAnnotation.EntityName), + TableName: safeTableName(entity.Entity), + KeyField: stateKeyField, Root: &FieldSpec{ ColumnName: "state", //PathFromRoot: psm.PathSpec{}, }, }, Event: EventTableSpec{ - TableName: safeTableName(stateObjectAnnotation.EntityName + "_event"), + TableName: safeTableName(entity.Entity + "_event"), Root: &FieldSpec{ ColumnName: "data", //PathFromRoot: psm.PathSpec{}, @@ -170,22 +169,9 @@ func buildDefaultTableMap(keyMessage protoreflect.MessageDescriptor) (*TableMap, }, } - schema, err := globalSchemaCache.Schema(keyMessage) - if err != nil { - return nil, nil - } - objSchema, ok := schema.(*j5schema.ObjectSchema) - if !ok { - return nil, fmt.Errorf("expected object schema, got %T", schema) - } - keyFields := keyMessage.Fields() - for _, field := range objSchema.Properties { - if len(field.ProtoField) != 1 { - return nil, fmt.Errorf("key field %s: expected one proto field, got %d", field.FullName(), len(field.ProtoField)) - } - desc := keyFields.ByNumber(field.ProtoField[0]) + for _, field := range keyMessage.Properties { - keyColumn, err := keyFieldColumn(field, desc) + keyColumn, err := keyFieldColumn(field) if err != nil { return nil, fmt.Errorf("field %s: %w", field.FullName(), err) } @@ -196,14 +182,15 @@ func buildDefaultTableMap(keyMessage protoreflect.MessageDescriptor) (*TableMap, return tm, nil } -func keyFieldColumn(field *j5schema.ObjectProperty, desc protoreflect.FieldDescriptor) (*KeyColumn, error) { +func keyFieldColumn(field *j5schema.ObjectProperty) (*KeyColumn, error) { isPrimary := field.Entity != nil && field.Entity.Primary kc := &KeyColumn{ - ColumnName: strcase.ToSnake(field.JSONName), - ProtoField: desc.Number(), - ProtoName: desc.Name(), + ColumnName: strcase.ToSnake(field.JSONName), + JSONFieldName: field.JSONName, + //ProtoField: desc.Number(), + //ProtoName: desc.Name(), Primary: isPrimary, Required: isPrimary || field.Required, ExplicitlyOptional: field.ExplicitlyOptional, @@ -215,53 +202,63 @@ func keyFieldColumn(field *j5schema.ObjectProperty, desc protoreflect.FieldDescr func tableMapFromStateAndEvent(stateMessage, eventMessage *j5schema.ObjectSchema) (*TableMap, error) { - var stateKeyField protoreflect.FieldDescriptor - var keyMessage protoreflect.MessageDescriptor + var stateKeyField *j5schema.ObjectProperty + var keyMessage *j5schema.ObjectSchema for _, field := range stateMessage.Properties { - if field.Kind() != protoreflect.MessageKind { + obj, ok := field.Schema.(*j5schema.ObjectField) + if !ok { + continue + } + objSchema := obj.ObjectSchema() + if objSchema.Entity == nil { continue } - msg := field.Message() - stateObjectAnnotation, ok := proto.GetExtension(msg.Options(), ext_j5pb.E_Psm).(*ext_j5pb.PSMOptions) - if ok && stateObjectAnnotation != nil { - keyMessage = msg - stateKeyField = field - break + if objSchema.Entity.Part != schema_j5pb.EntityPart_KEYS { + continue } + keyMessage = objSchema + stateKeyField = field + break } if stateKeyField == nil { return nil, fmt.Errorf("state message %s has no PSM Keys", stateMessage.FullName()) } - var eventKeysField protoreflect.FieldDescriptor + var eventKeyField *j5schema.ObjectProperty + var eventKeyMessage *j5schema.ObjectSchema for _, field := range eventMessage.Properties { - if field.Kind() != protoreflect.MessageKind { + + obj, ok := field.Schema.(*j5schema.ObjectField) + if !ok { + continue + } + objSchema := obj.ObjectSchema() + if objSchema.Entity == nil { continue } - msg := field.Message() - - stateObjectAnnotation, ok := proto.GetExtension(msg.Options(), ext_j5pb.E_Psm).(*ext_j5pb.PSMOptions) - if ok && stateObjectAnnotation != nil { - if keyMessage.FullName() != msg.FullName() { - return nil, fmt.Errorf("%s.%s is a %s, but %s.%s is a %s, these should be the same", - stateMessage.FullName(), - stateKeyField.Name(), - keyMessage.FullName(), - eventMessage.FullName(), - field.Name(), - msg.FullName(), - ) - } - eventKeysField = field + if objSchema.Entity.Part != schema_j5pb.EntityPart_KEYS { continue } + eventKeyMessage = objSchema + eventKeyField = field + } - if eventKeysField == nil { + if eventKeyField == nil { return nil, fmt.Errorf("event message %s has no PSM Keys", eventMessage.FullName()) } - return buildDefaultTableMap(keyMessage) + + if eventKeyMessage.FullName() != keyMessage.FullName() { + return nil, fmt.Errorf("state message %s has keys %s, but event message %s has keys %s", + stateMessage.FullName(), + stateKeyField.FullName(), + eventMessage.FullName(), + eventKeyField.FullName(), + ) + } + + return buildDefaultTableMap(stateKeyField, keyMessage) } From d242bc5f86b5cac8d45167a07e650287b54ab119 Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Mon, 28 Jul 2025 10:18:22 -0700 Subject: [PATCH 05/12] Remote Version --- go.mod | 10 ++++------ go.sum | 2 ++ j5.yaml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go.mod b/go.mod index 6214a5a..57e23fc 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/pentops/protostate go 1.24.0 require ( + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250603165357-b52ab10f4468.1 buf.build/go/protovalidate v0.12.0 buf.build/go/protoyaml v0.6.0 github.com/elgris/sqrl v0.0.0-20210727210741-7e0198b30236 @@ -11,12 +12,14 @@ require ( github.com/lib/pq v1.10.9 github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21 github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 - github.com/pentops/j5 v0.0.0-20250625220400-ddb847893140 + github.com/pentops/j5 v0.0.0-20250728171520-2c0bbfa073d2 github.com/pentops/log.go v0.0.16 github.com/pentops/o5-messaging v0.0.0-20250619024104-7e07c29129f0 github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b github.com/pentops/sqrlx.go v0.0.0-20250520210217-2f46de329c7a + github.com/shopspring/decimal v1.4.0 github.com/stretchr/testify v1.10.0 + google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 google.golang.org/grpc v1.72.2 google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 @@ -24,7 +27,6 @@ require ( ) require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.6-20250603165357-b52ab10f4468.1 // indirect cel.dev/expr v0.24.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/bufbuild/protocompile v0.14.1 // indirect @@ -40,15 +42,11 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pressly/goose v2.7.0+incompatible // indirect - github.com/shopspring/decimal v1.4.0 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b // indirect golang.org/x/net v0.40.0 // indirect golang.org/x/sync v0.14.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.25.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect ) - -replace github.com/pentops/j5 => /Users/daemonl/pentops/j5 diff --git a/go.sum b/go.sum index ff801f4..30f71cb 100644 --- a/go.sum +++ b/go.sum @@ -77,6 +77,8 @@ github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21 h1:EIMCYtccNxV59a github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21/go.mod h1:vNp8crAKcH0f/sZU9frkmQLUeDsTIgMqV14kQtkAqC0= github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 h1:s5qtWT2/s79gy/wm3/bwvKYLK6u2AkW05JiLPqxraP0= github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63/go.mod h1:I58JIVvL1/nP4CEHGKGbBhvWIEA9mVkGeoviemaqanU= +github.com/pentops/j5 v0.0.0-20250728171520-2c0bbfa073d2 h1:au4+nCshAOXPbl/D6bO2qKE8fGCpWcLCTlU9XXay3gM= +github.com/pentops/j5 v0.0.0-20250728171520-2c0bbfa073d2/go.mod h1:i+eCdoCoDvULbEuPeoQm4q1YLw13oaJIlYMB+kaX6fM= github.com/pentops/log.go v0.0.16 h1:oxCuHSBOBPjfUVSXyOSEEdYUwytysj4T29/7T2FBp9Q= github.com/pentops/log.go v0.0.16/go.mod h1:yR34x8aMlvhdGvqgIU4+0MiLjJTKt0vpcgUnVN2nZV4= github.com/pentops/o5-messaging v0.0.0-20250619024104-7e07c29129f0 h1:aAt5rIhzFyF1RdT262LGHL0V4vBxcGCTWIeC1C0vZ20= diff --git a/j5.yaml b/j5.yaml index 7417887..3445177 100644 --- a/j5.yaml +++ b/j5.yaml @@ -51,7 +51,7 @@ plugins: - base: go name: go-o5-messaging docker: - image: ghcr.io/pentops/protoc-gen-go-o5-messaging:fba07334e9aa1affc26b34eae82254a36f955267 + image: ghcr.io/pentops/protoc-gen-go-o5-messaging:7e07c29129f03edc9ef01ba4739328625ef24746 - base: go name: go-j5 From cfb0e8ace0971399ab5c4406edea63ab8cdfd02d Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Tue, 29 Jul 2025 13:14:31 -0700 Subject: [PATCH 06/12] Convert filter tests to direct lister --- go.mod | 2 +- go.sum | 4 +- .../integration/query_test/filter_test.go | 641 ++++++++++-------- internal/integration/query_test/helpers.go | 39 -- .../query_test/marshalling_test.go | 273 -------- .../integration/query_test/psm_universe.go | 106 +++ internal/integration/query_test/setup.go | 13 - internal/integration/query_test/sort_test.go | 1 + internal/integration/query_test/universe.go | 196 ++++-- .../gen/test/v1/test_pb/foo.j5s.pb.go | 601 +++++++++++----- .../gen/test/v1/test_pb/foo.j5s_j5.pb.go | 29 + .../gen/test/v1/test_pb/foo.j5s_sugar.pb.go | 2 + internal/testproto/test/v1/foo.j5s | 16 + pquery/lister.go | 26 +- pquery/path.go | 4 + 15 files changed, 1104 insertions(+), 849 deletions(-) delete mode 100644 internal/integration/query_test/marshalling_test.go create mode 100644 internal/integration/query_test/psm_universe.go diff --git a/go.mod b/go.mod index 57e23fc..b8abf7e 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/lib/pq v1.10.9 github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21 github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 - github.com/pentops/j5 v0.0.0-20250728171520-2c0bbfa073d2 + github.com/pentops/j5 v0.0.0-20250729201549-91d43d72f0bc github.com/pentops/log.go v0.0.16 github.com/pentops/o5-messaging v0.0.0-20250619024104-7e07c29129f0 github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b diff --git a/go.sum b/go.sum index 30f71cb..80eeb49 100644 --- a/go.sum +++ b/go.sum @@ -77,8 +77,8 @@ github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21 h1:EIMCYtccNxV59a github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21/go.mod h1:vNp8crAKcH0f/sZU9frkmQLUeDsTIgMqV14kQtkAqC0= github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 h1:s5qtWT2/s79gy/wm3/bwvKYLK6u2AkW05JiLPqxraP0= github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63/go.mod h1:I58JIVvL1/nP4CEHGKGbBhvWIEA9mVkGeoviemaqanU= -github.com/pentops/j5 v0.0.0-20250728171520-2c0bbfa073d2 h1:au4+nCshAOXPbl/D6bO2qKE8fGCpWcLCTlU9XXay3gM= -github.com/pentops/j5 v0.0.0-20250728171520-2c0bbfa073d2/go.mod h1:i+eCdoCoDvULbEuPeoQm4q1YLw13oaJIlYMB+kaX6fM= +github.com/pentops/j5 v0.0.0-20250729201549-91d43d72f0bc h1:ERDIagXUpaz9A7vaNU12U6ErIz9la9lqEUUYX0Uer04= +github.com/pentops/j5 v0.0.0-20250729201549-91d43d72f0bc/go.mod h1:i+eCdoCoDvULbEuPeoQm4q1YLw13oaJIlYMB+kaX6fM= github.com/pentops/log.go v0.0.16 h1:oxCuHSBOBPjfUVSXyOSEEdYUwytysj4T29/7T2FBp9Q= github.com/pentops/log.go v0.0.16/go.mod h1:yR34x8aMlvhdGvqgIU4+0MiLjJTKt0vpcgUnVN2nZV4= github.com/pentops/o5-messaging v0.0.0-20250619024104-7e07c29129f0 h1:aAt5rIhzFyF1RdT262LGHL0V4vBxcGCTWIeC1C0vZ20= diff --git a/internal/integration/query_test/filter_test.go b/internal/integration/query_test/filter_test.go index 721c52b..c5895c1 100644 --- a/internal/integration/query_test/filter_test.go +++ b/internal/integration/query_test/filter_test.go @@ -3,6 +3,7 @@ package integration import ( "context" "encoding/base64" + "fmt" "testing" "time" @@ -12,29 +13,25 @@ import ( "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" + "github.com/pentops/protostate/pquery" "google.golang.org/protobuf/proto" ) func TestDefaultFiltering(t *testing.T) { - ss, uu := NewFooUniverse(t) - sm := uu.SM - db := uu.DB - queryer := uu.Query - var err error + uu := NewSchemaUniverse(t) + ss := NewStepper(t) defer ss.RunSteps(t) - tenants := []string{uuid.NewString()} - tenantIDs := setupFooListableData(ss, sm, tenants, 10) + var queryer *pquery.Lister - ss.Step("Setup Extra Statuses", func(ctx context.Context, t flowtest.Asserter) { - for _, id := range tenantIDs[tenants[0]][:2] { - event := newFooDeletedEvent(id, tenants[0]) - - _, err := sm.Transition(ctx, event) - if err != nil { - t.Fatal(err.Error()) + ss.Setup(func(ctx context.Context, t flowtest.Asserter) error { + uu.SetupFoo(t, 10, func(ii int, foo *TestObject) { + if ii < 2 { + foo.SetScalar(pquery.JSONPath("status"), "DELETED") } - } + }) + queryer = uu.FooLister(t) + return nil }) ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { @@ -45,7 +42,7 @@ func TestDefaultFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err := queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -71,18 +68,41 @@ func TestDefaultFiltering(t *testing.T) { } func TestFilteringWithAuthScope(t *testing.T) { - ss, uu := NewFooUniverse(t) - sm := uu.SM - db := uu.DB - queryer := uu.Query - var err error + uu := NewSchemaUniverse(t) + ss := NewStepper(t) defer ss.RunSteps(t) + var queryer *pquery.Lister + tenantID1 := uuid.NewString() tenantID2 := uuid.NewString() tenants := []string{tenantID1, tenantID2} - setupFooListableData(ss, sm, tenants, 10) + + ss.Setup(func(ctx context.Context, t flowtest.Asserter) error { + uu.SetupFoo(t, 20, func(idx int, foo *TestObject) { + // recreating the old function logic + tenantId := idx % len(tenants) + tenantID := tenants[tenantId] + ii := idx / len(tenants) + ti := tenantId + + foo.SetScalar(pquery.JSONPath("tenantId"), tenantID) + + weight := ((10 + int64(ii)) * (int64(ti) + 1)) + height := ((50 - int64(ii)) * (int64(ti) + 1)) + length := (int64(ii%2) * (int64(ti) + 1)) + + foo.SetScalar(pquery.JSONPath("data", "characteristics", "weight"), weight) + foo.SetScalar(pquery.JSONPath("data", "characteristics", "height"), height) + foo.SetScalar(pquery.JSONPath("data", "characteristics", "length"), length) + createdAt := time.Now() + foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt.Format(time.RFC3339Nano)) + + }) + queryer = uu.FooLister(t) + return nil + }) tkn := &token{ claim: &auth_j5pb.Claim{ @@ -118,9 +138,9 @@ func TestFilteringWithAuthScope(t *testing.T) { }, }, } - res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + res := &test_spb.FooListResponse{} + err := queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -135,10 +155,10 @@ func TestFilteringWithAuthScope(t *testing.T) { for ii, state := range res.Foo { if *state.Keys.TenantId != tenantID1 { - t.Fatalf("expected tenant ID %s, got %s", tenantID1, state.Keys.TenantId) + t.Errorf("expected tenant ID %s, got %s", tenantID1, state.Keys.TenantId) } if state.Data.Characteristics.Weight != int64(15-ii) { - t.Fatalf("expected weight %d, got %d", 15-ii, state.Data.Characteristics.Weight) + t.Errorf("expected weight %d, got %d", 15-ii, state.Data.Characteristics.Weight) } } @@ -146,38 +166,85 @@ func TestFilteringWithAuthScope(t *testing.T) { if res.Page != nil { t.Fatalf("page response should be empty") } + }) } func TestDynamicFiltering(t *testing.T) { - ss, uu := NewFooUniverse(t) - sm := uu.SM - db := uu.DB - queryer := uu.Query - var err error - defer ss.RunSteps(t) + uu := NewSchemaUniverse(t) + var queryer *pquery.Lister + + tenantID := uuid.NewString() + uu.SetupFoo(t, 60, func(ii int, foo *TestObject) { + weight := (10 + int64(ii)) + height := (50 - int64(ii)) + length := (int64(ii % 2)) + + foo.SetScalar(pquery.JSONPath("tenantId"), tenantID) + foo.SetScalar(pquery.JSONPath("data", "characteristics", "weight"), weight) + foo.SetScalar(pquery.JSONPath("data", "characteristics", "height"), height) + foo.SetScalar(pquery.JSONPath("data", "characteristics", "length"), length) + createdAt := time.Now() + foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt.Format(time.RFC3339Nano)) + + label := fmt.Sprintf("foo %d at %s (weighted %d, height %d, length %d)", ii, createdAt.Format(time.RFC3339Nano), weight, height, length) + foo.SetScalar(pquery.JSONPath("data", "field"), label) + + val, err := foo.GetOrCreateValue("data", "profiles") + if err != nil { + t.Fatalf("failed to get or create profiles: %v", err) + } + + objArray, ok := val.AsArrayOfObject() + if !ok { + t.Fatalf("expected profiles to be an array of objects, got %T", val) + } + + profile1, _ := objArray.NewObjectElement() + profile2, _ := objArray.NewObjectElement() - tenants := []string{uuid.NewString()} - ids := setupFooListableData(ss, sm, tenants, 60) + if err := SetScalar(profile1, pquery.JSONPath("name"), fmt.Sprintf("profile %d", ii)); err != nil { + t.Fatalf("failed to set profile name: %v", err) + } + + if err := SetScalar(profile1, pquery.JSONPath("place"), int64(ii)+50); err != nil { + t.Fatalf("failed to set profile place: %v", err) + } + + if err := SetScalar(profile2, pquery.JSONPath("name"), fmt.Sprintf("profile %d", ii)); err != nil { + t.Fatalf("failed to set profile name: %v", err) + } + + if err := SetScalar(profile2, pquery.JSONPath("place"), int64(ii)+15); err != nil { + t.Fatalf("failed to set profile place: %v", err) + } + + switch ii { + case 10: + foo.SetScalar(pquery.JSONPath("data", "shape", "circle", "radius"), int64(10)) + case 11: + foo.SetScalar(pquery.JSONPath("data", "shape", "square", "side"), int64(10)) + } + }) + + queryer = uu.FooLister(t) t.Run("Single Range Filter", func(t *testing.T) { - ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.characteristics.weight", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Min: "12", - Max: "15", - }, + req := &test_spb.FooListRequest{ + Page: &list_j5pb.PageRequest{ + PageSize: proto.Int64(5), + }, + Query: &list_j5pb.QueryRequest{ + Filters: []*list_j5pb.Filter{ + { + Type: &list_j5pb.Filter_Field{ + Field: &list_j5pb.Field{ + Name: "data.characteristics.weight", + Type: &list_j5pb.FieldType{ + Type: &list_j5pb.FieldType_Range{ + Range: &list_j5pb.Range{ + Min: "12", + Max: "15", }, }, }, @@ -185,35 +252,35 @@ func TestDynamicFiltering(t *testing.T) { }, }, }, - } - res := &test_spb.FooListResponse{} + }, + } + res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) + if err != nil { + t.Fatal(err.Error()) + } - if len(res.Foo) != 4 { - t.Fatalf("expected %d states, got %d", 4, len(res.Foo)) - } + if len(res.Foo) != 4 { + t.Fatalf("expected %d states, got %d", 4, len(res.Foo)) + } - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } + for ii, state := range res.Foo { + t.Logf("%d: %s", ii, state.Data.Field) + } - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(15-ii) { - t.Fatalf("expected weight %d, got %d", 15-ii, state.Data.Characteristics.Weight) - } + for ii, state := range res.Foo { + if state.Data.Characteristics.Weight != int64(15-ii) { + t.Fatalf("expected weight %d, got %d", 15-ii, state.Data.Characteristics.Weight) } + } - if res.Page != nil { - t.Fatalf("page response should be empty") - } - }) + if res.Page != nil { + t.Fatalf("page response should be empty") + } }) - ss.Step("Min Range Filter", func(ctx context.Context, t flowtest.Asserter) { + t.Run("Min Range Filter", func(t *testing.T) { req := &test_spb.FooListRequest{ Page: &list_j5pb.PageRequest{ PageSize: proto.Int64(5), @@ -239,7 +306,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -262,7 +329,7 @@ func TestDynamicFiltering(t *testing.T) { } }) - ss.Step("Max Range Filter", func(ctx context.Context, t flowtest.Asserter) { + t.Run("Max Range Filter", func(t *testing.T) { req := &test_spb.FooListRequest{ Page: &list_j5pb.PageRequest{ PageSize: proto.Int64(5), @@ -288,7 +355,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -352,7 +419,8 @@ func TestDynamicFiltering(t *testing.T) { }, }, } - ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { + + { req := &test_spb.FooListRequest{ Page: &list_j5pb.PageRequest{ PageSize: proto.Int64(10), @@ -361,7 +429,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -397,9 +465,9 @@ func TestDynamicFiltering(t *testing.T) { nextToken = pageResp.GetNextToken() - }) + } - ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { + { dec, err := base64.StdEncoding.DecodeString(nextToken) if err != nil { t.Fatalf("failed to decode next token: %v", err) @@ -414,7 +482,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err = queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -436,206 +504,201 @@ func TestDynamicFiltering(t *testing.T) { if res.Page != nil { t.Fatalf("page response should be empty") } - }) + } }) t.Run("Flattened filterable fields", func(t *testing.T) { - ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "tenantId", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: tenants[0], - }, + req := &test_spb.FooListRequest{ + Page: &list_j5pb.PageRequest{ + PageSize: proto.Int64(5), + }, + Query: &list_j5pb.QueryRequest{ + Filters: []*list_j5pb.Filter{ + { + Type: &list_j5pb.Filter_Field{ + Field: &list_j5pb.Field{ + Name: "tenantId", + Type: &list_j5pb.FieldType{ + Type: &list_j5pb.FieldType_Value{ + Value: tenantID, }, }, }, }, }, }, - } - res := &test_spb.FooListResponse{} + }, + } + res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err) - } + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) + if err != nil { + t.Fatal(err) + } - if len(res.Foo) == 0 { - t.Fatalf("expected to receive results filtered by tenantId, but got none") - } + if len(res.Foo) == 0 { + t.Fatalf("expected to receive results filtered by tenantId, but got none") + } - for _, foo := range res.Foo { - if *foo.Keys.TenantId != tenants[0] { - t.Fatalf("expected tenantId %s, got %s", tenants[0], *foo.Keys.TenantId) - } + for _, foo := range res.Foo { + if *foo.Keys.TenantId != tenantID { + t.Fatalf("expected tenantId %s, got %s", tenantID, *foo.Keys.TenantId) } - }) + } }) t.Run("Non filterable fields", func(t *testing.T) { - ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "foo_id", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: "d34d826f-afe3-410d-8326-4e9af3f09467", - }, + req := &test_spb.FooListRequest{ + Page: &list_j5pb.PageRequest{ + PageSize: proto.Int64(5), + }, + Query: &list_j5pb.QueryRequest{ + Filters: []*list_j5pb.Filter{ + { + Type: &list_j5pb.Filter_Field{ + Field: &list_j5pb.Field{ + Name: "foo_id", + Type: &list_j5pb.FieldType{ + Type: &list_j5pb.FieldType_Value{ + Value: "d34d826f-afe3-410d-8326-4e9af3f09467", }, }, }, }, }, }, - } - res := &test_spb.FooListResponse{} + }, + } + res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) - if err == nil { - t.Fatalf("expected error, got nil") - } - }) + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) + if err == nil { + t.Fatalf("expected error, got nil") + } }) - t.Run("Enum values", func(t *testing.T) { - ss.Step("List Page short enum name", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "status", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: "ACTIVE", - }, + t.Run("Enum values - Short", func(t *testing.T) { + req := &test_spb.FooListRequest{ + Page: &list_j5pb.PageRequest{ + PageSize: proto.Int64(5), + }, + Query: &list_j5pb.QueryRequest{ + Filters: []*list_j5pb.Filter{ + { + Type: &list_j5pb.Filter_Field{ + Field: &list_j5pb.Field{ + Name: "status", + Type: &list_j5pb.FieldType{ + Type: &list_j5pb.FieldType_Value{ + Value: "ACTIVE", }, }, }, }, }, }, - } - res := &test_spb.FooListResponse{} + }, + } + res := &test_spb.FooListResponse{} - err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err) - } + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) + if err != nil { + t.Fatal(err) + } - if len(res.Foo) != 5 { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } + if len(res.Foo) != 5 { + t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) + } - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } + for ii, state := range res.Foo { + t.Logf("%d: %s", ii, state.Data.Field) + } - for _, state := range res.Foo { - if state.Status != test_pb.FooStatus_ACTIVE { - t.Fatalf("expected status %s, got %s", test_pb.FooStatus_ACTIVE, state.Status) - } + for _, state := range res.Foo { + if state.Status != test_pb.FooStatus_ACTIVE { + t.Fatalf("expected status %s, got %s", test_pb.FooStatus_ACTIVE, state.Status) } - }) + } + }) - ss.Step("List Page full enum name", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "status", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: "FOO_STATUS_ACTIVE", - }, + t.Run("Enum values - Full", func(t *testing.T) { + req := &test_spb.FooListRequest{ + Page: &list_j5pb.PageRequest{ + PageSize: proto.Int64(5), + }, + Query: &list_j5pb.QueryRequest{ + Filters: []*list_j5pb.Filter{ + { + Type: &list_j5pb.Filter_Field{ + Field: &list_j5pb.Field{ + Name: "status", + Type: &list_j5pb.FieldType{ + Type: &list_j5pb.FieldType_Value{ + Value: "FOO_STATUS_ACTIVE", }, }, }, }, }, }, - } - res := &test_spb.FooListResponse{} + }, + } + res := &test_spb.FooListResponse{} - err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err) - } + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) + if err != nil { + t.Fatal(err) + } - if len(res.Foo) != 5 { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } + if len(res.Foo) != 5 { + t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) + } - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } + for ii, state := range res.Foo { + t.Logf("%d: %s", ii, state.Data.Field) + } - for _, state := range res.Foo { - if state.Status != test_pb.FooStatus_ACTIVE { - t.Fatalf("expected status %s, got %s", test_pb.FooStatus_ACTIVE, state.Status) - } + for _, state := range res.Foo { + if state.Status != test_pb.FooStatus_ACTIVE { + t.Fatalf("expected status %s, got %s", test_pb.FooStatus_ACTIVE, state.Status) } - }) + } - ss.Step("List Page bad enum name", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "status", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: "FOO_STATUS_UNUSED", - }, + }) + + t.Run("List Page bad enum name", func(t *testing.T) { + req := &test_spb.FooListRequest{ + Page: &list_j5pb.PageRequest{ + PageSize: proto.Int64(5), + }, + Query: &list_j5pb.QueryRequest{ + Filters: []*list_j5pb.Filter{ + { + Type: &list_j5pb.Filter_Field{ + Field: &list_j5pb.Field{ + Name: "status", + Type: &list_j5pb.FieldType{ + Type: &list_j5pb.FieldType_Value{ + Value: "FOO_STATUS_UNUSED", }, }, }, }, }, }, - } - res := &test_spb.FooListResponse{} + }, + } + res := &test_spb.FooListResponse{} - err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) - if err == nil { - t.Fatal("expected error, got nil") - } - }) + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) + if err == nil { + t.Fatal("expected error, got nil") + } }) t.Run("Single Complex Range Filter", func(t *testing.T) { nextToken := "" - ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { + { req := &test_spb.FooListRequest{ Page: &list_j5pb.PageRequest{ PageSize: proto.Int64(5), @@ -662,7 +725,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -699,9 +762,9 @@ func TestDynamicFiltering(t *testing.T) { } nextToken = pageResp.GetNextToken() - }) + } - ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { + { req := &test_spb.FooListRequest{ Page: &list_j5pb.PageRequest{ PageSize: proto.Int64(5), @@ -729,7 +792,7 @@ func TestDynamicFiltering(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -755,92 +818,88 @@ func TestDynamicFiltering(t *testing.T) { t.Fatalf("expected at least one profile to match the filter") } } - }) + } }) - t.Run("Oneof filter", func(t *testing.T) { - ss.Step("List Page (created)", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooEventsRequest{ - FooId: ids[tenants[0]][0], - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - // match the JSON encoding - // { - // "event": { - // "!type": "created" - // "created": {...} - // } - // ... - // } - Name: "event.!type", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: "created", - }, + t.Run("Oneof filter - happy", func(t *testing.T) { + req := &test_spb.FooListRequest{ + Page: &list_j5pb.PageRequest{ + PageSize: proto.Int64(5), + }, + Query: &list_j5pb.QueryRequest{ + Filters: []*list_j5pb.Filter{ + { + Type: &list_j5pb.Filter_Field{ + Field: &list_j5pb.Field{ + // match the JSON encoding + // { + // "event": { + // "!type": "created" + // "created": {...} + // } + // ... + // } + Name: "data.shape.!type", + Type: &list_j5pb.FieldType{ + Type: &list_j5pb.FieldType_Value{ + Value: "circle", }, }, }, }, }, }, - } - res := &test_spb.FooEventsResponse{} - - err := queryer.EventLister.List(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err) - } + }, + } + res := &test_spb.FooListResponse{} - if len(res.Events) != 1 { - t.Fatalf("expected %d states, got %d", 1, len(res.Events)) - } + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) + if err != nil { + t.Fatal(err) + } - for ii, event := range res.Events { - switch event.Event.Type.(type) { - case *test_pb.FooEventType_Created_: - default: - t.Fatalf("expected event to be of type %T, got %T", &test_pb.FooEventType_Created_{}, event.Event.Type) - } + if len(res.Foo) != 1 { + t.Fatalf("expected %d states, got %d", 1, len(res.Foo)) + } - t.Logf("%d: %s", ii, event.Event) + for ii, event := range res.Foo { + switch event.Data.Shape.Type.(type) { + case *test_pb.FooData_Shape_Circle_: + default: + t.Fatalf("expected event to be of type %T, got %T", &test_pb.FooData_Shape_Circle_{}, event.Data.Shape.Type) } - }) - ss.Step("List Page bad name", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooEventsRequest{ - FooId: ids[tenants[0]][0], - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "event.type", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: "damaged", - }, + t.Logf("%d: %s", ii, event.Data.Shape) + } + }) + + t.Run("Oneof filter - bad name", func(t *testing.T) { + req := &test_spb.FooEventsRequest{ + Page: &list_j5pb.PageRequest{ + PageSize: proto.Int64(5), + }, + Query: &list_j5pb.QueryRequest{ + Filters: []*list_j5pb.Filter{ + { + Type: &list_j5pb.Filter_Field{ + Field: &list_j5pb.Field{ + Name: "data.shape.!type", + Type: &list_j5pb.FieldType{ + Type: &list_j5pb.FieldType_Value{ + Value: "damaged", }, }, }, }, }, }, - } - res := &test_spb.FooEventsResponse{} + }, + } + res := &test_spb.FooListResponse{} - err := queryer.EventLister.List(ctx, db, req.J5Object(), res.J5Object()) - if err == nil { - t.Fatal("expected error, got nil") - } - }) + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) + if err == nil { + t.Fatal("expected error, got nil") + } }) } diff --git a/internal/integration/query_test/helpers.go b/internal/integration/query_test/helpers.go index 56b9297..90f4b09 100644 --- a/internal/integration/query_test/helpers.go +++ b/internal/integration/query_test/helpers.go @@ -13,7 +13,6 @@ import ( "github.com/pentops/j5/gen/j5/auth/v1/auth_j5pb" "github.com/pentops/log.go/log" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" - "github.com/pentops/sqrlx.go/sqrlx" "k8s.io/utils/ptr" ) @@ -34,44 +33,6 @@ func printQuery(t flowtest.TB, query sqrl.Sqlizer) { t.Log(stmt, args) } -func getRawState(db sqrlx.Transactor, id string) (string, error) { - var state []byte - err := db.Transact(context.Background(), nil, func(ctx context.Context, tx sqrlx.Transaction) error { - q := sqrl.Select("state").From("foo").Where("foo_id = ?", id) - err := tx.QueryRow(ctx, q).Scan(&state) - if err != nil { - return err - } - - return nil - }) - - if err != nil { - return "", err - } - - return string(state), nil -} - -func getRawEvent(db sqrlx.Transactor, id string) (string, error) { - var data []byte - err := db.Transact(context.Background(), nil, func(ctx context.Context, tx sqrlx.Transaction) error { - q := sqrl.Select("data").From("foo_event").Where("id = ?", id) - err := tx.QueryRow(ctx, q).Scan(&data) - if err != nil { - return err - } - - return nil - }) - - if err != nil { - return "", err - } - - return string(data), nil -} - func setupFooListableData(ss *flowtest.Stepper[*testing.T], sm *test_pb.FooPSMDB, tenants []string, count int) map[string][]string { ids := make(map[string][]string, len(tenants)) diff --git a/internal/integration/query_test/marshalling_test.go b/internal/integration/query_test/marshalling_test.go deleted file mode 100644 index f360099..0000000 --- a/internal/integration/query_test/marshalling_test.go +++ /dev/null @@ -1,273 +0,0 @@ -package integration - -import ( - "strings" - "testing" - - "github.com/google/uuid" - "github.com/pentops/golib/gl" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" - "k8s.io/utils/ptr" -) - -func TestMarshaling(t *testing.T) { - ss, uu := NewFooUniverse(t) - sm := uu.SM - db := uu.DB - queryer := uu.Query - defer ss.RunSteps(t) - - tenantID := uuid.NewString() - - t.Run("Optional field", func(t *testing.T) { - fooID := uuid.NewString() - - t.Run("Get with empty", func(t *testing.T) { - ctx := t.Context() - event := newFooCreatedEvent(fooID, tenantID, func(c *test_pb.FooEventType_Created) { - c.Description = ptr.To("") - }) - - _, err := sm.Transition(ctx, event) - if err != nil { - t.Fatal(err.Error()) - } - - req := &test_spb.FooGetRequest{ - FooId: fooID, - } - - res := &test_spb.FooGetResponse{} - - err = queryer.Get(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if res.Foo.Data.Description == nil { - t.Fatalf("expected description to be non nil") - } - - if *res.Foo.Data.Description != "" { - t.Fatalf("expected description to be empty, got %s", *res.Foo.Data.Description) - } - - stateJSON, err := getRawState(db, fooID) - if err != nil { - t.Fatal(err.Error()) - } - - if !strings.Contains(stateJSON, `"description": ""`) { - t.Fatalf("expected description to be present, but empty: %s", stateJSON) - } - - t.Log("Event Count:", len(res.Events)) - if len(res.Events) == 0 { - t.Fatal("expected at least one event") - } - - eventJSON, err := getRawEvent(db, res.Events[len(res.Events)-1].Metadata.EventId) - if err != nil { - t.Fatal(err.Error()) - } - - if !strings.Contains(eventJSON, `"description": ""`) { - t.Fatalf("expected description to be present, but empty: %s", eventJSON) - } - }) - - t.Run("Get with non empty", func(t *testing.T) { - ctx := t.Context() - event := newFooUpdatedEvent(fooID, tenantID, func(u *test_pb.FooEventType_Updated) { - u.Description = ptr.To("non blank description") - }) - _, err := sm.Transition(ctx, event) - if err != nil { - t.Fatal(err.Error()) - } - - req := &test_spb.FooGetRequest{ - FooId: fooID, - } - - res := &test_spb.FooGetResponse{} - - err = queryer.Get(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if res.Foo.Data.Description == nil { - t.Fatalf("expected description to be non nil") - } - - if *res.Foo.Data.Description == "" { - t.Fatalf("expected description to be empty, got %s", *res.Foo.Data.Description) - } - - stateJSON, err := getRawState(db, fooID) - if err != nil { - t.Fatal(err.Error()) - } - - if !strings.Contains(stateJSON, `"description": "non blank description"`) { - t.Fatalf("expected description to be present, and not empty: %s", stateJSON) - } - - t.Log("Event Count:", len(res.Events)) - if len(res.Events) == 0 { - t.Fatal("expected at least one event") - } - - eventJSON, err := getRawEvent(db, res.Events[len(res.Events)-1].Metadata.EventId) - if err != nil { - t.Fatal(err.Error()) - } - - if !strings.Contains(eventJSON, `"description":`) { - t.Fatalf("expected description to be present: %s", eventJSON) - } - }) - - t.Run("Get with missing", func(t *testing.T) { - ctx := t.Context() - event := newFooUpdatedEvent(fooID, tenantID, func(u *test_pb.FooEventType_Updated) { - u.Description = nil - }) - - _, err := sm.Transition(ctx, event) - if err != nil { - t.Fatal(err.Error()) - } - - req := &test_spb.FooGetRequest{ - FooId: fooID, - } - - res := &test_spb.FooGetResponse{} - - err = queryer.Get(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if res.Foo.Data.Description != nil { - t.Fatalf("expected description to be nil") - } - - stateJSON, err := getRawState(db, fooID) - if err != nil { - t.Fatal(err.Error()) - } - - if !strings.Contains(stateJSON, `"description": null`) { - t.Fatalf("expected 'description' to be present as null in state: %s", stateJSON) - } - - eventJSON, err := getRawEvent(db, res.Events[len(res.Events)-1].Metadata.EventId) - if err != nil { - t.Fatal(err.Error()) - } - - if !strings.Contains(eventJSON, `"description": null`) { - t.Fatalf("expected 'description' to be present as null in event: %s", stateJSON) - } - }) - }) - - t.Run("Non optional field", func(t *testing.T) { - ctx := t.Context() - fooID := uuid.NewString() - - t.Run("Get with empty", func(t *testing.T) { - event := newFooCreatedEvent(fooID, tenantID, func(c *test_pb.FooEventType_Created) { - c.Description = gl.Ptr("") - }) - - _, err := sm.Transition(ctx, event) - if err != nil { - t.Fatal(err.Error()) - } - - req := &test_spb.FooGetRequest{ - FooId: fooID, - } - - res := &test_spb.FooGetResponse{} - - err = queryer.Get(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if res.Foo.Data.Description == nil && *res.Foo.Data.Description != "" { - t.Errorf("expected description to be not nill but empty") - } - - stateJSON, err := getRawState(db, fooID) - if err != nil { - t.Fatal(err.Error()) - } - - if !strings.Contains(stateJSON, `"description": ""`) { - t.Fatalf("expected 'description' to be present as empty in: %s", stateJSON) - } - - eventJSON, err := getRawEvent(db, res.Events[len(res.Events)-1].Metadata.EventId) - if err != nil { - t.Fatal(err.Error()) - } - - if !strings.Contains(eventJSON, `"description": ""`) { - t.Fatalf("expected 'description' to be present, but empty, in: %s", eventJSON) - } - }) - - t.Run("Get with non empty", func(t *testing.T) { - ctx := t.Context() - event := newFooUpdatedEvent(fooID, tenantID, func(u *test_pb.FooEventType_Updated) { - u.Field = "non empty" - u.Description = nil - }) - - _, err := sm.Transition(ctx, event) - if err != nil { - t.Fatal(err.Error()) - } - - req := &test_spb.FooGetRequest{ - FooId: fooID, - } - - res := &test_spb.FooGetResponse{} - - err = queryer.Get(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if res.Foo.Data.Field == "" { - t.Fatalf("expected description to be non empty") - } - - stateJSON, err := getRawState(db, fooID) - if err != nil { - t.Fatal(err.Error()) - } - - if !strings.Contains(stateJSON, `"field":`) { - t.Fatalf("expected field to be present: %s", stateJSON) - } - - eventJSON, err := getRawEvent(db, res.Events[len(res.Events)-1].Metadata.EventId) - if err != nil { - t.Fatal(err.Error()) - } - - if !strings.Contains(eventJSON, `"field":`) { - t.Fatalf("expected 'field' to be present in: %s", eventJSON) - } - }) - }) -} diff --git a/internal/integration/query_test/psm_universe.go b/internal/integration/query_test/psm_universe.go new file mode 100644 index 0000000..988755d --- /dev/null +++ b/internal/integration/query_test/psm_universe.go @@ -0,0 +1,106 @@ +package integration + +import ( + "context" + "testing" + + "github.com/pentops/flowtest" + "github.com/pentops/pgtest.go/pgtest" + "github.com/pentops/protostate/internal/pgstore/pgmigrate" + "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" + "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" + "github.com/pentops/protostate/pquery" + "github.com/pentops/protostate/psm" + "github.com/pentops/sqrlx.go/sqrlx" +) + +type FooUniverse struct { + SM *test_pb.FooPSMDB + Query *test_spb.FooPSMQuerySet + DB sqrlx.Transactor +} + +type universeSpec struct { + opts psm.StateQueryOptions +} + +type universeOption func(*universeSpec) + +func WithStateQueryOptions(opts psm.StateQueryOptions) universeOption { + return func(us *universeSpec) { + us.opts = opts + } +} + +func NewFooUniverse(t *testing.T, opts ...universeOption) (*flowtest.Stepper[*testing.T], *FooUniverse) { + t.Helper() + + spec := &universeSpec{ + opts: psm.StateQueryOptions{}, + } + + for _, opt := range opts { + opt(spec) + } + + smR, err := NewFooStateMachine() + if err != nil { + t.Fatal(err.Error()) + } + + conn := pgtest.GetTestDB(t, pgtest.WithSchemaName("query_test")) + db := sqrlx.NewPostgres(conn) + + specs := []psm.QueryTableSpec{ + smR.StateTableSpec(), + } + + if err := pgmigrate.CreateStateMachines(context.Background(), conn, specs...); err != nil { + t.Fatal(err.Error()) + } + + if err := pgmigrate.AddIndexes(context.Background(), conn, specs...); err != nil { + t.Fatal(err.Error()) + } + + sm := smR.WithDB(db) + + ss := flowtest.NewStepper[*testing.T](t.Name()) + defer ss.RunSteps(t) + + queryer, err := test_spb.NewFooPSMQuerySet(test_spb.DefaultFooPSMQuerySpec(sm.StateTableSpec()), spec.opts) + if err != nil { + t.Fatal(err.Error()) + } + queryer.SetQueryLogger(testLogger(t)) + + return ss, &FooUniverse{ + DB: db, + SM: sm, + Query: queryer, + } +} + +func (uu *FooUniverse) ListFoo(t flowtest.Asserter, req *test_spb.FooListRequest) *test_spb.FooListResponse { + t.Helper() + ctx := context.Background() + + resp := &test_spb.FooListResponse{} + err := uu.Query.List(ctx, uu.DB, req.J5Object(), resp.J5Object()) + if err != nil { + t.Fatal(err.Error()) + } + + return resp +} + +func testLogger(t flowtest.TB) pquery.QueryLogger { + return func(query sqrlx.Sqlizer) { + queryString, args, err := query.ToSql() + if err != nil { + t.Logf("Query Error: %s", err.Error()) + return + } + t.Logf("Query %s; ARGS %#v", queryString, args) + } +} diff --git a/internal/integration/query_test/setup.go b/internal/integration/query_test/setup.go index 224e0d1..03a7e7e 100644 --- a/internal/integration/query_test/setup.go +++ b/internal/integration/query_test/setup.go @@ -103,19 +103,6 @@ func newFooUpdatedEvent(fooID, tenantID string, mod ...func(u *test_pb.FooEventT }, updated) } -func newFooDeletedEvent(fooID, tenantID string, mod ...func(u *test_pb.FooEventType_Deleted)) *test_pb.FooPSMEventSpec { - deleted := &test_pb.FooEventType_Deleted{} - - for _, m := range mod { - m(deleted) - } - - return newFooEvent(&test_pb.FooKeys{ - FooId: fooID, - TenantId: &tenantID, - MetaTenantId: metaTenant, - }, deleted) -} func newFooEvent(keys *test_pb.FooKeys, et test_pb.FooPSMEvent) *test_pb.FooPSMEventSpec { diff --git a/internal/integration/query_test/sort_test.go b/internal/integration/query_test/sort_test.go index cac4904..de27952 100644 --- a/internal/integration/query_test/sort_test.go +++ b/internal/integration/query_test/sort_test.go @@ -515,6 +515,7 @@ func TestDynamicSorting(t *testing.T) { t.Fatalf("failed to decode next token: %v", err) } t.Logf("Page Token: %s", string(pageTokenBytes)) + req := &test_spb.FooListRequest{ Page: &list_j5pb.PageRequest{ PageSize: proto.Int64(5), diff --git a/internal/integration/query_test/universe.go b/internal/integration/query_test/universe.go index 84ea322..afb204d 100644 --- a/internal/integration/query_test/universe.go +++ b/internal/integration/query_test/universe.go @@ -2,11 +2,19 @@ package integration import ( "context" + "database/sql" + "fmt" + "strings" "testing" + sq "github.com/elgris/sqrl" "github.com/pentops/flowtest" + "github.com/pentops/golib/gl" + "github.com/pentops/j5/lib/id62" + "github.com/pentops/j5/lib/j5reflect" + "github.com/pentops/j5/lib/j5schema" "github.com/pentops/pgtest.go/pgtest" - "github.com/pentops/protostate/internal/pgstore/pgmigrate" + "github.com/pentops/protostate/internal/dbconvert" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" "github.com/pentops/protostate/pquery" @@ -14,25 +22,17 @@ import ( "github.com/pentops/sqrlx.go/sqrlx" ) -type Universe struct { - SM *test_pb.FooPSMDB - Query *test_spb.FooPSMQuerySet - DB sqrlx.Transactor +func NewStepper(t *testing.T) *flowtest.Stepper[*testing.T] { + return flowtest.NewStepper[*testing.T](t.Name()) } -type universeSpec struct { - opts psm.StateQueryOptions -} - -type universeOption func(*universeSpec) +type SchemaUniverse struct { + DB sqrlx.Transactor -func WithStateQueryOptions(opts psm.StateQueryOptions) universeOption { - return func(us *universeSpec) { - us.opts = opts - } + conn *sql.DB } -func NewFooUniverse(t *testing.T, opts ...universeOption) (*flowtest.Stepper[*testing.T], *Universe) { +func NewSchemaUniverse(t *testing.T, opts ...universeOption) *SchemaUniverse { t.Helper() spec := &universeSpec{ @@ -43,64 +43,152 @@ func NewFooUniverse(t *testing.T, opts ...universeOption) (*flowtest.Stepper[*te opt(spec) } - smR, err := NewFooStateMachine() - if err != nil { - t.Fatal(err.Error()) - } - conn := pgtest.GetTestDB(t, pgtest.WithSchemaName("query_test")) db := sqrlx.NewPostgres(conn) - specs := []psm.QueryTableSpec{ - smR.StateTableSpec(), - } + /* + smR, err := NewFooStateMachine() + if err != nil { + t.Fatal(err.Error()) + } - if err := pgmigrate.CreateStateMachines(context.Background(), conn, specs...); err != nil { - t.Fatal(err.Error()) - } + specs := []psm.QueryTableSpec{ + smR.StateTableSpec(), + } - if err := pgmigrate.AddIndexes(context.Background(), conn, specs...); err != nil { - t.Fatal(err.Error()) - } + if err := pgmigrate.CreateStateMachines(context.Background(), conn, specs...); err != nil { + t.Fatal(err.Error()) + } + + if err := pgmigrate.AddIndexes(context.Background(), conn, specs...); err != nil { + t.Fatal(err.Error()) + } - sm := smR.WithDB(db) + sm := smR.WithDB(db) + + queryer, err := test_spb.NewFooPSMQuerySet(test_spb.DefaultFooPSMQuerySpec(sm.StateTableSpec()), spec.opts) + if err != nil { + t.Fatal(err.Error()) + } + queryer.SetQueryLogger(testLogger(t)) + */ + return &SchemaUniverse{ + DB: db, + conn: conn, + } +} - ss := flowtest.NewStepper[*testing.T](t.Name()) - defer ss.RunSteps(t) +func (uu *SchemaUniverse) Migrate(t flowtest.TB, commands ...string) { + for _, cmd := range commands { + if _, err := uu.conn.Exec(cmd); err != nil { + t.Fatal(err.Error()) + } + } +} - queryer, err := test_spb.NewFooPSMQuerySet(test_spb.DefaultFooPSMQuerySpec(sm.StateTableSpec()), spec.opts) +func SetScalar(obj j5reflect.Object, fieldPath pquery.JSONPathSpec, value any) error { + field, err := obj.GetOrCreateValue(fieldPath...) if err != nil { - t.Fatal(err.Error()) + return err } - queryer.SetQueryLogger(testLogger(t)) + scalar, ok := field.AsScalar() + if !ok { + return fmt.Errorf("field %s is not a scalar", strings.Join(fieldPath, ".")) + } + if err := scalar.SetGoValue(value); err != nil { + return err + } + return nil +} + +type TestObject struct { + j5reflect.Object + t flowtest.TB +} - return ss, &Universe{ - DB: db, - SM: sm, - Query: queryer, +func (to *TestObject) SetScalar(fieldPath pquery.JSONPathSpec, value any) { + to.t.Helper() + if err := SetScalar(to.Object, fieldPath, value); err != nil { + to.t.Fatal(err.Error()) } } -func (uu *Universe) ListFoo(t flowtest.Asserter, req *test_spb.FooListRequest) *test_spb.FooListResponse { - t.Helper() - ctx := context.Background() +func (uu *SchemaUniverse) SetupFoo(t flowtest.TB, count int, callback ...func(int, *TestObject)) { + uu.Migrate(t, ` + CREATE TABLE foo ( + foo_id char(36) NOT NULL, + state jsonb NOT NULL + )`) + if err := uu.DB.Transact(t.Context(), &sqrlx.TxOptions{ + Isolation: sql.LevelDefault, + }, func(ctx context.Context, tx sqrlx.Transaction) error { + for ii := range count { + id := id62.NewString() + foo := &test_pb.FooState{} + fooRefl := foo.J5Object() + if err := SetScalar(fooRefl, pquery.JSONPath("fooId"), id); err != nil { + return err + } + if err := SetScalar(fooRefl, pquery.JSONPath("data", "field"), fmt.Sprintf("Foo %d", ii)); err != nil { + return err + } + + if err := SetScalar(fooRefl, pquery.JSONPath("status"), "ACTIVE"); err != nil { + return err + } + + for _, cb := range callback { + cb(ii, &TestObject{t: t, Object: fooRefl}) + } + + fooJSON, err := dbconvert.MarshalJ5(fooRefl) + if err != nil { + return fmt.Errorf("marshal: %w", err) + } + + _, err = tx.Insert(ctx, sq.Insert("foo").Columns("foo_id", "state").Values(id, fooJSON)) + if err != nil { + return err + } - resp := &test_spb.FooListResponse{} - err := uu.Query.List(ctx, uu.DB, req.J5Object(), resp.J5Object()) - if err != nil { + } + return nil + }); err != nil { t.Fatal(err.Error()) } - return resp } -func testLogger(t *testing.T) pquery.QueryLogger { - return func(query sqrlx.Sqlizer) { - queryString, args, err := query.ToSql() - if err != nil { - t.Logf("Query Error: %s", err.Error()) - return - } - t.Logf("Query %s; ARGS %#v", queryString, args) +func (uu *SchemaUniverse) FooLister(t flowtest.TB) *pquery.Lister { + requestSchema, ok := (&test_spb.FooListRequest{}).J5Object().RootSchema() + if !ok { + t.Fatal("failed to get request schema") + } + responseSchema, ok := (&test_spb.FooListResponse{}).J5Object().RootSchema() + if !ok { + t.Fatal("failed to get response schema") + } + + method := &j5schema.MethodSchema{ + Request: requestSchema.(*j5schema.ObjectSchema), + Response: responseSchema.(*j5schema.ObjectSchema), } + listSpec := pquery.ListSpec{ + TableSpec: pquery.TableSpec{ + TableName: "foo", + DataColumn: "state", + FallbackSortColumns: []pquery.ProtoField{ + pquery.NewJSONField("fooId", gl.Ptr("foo_id")), + }, + }, + Method: method, + } + var err error + queryer, err := pquery.NewLister(listSpec) + if err != nil { + t.Fatalf("failed to create queryer: %w", err) + } + queryer.SetQueryLogger(testLogger(t)) + return queryer + } diff --git a/internal/testproto/gen/test/v1/test_pb/foo.j5s.pb.go b/internal/testproto/gen/test/v1/test_pb/foo.j5s.pb.go index 47cbb18..6f2d3bd 100644 --- a/internal/testproto/gen/test/v1/test_pb/foo.j5s.pb.go +++ b/internal/testproto/gen/test/v1/test_pb/foo.j5s.pb.go @@ -144,8 +144,9 @@ type FooData struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Field string `protobuf:"bytes,2,opt,name=field,proto3" json:"field,omitempty"` Description *string `protobuf:"bytes,3,opt,name=description,proto3,oneof" json:"description,omitempty"` - Characteristics *FooCharacteristics `protobuf:"bytes,4,opt,name=characteristics,proto3" json:"characteristics,omitempty"` - Profiles []*FooProfile `protobuf:"bytes,5,rep,name=profiles,proto3" json:"profiles,omitempty"` + Shape *FooData_Shape `protobuf:"bytes,4,opt,name=shape,proto3" json:"shape,omitempty"` + Characteristics *FooCharacteristics `protobuf:"bytes,5,opt,name=characteristics,proto3" json:"characteristics,omitempty"` + Profiles []*FooProfile `protobuf:"bytes,6,rep,name=profiles,proto3" json:"profiles,omitempty"` } func (x *FooData) Reset() { @@ -201,6 +202,13 @@ func (x *FooData) GetDescription() string { return "" } +func (x *FooData) GetShape() *FooData_Shape { + if x != nil { + return x.Shape + } + return nil +} + func (x *FooData) GetCharacteristics() *FooCharacteristics { if x != nil { return x.Characteristics @@ -562,6 +570,181 @@ func (x *FooProfile) GetName() string { return "" } +type FooData_Shape struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Type: + // + // *FooData_Shape_Circle_ + // *FooData_Shape_Square_ + Type isFooData_Shape_Type `protobuf_oneof:"type"` +} + +func (x *FooData_Shape) Reset() { + *x = FooData_Shape{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_foo_j5s_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooData_Shape) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooData_Shape) ProtoMessage() {} + +func (x *FooData_Shape) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_foo_j5s_proto_msgTypes[7] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooData_Shape.ProtoReflect.Descriptor instead. +func (*FooData_Shape) Descriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{1, 0} +} + +func (m *FooData_Shape) GetType() isFooData_Shape_Type { + if m != nil { + return m.Type + } + return nil +} + +func (x *FooData_Shape) GetCircle() *FooData_Shape_Circle { + if x, ok := x.GetType().(*FooData_Shape_Circle_); ok { + return x.Circle + } + return nil +} + +func (x *FooData_Shape) GetSquare() *FooData_Shape_Square { + if x, ok := x.GetType().(*FooData_Shape_Square_); ok { + return x.Square + } + return nil +} + +type isFooData_Shape_Type interface { + isFooData_Shape_Type() +} + +type FooData_Shape_Circle_ struct { + Circle *FooData_Shape_Circle `protobuf:"bytes,1,opt,name=circle,proto3,oneof"` +} + +type FooData_Shape_Square_ struct { + Square *FooData_Shape_Square `protobuf:"bytes,2,opt,name=square,proto3,oneof"` +} + +func (*FooData_Shape_Circle_) isFooData_Shape_Type() {} + +func (*FooData_Shape_Square_) isFooData_Shape_Type() {} + +type FooData_Shape_Circle struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Radius int64 `protobuf:"varint,1,opt,name=radius,proto3" json:"radius,omitempty"` +} + +func (x *FooData_Shape_Circle) Reset() { + *x = FooData_Shape_Circle{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_foo_j5s_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooData_Shape_Circle) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooData_Shape_Circle) ProtoMessage() {} + +func (x *FooData_Shape_Circle) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_foo_j5s_proto_msgTypes[8] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooData_Shape_Circle.ProtoReflect.Descriptor instead. +func (*FooData_Shape_Circle) Descriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{1, 0, 0} +} + +func (x *FooData_Shape_Circle) GetRadius() int64 { + if x != nil { + return x.Radius + } + return 0 +} + +type FooData_Shape_Square struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Side int64 `protobuf:"varint,1,opt,name=side,proto3" json:"side,omitempty"` +} + +func (x *FooData_Shape_Square) Reset() { + *x = FooData_Shape_Square{} + if protoimpl.UnsafeEnabled { + mi := &file_test_v1_foo_j5s_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *FooData_Shape_Square) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*FooData_Shape_Square) ProtoMessage() {} + +func (x *FooData_Shape_Square) ProtoReflect() protoreflect.Message { + mi := &file_test_v1_foo_j5s_proto_msgTypes[9] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use FooData_Shape_Square.ProtoReflect.Descriptor instead. +func (*FooData_Shape_Square) Descriptor() ([]byte, []int) { + return file_test_v1_foo_j5s_proto_rawDescGZIP(), []int{1, 0, 1} +} + +func (x *FooData_Shape_Square) GetSide() int64 { + if x != nil { + return x.Side + } + return 0 +} + type FooEventType_Created struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -579,7 +762,7 @@ type FooEventType_Created struct { func (x *FooEventType_Created) Reset() { *x = FooEventType_Created{} if protoimpl.UnsafeEnabled { - mi := &file_test_v1_foo_j5s_proto_msgTypes[7] + mi := &file_test_v1_foo_j5s_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -592,7 +775,7 @@ func (x *FooEventType_Created) String() string { func (*FooEventType_Created) ProtoMessage() {} func (x *FooEventType_Created) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_foo_j5s_proto_msgTypes[7] + mi := &file_test_v1_foo_j5s_proto_msgTypes[10] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -675,7 +858,7 @@ type FooEventType_Updated struct { func (x *FooEventType_Updated) Reset() { *x = FooEventType_Updated{} if protoimpl.UnsafeEnabled { - mi := &file_test_v1_foo_j5s_proto_msgTypes[8] + mi := &file_test_v1_foo_j5s_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -688,7 +871,7 @@ func (x *FooEventType_Updated) String() string { func (*FooEventType_Updated) ProtoMessage() {} func (x *FooEventType_Updated) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_foo_j5s_proto_msgTypes[8] + mi := &file_test_v1_foo_j5s_proto_msgTypes[11] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -771,7 +954,7 @@ type FooEventType_Deleted struct { func (x *FooEventType_Deleted) Reset() { *x = FooEventType_Deleted{} if protoimpl.UnsafeEnabled { - mi := &file_test_v1_foo_j5s_proto_msgTypes[9] + mi := &file_test_v1_foo_j5s_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -784,7 +967,7 @@ func (x *FooEventType_Deleted) String() string { func (*FooEventType_Deleted) ProtoMessage() {} func (x *FooEventType_Deleted) ProtoReflect() protoreflect.Message { - mi := &file_test_v1_foo_j5s_proto_msgTypes[9] + mi := &file_test_v1_foo_j5s_proto_msgTypes[12] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -836,7 +1019,7 @@ var file_test_v1_foo_j5s_proto_rawDesc = []byte{ 0x08, 0x1a, 0x06, 0x12, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x61, 0x54, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x49, 0x64, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x10, 0x01, 0x42, 0x0c, 0x0a, 0x0a, - 0x5f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, 0xf8, 0x02, 0x0a, 0x07, 0x46, + 0x5f, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x22, 0xe1, 0x05, 0x0a, 0x07, 0x46, 0x6f, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x12, 0x34, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x20, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x12, 0x72, 0x10, 0x0a, 0x0e, 0x52, 0x0c, 0x08, 0x01, 0x12, 0x08, 0x74, 0x73, @@ -849,147 +1032,169 @@ var file_test_v1_foo_j5s_proto_rawDesc = []byte{ 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x19, 0x72, 0x17, 0x0a, 0x15, 0x52, 0x13, 0x08, 0x01, 0x12, 0x0f, 0x74, 0x73, 0x76, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x4e, 0x0a, 0x0f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, - 0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x43, 0x68, 0x61, 0x72, - 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x42, 0x07, 0xc2, 0xff, 0x8e, - 0x02, 0x02, 0x52, 0x00, 0x52, 0x0f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, - 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, - 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, + 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x42, 0x0a, 0x05, 0x73, 0x68, 0x61, 0x70, 0x65, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, + 0x6f, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x70, 0x65, 0x42, 0x14, 0xc2, 0xff, + 0x8e, 0x02, 0x02, 0x62, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x07, 0xaa, 0x01, 0x04, 0x52, 0x02, + 0x08, 0x01, 0x52, 0x05, 0x73, 0x68, 0x61, 0x70, 0x65, 0x12, 0x4e, 0x0a, 0x0f, 0x63, 0x68, 0x61, + 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, + 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x42, + 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x0f, 0x63, 0x68, 0x61, 0x72, 0x61, 0x63, + 0x74, 0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, + 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x73, 0x1a, 0xa2, 0x02, 0x0a, 0x05, 0x53, 0x68, 0x61, 0x70, 0x65, 0x12, 0x40, + 0x0a, 0x06, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x44, 0x61, 0x74, 0x61, + 0x2e, 0x53, 0x68, 0x61, 0x70, 0x65, 0x2e, 0x43, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x42, 0x07, 0xc2, + 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x06, 0x63, 0x69, 0x72, 0x63, 0x6c, 0x65, + 0x12, 0x40, 0x0a, 0x06, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x44, 0x61, + 0x74, 0x61, 0x2e, 0x53, 0x68, 0x61, 0x70, 0x65, 0x2e, 0x53, 0x71, 0x75, 0x61, 0x72, 0x65, 0x42, + 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x06, 0x73, 0x71, 0x75, 0x61, + 0x72, 0x65, 0x1a, 0x43, 0x0a, 0x06, 0x43, 0x69, 0x72, 0x63, 0x6c, 0x65, 0x12, 0x30, 0x0a, 0x06, + 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x18, 0xc2, 0xff, + 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x32, 0x08, 0x52, 0x02, + 0x08, 0x01, 0x5a, 0x02, 0x08, 0x01, 0x52, 0x06, 0x72, 0x61, 0x64, 0x69, 0x75, 0x73, 0x3a, 0x07, + 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x1a, 0x3f, 0x0a, 0x06, 0x53, 0x71, 0x75, 0x61, 0x72, + 0x65, 0x12, 0x2c, 0x0a, 0x04, 0x73, 0x69, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, + 0x18, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x32, + 0x08, 0x52, 0x02, 0x08, 0x01, 0x5a, 0x02, 0x08, 0x01, 0x52, 0x04, 0x73, 0x69, 0x64, 0x65, 0x3a, + 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x5a, + 0x00, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, + 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x10, 0x04, 0x42, 0x0e, + 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb2, + 0x02, 0x0a, 0x08, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, + 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x12, 0x35, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x4b, 0x65, + 0x79, 0x73, 0x42, 0x0f, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, + 0x02, 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x33, 0x0a, 0x04, 0x64, 0x61, 0x74, + 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, + 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, + 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x5e, + 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, + 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x42, 0x32, 0xba, 0x48, 0x08, 0xc8, 0x01, 0x01, 0x82, 0x01, 0x02, 0x10, 0x01, 0xc2, + 0xff, 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x1a, 0xa2, 0x01, 0x17, 0x52, + 0x15, 0x08, 0x01, 0x12, 0x11, 0x46, 0x4f, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, + 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x13, + 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, + 0x6f, 0x10, 0x02, 0x22, 0x8d, 0x08, 0x0a, 0x0c, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x42, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x64, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, + 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, + 0x00, 0x48, 0x00, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x07, + 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x42, 0x07, 0xc2, 0xff, + 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, + 0x1a, 0xe2, 0x02, 0x0a, 0x07, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, + 0x03, 0xf2, 0x01, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x66, 0x69, + 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, + 0xf2, 0x01, 0x00, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2f, 0x0a, 0x0b, 0x64, 0x65, + 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x73, + 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, + 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, 0x01, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, + 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, 0x02, 0x52, 0x06, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x6c, 0x65, 0x6e, + 0x67, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, + 0xfa, 0x01, 0x00, 0x48, 0x03, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x88, 0x01, 0x01, + 0x12, 0x39, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, + 0x00, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x3a, 0x07, 0xc2, 0xff, 0x8e, + 0x02, 0x02, 0x52, 0x00, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, + 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, + 0x65, 0x6e, 0x67, 0x74, 0x68, 0x1a, 0x84, 0x03, 0x0a, 0x07, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x64, 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, + 0x1e, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, + 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, + 0x2f, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x48, 0x00, + 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, + 0x12, 0x25, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, 0x01, 0x52, 0x06, 0x77, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, + 0x00, 0x48, 0x02, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, + 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, + 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, 0x03, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, + 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, - 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, - 0x66, 0x6f, 0x6f, 0x10, 0x04, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xb2, 0x02, 0x0a, 0x08, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, - 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, - 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x35, 0x0a, 0x04, 0x6b, 0x65, 0x79, - 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, - 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0f, 0xba, 0x48, 0x03, 0xc8, 0x01, - 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, - 0x12, 0x33, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, - 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x44, 0x61, 0x74, 0x61, - 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, - 0x04, 0x64, 0x61, 0x74, 0x61, 0x12, 0x5e, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, - 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x32, 0xba, 0x48, 0x08, 0xc8, 0x01, - 0x01, 0x82, 0x01, 0x02, 0x10, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x8a, 0xf7, 0x98, - 0xc6, 0x02, 0x1a, 0xa2, 0x01, 0x17, 0x52, 0x15, 0x08, 0x01, 0x12, 0x11, 0x46, 0x4f, 0x4f, 0x5f, - 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x52, 0x06, 0x73, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, - 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x10, 0x02, 0x22, 0x8d, 0x08, 0x0a, 0x0c, 0x46, - 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x42, 0x0a, 0x07, 0x63, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, - 0x79, 0x70, 0x65, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x42, 0x07, 0xc2, 0xff, 0x8e, - 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, - 0x42, 0x0a, 0x07, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x42, - 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x07, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x64, 0x12, 0x42, 0x0a, 0x07, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, - 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x65, - 0x74, 0x65, 0x64, 0x42, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x48, 0x00, 0x52, 0x07, - 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x1a, 0xe2, 0x02, 0x0a, 0x07, 0x43, 0x72, 0x65, 0x61, - 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, 0x05, 0x66, 0x69, 0x65, 0x6c, - 0x64, 0x12, 0x2f, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, - 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x88, - 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, 0x01, 0x52, 0x06, - 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x68, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, - 0xfa, 0x01, 0x00, 0x48, 0x02, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, - 0x12, 0x25, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, - 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, 0x03, 0x52, 0x06, 0x6c, 0x65, - 0x6e, 0x67, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x74, 0x65, 0x73, 0x74, - 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x42, 0x08, - 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, - 0x65, 0x73, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, + 0x12, 0x20, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, + 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0x8a, 0x02, 0x00, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x1a, 0x84, 0x03, 0x0a, - 0x07, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x12, 0x1c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, - 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x52, - 0x05, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2f, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, - 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xc2, 0xff, 0x8e, - 0x02, 0x03, 0xf2, 0x01, 0x00, 0x48, 0x00, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x69, 0x6f, 0x6e, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, - 0x00, 0x48, 0x01, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, - 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, - 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, 0x02, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x03, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x48, - 0x03, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x88, 0x01, 0x01, 0x12, 0x39, 0x0a, 0x08, - 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x50, 0x72, 0x6f, 0x66, - 0x69, 0x6c, 0x65, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xaa, 0x01, 0x00, 0x52, 0x08, 0x70, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, - 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0x8a, 0x02, + 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x1a, 0x34, 0x0a, 0x07, + 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x20, 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x08, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0x8a, 0x02, 0x00, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, - 0x52, 0x00, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, - 0x6f, 0x6e, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x09, 0x0a, - 0x07, 0x5f, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, 0x65, 0x6e, - 0x67, 0x74, 0x68, 0x1a, 0x34, 0x0a, 0x07, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x12, 0x20, - 0x0a, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x42, 0x08, - 0xc2, 0xff, 0x8e, 0x02, 0x03, 0x8a, 0x02, 0x00, 0x52, 0x06, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, - 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, - 0x5a, 0x00, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xe6, 0x01, 0x0a, 0x08, 0x46, - 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, - 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6a, 0x35, 0x2e, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, - 0x02, 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x35, - 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, - 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0f, - 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, - 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x47, 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, - 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x42, 0x1a, 0xba, 0x48, 0x03, - 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x62, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x07, - 0xaa, 0x01, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x13, - 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, - 0x6f, 0x10, 0x03, 0x22, 0xb3, 0x01, 0x0a, 0x12, 0x46, 0x6f, 0x6f, 0x43, 0x68, 0x61, 0x72, 0x61, - 0x63, 0x74, 0x65, 0x72, 0x69, 0x73, 0x74, 0x69, 0x63, 0x73, 0x12, 0x30, 0x0a, 0x06, 0x77, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x18, 0xc2, 0xff, 0x8e, 0x02, - 0x03, 0xfa, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x32, 0x08, 0x52, 0x02, 0x08, 0x01, - 0x5a, 0x02, 0x08, 0x01, 0x52, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x30, 0x0a, 0x06, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, 0x18, 0xc2, 0xff, - 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x32, 0x08, 0x52, 0x02, - 0x08, 0x01, 0x5a, 0x02, 0x08, 0x01, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x30, - 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x18, - 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x32, 0x08, - 0x52, 0x02, 0x08, 0x01, 0x5a, 0x02, 0x08, 0x01, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, - 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x22, 0x7f, 0x0a, 0x0a, 0x46, 0x6f, 0x6f, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x2a, 0x0a, 0x05, 0x70, 0x6c, 0x61, 0x63, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, 0x14, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, - 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x06, 0x32, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x05, 0x70, 0x6c, - 0x61, 0x63, 0x65, 0x12, 0x3c, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x28, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, - 0x1a, 0x72, 0x18, 0x0a, 0x16, 0x52, 0x14, 0x08, 0x01, 0x12, 0x10, 0x74, 0x73, 0x76, 0x5f, 0x70, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x2a, 0x56, 0x0a, 0x09, 0x46, 0x6f, - 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x4f, 0x4f, 0x5f, 0x53, - 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, - 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x46, 0x4f, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, - 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x4f, - 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, - 0x10, 0x02, 0x42, 0x46, 0x5a, 0x44, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, - 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, - 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x52, 0x00, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x5a, 0x00, 0x42, 0x06, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x22, 0xe6, 0x01, 0x0a, 0x08, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x45, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6a, 0x35, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2e, 0x76, 0x31, + 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x0d, + 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, 0x52, 0x08, 0x6d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x35, 0x0a, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, + 0x46, 0x6f, 0x6f, 0x4b, 0x65, 0x79, 0x73, 0x42, 0x0f, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, + 0xff, 0x8e, 0x02, 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x04, 0x6b, 0x65, 0x79, 0x73, 0x12, 0x47, + 0x0a, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x6f, 0x6f, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x54, 0x79, 0x70, 0x65, 0x42, 0x1a, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0xc2, 0xff, 0x8e, 0x02, + 0x02, 0x62, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x07, 0xaa, 0x01, 0x04, 0x52, 0x02, 0x08, 0x01, + 0x52, 0x05, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x3a, 0x13, 0xc2, 0xff, 0x8e, 0x02, 0x02, 0x52, 0x00, + 0xea, 0x85, 0x8f, 0x02, 0x07, 0x0a, 0x03, 0x66, 0x6f, 0x6f, 0x10, 0x03, 0x22, 0xb3, 0x01, 0x0a, + 0x12, 0x46, 0x6f, 0x6f, 0x43, 0x68, 0x61, 0x72, 0x61, 0x63, 0x74, 0x65, 0x72, 0x69, 0x73, 0x74, + 0x69, 0x63, 0x73, 0x12, 0x30, 0x0a, 0x06, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x03, 0x42, 0x18, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x8a, 0xf7, 0x98, + 0xc6, 0x02, 0x0a, 0x32, 0x08, 0x52, 0x02, 0x08, 0x01, 0x5a, 0x02, 0x08, 0x01, 0x52, 0x06, 0x77, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x30, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x03, 0x42, 0x18, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x8a, + 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x32, 0x08, 0x52, 0x02, 0x08, 0x01, 0x5a, 0x02, 0x08, 0x01, 0x52, + 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x30, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x42, 0x18, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, + 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x0a, 0x32, 0x08, 0x52, 0x02, 0x08, 0x01, 0x5a, 0x02, 0x08, + 0x01, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, 0x02, + 0x52, 0x00, 0x22, 0x7f, 0x0a, 0x0a, 0x46, 0x6f, 0x6f, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x12, 0x2a, 0x0a, 0x05, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x42, + 0x14, 0xc2, 0xff, 0x8e, 0x02, 0x03, 0xfa, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x06, 0x32, + 0x04, 0x52, 0x02, 0x08, 0x01, 0x52, 0x05, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x12, 0x3c, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x28, 0xc2, 0xff, 0x8e, 0x02, + 0x03, 0xf2, 0x01, 0x00, 0x8a, 0xf7, 0x98, 0xc6, 0x02, 0x1a, 0x72, 0x18, 0x0a, 0x16, 0x52, 0x14, + 0x08, 0x01, 0x12, 0x10, 0x74, 0x73, 0x76, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, + 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x3a, 0x07, 0xc2, 0xff, 0x8e, 0x02, + 0x02, 0x52, 0x00, 0x2a, 0x56, 0x0a, 0x09, 0x46, 0x6f, 0x6f, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x1a, 0x0a, 0x16, 0x46, 0x4f, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, + 0x46, 0x4f, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, + 0x45, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x46, 0x4f, 0x4f, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, + 0x53, 0x5f, 0x44, 0x45, 0x4c, 0x45, 0x54, 0x45, 0x44, 0x10, 0x02, 0x42, 0x46, 0x5a, 0x44, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x70, 0x65, 0x6e, 0x74, 0x6f, 0x70, + 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, + 0x67, 0x65, 0x6e, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x65, 0x73, 0x74, + 0x5f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1005,7 +1210,7 @@ func file_test_v1_foo_j5s_proto_rawDescGZIP() []byte { } var file_test_v1_foo_j5s_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_test_v1_foo_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_test_v1_foo_j5s_proto_msgTypes = make([]protoimpl.MessageInfo, 13) var file_test_v1_foo_j5s_proto_goTypes = []interface{}{ (FooStatus)(0), // 0: test.v1.FooStatus (*FooKeys)(nil), // 1: test.v1.FooKeys @@ -1015,32 +1220,38 @@ var file_test_v1_foo_j5s_proto_goTypes = []interface{}{ (*FooEvent)(nil), // 5: test.v1.FooEvent (*FooCharacteristics)(nil), // 6: test.v1.FooCharacteristics (*FooProfile)(nil), // 7: test.v1.FooProfile - (*FooEventType_Created)(nil), // 8: test.v1.FooEventType.Created - (*FooEventType_Updated)(nil), // 9: test.v1.FooEventType.Updated - (*FooEventType_Deleted)(nil), // 10: test.v1.FooEventType.Deleted - (*psm_j5pb.StateMetadata)(nil), // 11: j5.state.v1.StateMetadata - (*psm_j5pb.EventMetadata)(nil), // 12: j5.state.v1.EventMetadata + (*FooData_Shape)(nil), // 8: test.v1.FooData.Shape + (*FooData_Shape_Circle)(nil), // 9: test.v1.FooData.Shape.Circle + (*FooData_Shape_Square)(nil), // 10: test.v1.FooData.Shape.Square + (*FooEventType_Created)(nil), // 11: test.v1.FooEventType.Created + (*FooEventType_Updated)(nil), // 12: test.v1.FooEventType.Updated + (*FooEventType_Deleted)(nil), // 13: test.v1.FooEventType.Deleted + (*psm_j5pb.StateMetadata)(nil), // 14: j5.state.v1.StateMetadata + (*psm_j5pb.EventMetadata)(nil), // 15: j5.state.v1.EventMetadata } var file_test_v1_foo_j5s_proto_depIdxs = []int32{ - 6, // 0: test.v1.FooData.characteristics:type_name -> test.v1.FooCharacteristics - 7, // 1: test.v1.FooData.profiles:type_name -> test.v1.FooProfile - 11, // 2: test.v1.FooState.metadata:type_name -> j5.state.v1.StateMetadata - 1, // 3: test.v1.FooState.keys:type_name -> test.v1.FooKeys - 2, // 4: test.v1.FooState.data:type_name -> test.v1.FooData - 0, // 5: test.v1.FooState.status:type_name -> test.v1.FooStatus - 8, // 6: test.v1.FooEventType.created:type_name -> test.v1.FooEventType.Created - 9, // 7: test.v1.FooEventType.updated:type_name -> test.v1.FooEventType.Updated - 10, // 8: test.v1.FooEventType.deleted:type_name -> test.v1.FooEventType.Deleted - 12, // 9: test.v1.FooEvent.metadata:type_name -> j5.state.v1.EventMetadata - 1, // 10: test.v1.FooEvent.keys:type_name -> test.v1.FooKeys - 4, // 11: test.v1.FooEvent.event:type_name -> test.v1.FooEventType - 7, // 12: test.v1.FooEventType.Created.profiles:type_name -> test.v1.FooProfile - 7, // 13: test.v1.FooEventType.Updated.profiles:type_name -> test.v1.FooProfile - 14, // [14:14] is the sub-list for method output_type - 14, // [14:14] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 8, // 0: test.v1.FooData.shape:type_name -> test.v1.FooData.Shape + 6, // 1: test.v1.FooData.characteristics:type_name -> test.v1.FooCharacteristics + 7, // 2: test.v1.FooData.profiles:type_name -> test.v1.FooProfile + 14, // 3: test.v1.FooState.metadata:type_name -> j5.state.v1.StateMetadata + 1, // 4: test.v1.FooState.keys:type_name -> test.v1.FooKeys + 2, // 5: test.v1.FooState.data:type_name -> test.v1.FooData + 0, // 6: test.v1.FooState.status:type_name -> test.v1.FooStatus + 11, // 7: test.v1.FooEventType.created:type_name -> test.v1.FooEventType.Created + 12, // 8: test.v1.FooEventType.updated:type_name -> test.v1.FooEventType.Updated + 13, // 9: test.v1.FooEventType.deleted:type_name -> test.v1.FooEventType.Deleted + 15, // 10: test.v1.FooEvent.metadata:type_name -> j5.state.v1.EventMetadata + 1, // 11: test.v1.FooEvent.keys:type_name -> test.v1.FooKeys + 4, // 12: test.v1.FooEvent.event:type_name -> test.v1.FooEventType + 9, // 13: test.v1.FooData.Shape.circle:type_name -> test.v1.FooData.Shape.Circle + 10, // 14: test.v1.FooData.Shape.square:type_name -> test.v1.FooData.Shape.Square + 7, // 15: test.v1.FooEventType.Created.profiles:type_name -> test.v1.FooProfile + 7, // 16: test.v1.FooEventType.Updated.profiles:type_name -> test.v1.FooProfile + 17, // [17:17] is the sub-list for method output_type + 17, // [17:17] is the sub-list for method input_type + 17, // [17:17] is the sub-list for extension type_name + 17, // [17:17] is the sub-list for extension extendee + 0, // [0:17] is the sub-list for field type_name } func init() { file_test_v1_foo_j5s_proto_init() } @@ -1134,7 +1345,7 @@ func file_test_v1_foo_j5s_proto_init() { } } file_test_v1_foo_j5s_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooEventType_Created); i { + switch v := v.(*FooData_Shape); i { case 0: return &v.state case 1: @@ -1146,7 +1357,7 @@ func file_test_v1_foo_j5s_proto_init() { } } file_test_v1_foo_j5s_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*FooEventType_Updated); i { + switch v := v.(*FooData_Shape_Circle); i { case 0: return &v.state case 1: @@ -1158,6 +1369,42 @@ func file_test_v1_foo_j5s_proto_init() { } } file_test_v1_foo_j5s_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooData_Shape_Square); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_foo_j5s_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooEventType_Created); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_foo_j5s_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*FooEventType_Updated); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_test_v1_foo_j5s_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*FooEventType_Deleted); i { case 0: return &v.state @@ -1177,15 +1424,19 @@ func file_test_v1_foo_j5s_proto_init() { (*FooEventType_Updated_)(nil), (*FooEventType_Deleted_)(nil), } - file_test_v1_foo_j5s_proto_msgTypes[7].OneofWrappers = []interface{}{} - file_test_v1_foo_j5s_proto_msgTypes[8].OneofWrappers = []interface{}{} + file_test_v1_foo_j5s_proto_msgTypes[7].OneofWrappers = []interface{}{ + (*FooData_Shape_Circle_)(nil), + (*FooData_Shape_Square_)(nil), + } + file_test_v1_foo_j5s_proto_msgTypes[10].OneofWrappers = []interface{}{} + file_test_v1_foo_j5s_proto_msgTypes[11].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_test_v1_foo_j5s_proto_rawDesc, NumEnums: 1, - NumMessages: 10, + NumMessages: 13, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/testproto/gen/test/v1/test_pb/foo.j5s_j5.pb.go b/internal/testproto/gen/test/v1/test_pb/foo.j5s_j5.pb.go index e07ed80..8e82b3a 100644 --- a/internal/testproto/gen/test/v1/test_pb/foo.j5s_j5.pb.go +++ b/internal/testproto/gen/test/v1/test_pb/foo.j5s_j5.pb.go @@ -29,6 +29,35 @@ func (msg *FooData) J5Object() j5reflect.Object { func (msg *FooData) Clone() any { return proto.Clone(msg).(*FooData) } +func (msg *FooData_Shape) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooData_Shape) Clone() any { + return proto.Clone(msg).(*FooData_Shape) +} +func (msg *FooData_Shape_Circle) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooData_Shape_Circle) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooData_Shape_Circle) Clone() any { + return proto.Clone(msg).(*FooData_Shape_Circle) +} +func (msg *FooData_Shape_Square) J5Reflect() j5reflect.Root { + return j5reflect.MustReflect(msg.ProtoReflect()) +} + +func (msg *FooData_Shape_Square) J5Object() j5reflect.Object { + return j5reflect.MustReflect(msg.ProtoReflect()).(j5reflect.Object) +} + +func (msg *FooData_Shape_Square) Clone() any { + return proto.Clone(msg).(*FooData_Shape_Square) +} func (msg *FooState) J5Reflect() j5reflect.Root { return j5reflect.MustReflect(msg.ProtoReflect()) } diff --git a/internal/testproto/gen/test/v1/test_pb/foo.j5s_sugar.pb.go b/internal/testproto/gen/test/v1/test_pb/foo.j5s_sugar.pb.go index 59173e5..6d8f054 100644 --- a/internal/testproto/gen/test/v1/test_pb/foo.j5s_sugar.pb.go +++ b/internal/testproto/gen/test/v1/test_pb/foo.j5s_sugar.pb.go @@ -8,6 +8,8 @@ import ( proto "google.golang.org/protobuf/proto" ) +type IsFooData_Shape_Type = isFooData_Shape_Type + // FooEventType is a oneof wrapper type FooEventTypeKey string diff --git a/internal/testproto/test/v1/foo.j5s b/internal/testproto/test/v1/foo.j5s index c643679..2ceb6b5 100644 --- a/internal/testproto/test/v1/foo.j5s +++ b/internal/testproto/test/v1/foo.j5s @@ -36,6 +36,22 @@ entity Foo { listRules.searching.fieldIdentifier = "tsv_description" } + data shape oneof:FooShape { + listRules.filtering.filterable = true + option circle object { + field radius integer:INT64 { + listRules.filtering.filterable = true + listRules.sorting.sortable = true + } + } + option square object { + field side integer:INT64 { + listRules.filtering.filterable = true + listRules.sorting.sortable = true + } + } + } + data characteristics object:FooCharacteristics data profiles array:object:FooProfile diff --git a/pquery/lister.go b/pquery/lister.go index c9f7fe4..92167c6 100644 --- a/pquery/lister.go +++ b/pquery/lister.go @@ -44,6 +44,16 @@ type TableSpec struct { FallbackSortColumns []ProtoField } +func (ts *TableSpec) Validate() error { + if ts.TableName == "" { + return fmt.Errorf("table name must be set") + } + if ts.DataColumn == "" { + return fmt.Errorf("data column must be set") + } + return nil +} + // ProtoField represents a field within a the root data. type ProtoField struct { // path from the root object to this field @@ -76,6 +86,16 @@ type ListSpec struct { RequestFilter func(j5reflect.Object) (map[string]any, error) } +func (ls *ListSpec) Validate() error { + if ls.Method == nil { + return fmt.Errorf("list spec must have a method") + } + if err := ls.TableSpec.Validate(); err != nil { + return fmt.Errorf("validate table spec: %w", err) + } + return nil +} + type QueryLogger func(sqrlx.Sqlizer) type ListReflectionSet struct { @@ -142,7 +162,7 @@ func buildListReflection(method *j5schema.MethodSchema, table TableSpec) (*ListR } - return nil, fmt.Errorf("unknown field in response: '%s' of type %s", field.FullName(), field.Schema.TypeName()) + return nil, fmt.Errorf("unknown field in list response: '%s' of type %s", field.FullName(), field.Schema.TypeName()) } if ll.arrayField == nil { @@ -244,6 +264,10 @@ func NewLister(spec ListSpec) (*Lister, error) { authJoin: spec.AuthJoin, } + if err := spec.Validate(); err != nil { + return nil, fmt.Errorf("validate list spec: %w", err) + } + listFields, err := buildListReflection(spec.Method, spec.TableSpec) if err != nil { return nil, err diff --git a/pquery/path.go b/pquery/path.go index 9b86944..7e6a9e1 100644 --- a/pquery/path.go +++ b/pquery/path.go @@ -225,6 +225,10 @@ func (pp Path) walk(props j5schema.PropertySet, callback func(Path) error) error // Like ProtoPathSpec but uses JSON field names type JSONPathSpec []string +func JSONPath(path ...string) JSONPathSpec { + return JSONPathSpec(path) +} + func ParseJSONPathSpec(path string) JSONPathSpec { return JSONPathSpec(strings.Split(path, ".")) } From 6f3a320cfc0b735bd186604db995341d2d5e4d7e Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Tue, 29 Jul 2025 13:41:58 -0700 Subject: [PATCH 07/12] More No-PSM Tests --- .../integration/query_test/pagination_test.go | 246 ++---------------- .../integration/query_test/search_test.go | 215 ++++----------- internal/integration/query_test/universe.go | 11 +- internal/pgstore/pgmigrate/psm.go | 36 +++ pquery/lister.go | 4 + 5 files changed, 126 insertions(+), 386 deletions(-) diff --git a/internal/integration/query_test/pagination_test.go b/internal/integration/query_test/pagination_test.go index deaa8ac..3907507 100644 --- a/internal/integration/query_test/pagination_test.go +++ b/internal/integration/query_test/pagination_test.go @@ -9,50 +9,45 @@ import ( "github.com/google/uuid" "github.com/pentops/flowtest" "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" - "github.com/pentops/protostate/psm" - "k8s.io/utils/ptr" + "github.com/pentops/protostate/pquery" ) func TestPagination(t *testing.T) { - ss, uu := NewFooUniverse(t) - sm := uu.SM - defer ss.RunSteps(t) + uu := NewSchemaUniverse(t) + var queryer *pquery.Lister - queryer, err := test_spb.NewFooPSMQuerySet(test_spb.DefaultFooPSMQuerySpec(sm.StateTableSpec()), psm.StateQueryOptions{}) - if err != nil { - t.Fatal(err.Error()) - } + ss := NewStepper(t) + defer ss.RunSteps(t) - ss.Step("Create", func(ctx context.Context, t flowtest.Asserter) { + ss.Setup(func(ctx context.Context, t flowtest.Asserter) error { tenantID := uuid.NewString() - restore := silenceLogger() - defer restore() + uu.SetupFoo(t, 30, func(ii int, foo *TestObject) { - for ii := range 30 { - tt := time.Now() - fooID := uuid.NewString() + foo.SetScalar(pquery.JSONPath("tenantId"), tenantID) - event := newFooCreatedEvent(fooID, tenantID, func(c *test_pb.FooEventType_Created) { - c.Field = fmt.Sprintf("foo %d at %s", ii, tt.Format(time.RFC3339Nano)) - c.Weight = ptr.To(10 + int64(ii)) - }) + weight := (10 + int64(ii)) + + foo.SetScalar(pquery.JSONPath("data", "characteristics", "weight"), weight) + createdAt := time.Now() + foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt) + foo.SetScalar(pquery.JSONPath("data", "field"), fmt.Sprintf("foo %d at %s", ii, createdAt.Format(time.RFC3339Nano))) + }) + queryer = uu.FooLister(t) + return nil - stateOut, err := sm.Transition(ctx, event) - if err != nil { - t.Fatal(err.Error()) - } - t.Equal(test_pb.FooStatus_ACTIVE, stateOut.Status) - t.Equal(tenantID, *stateOut.Keys.TenantId) - } }) + queryer = uu.FooLister(t) var pageResp *list_j5pb.PageResponse ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { - res := uu.ListFoo(t, &test_spb.FooListRequest{}) + req := &test_spb.FooListRequest{} + res := &test_spb.FooListResponse{} + if err := queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()); err != nil { + t.Fatal(err.Error()) + } if len(res.Foo) != 20 { t.Fatalf("expected 20 states, got %d", len(res.Foo)) @@ -80,7 +75,7 @@ func TestPagination(t *testing.T) { } res := &test_spb.FooListResponse{} - query, err := queryer.MainLister.BuildQuery(ctx, req.J5Object(), res.J5Object()) + query, err := queryer.BuildQuery(ctx, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -99,193 +94,8 @@ func TestPagination(t *testing.T) { t.Fatalf("expected 10 states, got %d", len(res.Foo)) } }) -} - -func TestEventPagination(t *testing.T) { - // Foo event default sort is deeply nested. This tests that the nested filter - // works on pagination - ss, uu := NewFooUniverse(t) - sm := uu.SM - db := uu.DB - defer ss.RunSteps(t) - - queryer, err := test_spb.NewFooPSMQuerySet(test_spb.DefaultFooPSMQuerySpec(sm.StateTableSpec()), psm.StateQueryOptions{}) - if err != nil { - t.Fatal(err.Error()) - } - - fooID := uuid.NewString() - ss.Step("CreateEvents", func(ctx context.Context, t flowtest.Asserter) { - tenantID := uuid.NewString() - - restore := silenceLogger() - defer restore() - - event := newFooCreatedEvent(fooID, tenantID) - _, err := sm.Transition(ctx, event) - if err != nil { - t.Fatal(err.Error()) - } - - for ii := range 30 { - tt := time.Now() - event := newFooUpdatedEvent(fooID, tenantID, func(u *test_pb.FooEventType_Updated) { - u.Field = fmt.Sprintf("foo %d at %s", ii, tt.Format(time.RFC3339Nano)) - u.Weight = ptr.To(11 + int64(ii)) - }) - _, err := sm.Transition(ctx, event) - if err != nil { - t.Fatal(err.Error()) - } - } - }) - - var pageResp *list_j5pb.PageResponse - - ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { - - req := &test_spb.FooEventsRequest{ - FooId: fooID, - } - res := &test_spb.FooEventsResponse{} - - err = queryer.ListEvents(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Events) != 20 { - t.Fatalf("expected 20 states, got %d", len(res.Events)) - } - - for ii, evt := range res.Events { - tsv := evt.Metadata.Timestamp.AsTime().Round(time.Microsecond).UnixMicro() - switch et := evt.Event.Type.(type) { - case *test_pb.FooEventType_Created_: - t.Logf("%03d: Create %s - %d", ii, et.Created.Field, tsv) - case *test_pb.FooEventType_Updated_: - t.Logf("%03d: Update %s - %d", ii, et.Updated.Field, tsv) - default: - t.Fatalf("unexpected event type %T", et) - } - } - - pageResp = res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - - }) - - ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { - - req := &test_spb.FooEventsRequest{ - Page: &list_j5pb.PageRequest{ - Token: pageResp.NextToken, - }, - FooId: fooID, - } - res := &test_spb.FooEventsResponse{} - - query, err := queryer.EventLister.BuildQuery(ctx, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - printQuery(t, query) - - err = queryer.ListEvents(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - for ii, evt := range res.Events { - switch et := evt.Event.Type.(type) { - case *test_pb.FooEventType_Created_: - t.Logf("%d: Create %s", ii, et.Created.Field) - case *test_pb.FooEventType_Updated_: - t.Logf("%d: Update %s", ii, et.Updated.Field) - default: - t.Fatalf("unexpected event type %T", et) - } - } - - if len(res.Events) != 11 { - t.Fatalf("expected 10 states, got %d", len(res.Events)) - } - }) -} - -func TestPageSize(t *testing.T) { - - ss, uu := NewFooUniverse(t) - sm := uu.SM - db := uu.DB - queryer := uu.Query - defer ss.RunSteps(t) - - ss.Step("Create", func(ctx context.Context, t flowtest.Asserter) { - tenantID := uuid.NewString() - - restore := silenceLogger() - defer restore() - - for ii := range 30 { - tt := time.Now() - fooID := uuid.NewString() - - event1 := newFooEvent(&test_pb.FooKeys{ - TenantId: &tenantID, - FooId: fooID, - MetaTenantId: metaTenant, - }, &test_pb.FooEventType_Created{ - Name: "foo", - Field: fmt.Sprintf("foo %d at %s", ii, tt.Format(time.RFC3339Nano)), - Weight: ptr.To(10 + int64(ii)), - }, - ) - stateOut, err := sm.Transition(ctx, event1) - if err != nil { - t.Fatal(err.Error()) - } - t.Equal(test_pb.FooStatus_ACTIVE, stateOut.Status) - t.Equal(tenantID, *stateOut.Keys.TenantId) - } - }) - - var pageResp *list_j5pb.PageResponse - - ss.Step("List Page (default)", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{} - res := &test_spb.FooListResponse{} - - err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != int(20) { - t.Fatalf("expected %d states, got %d", 20, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - pageResp = res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - }) - ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { + ss.Step("List Page - Short", func(ctx context.Context, t flowtest.Asserter) { pageSize := int64(5) req := &test_spb.FooListRequest{ Page: &list_j5pb.PageRequest{ @@ -294,7 +104,7 @@ func TestPageSize(t *testing.T) { } res := &test_spb.FooListResponse{} - err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err := queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -317,7 +127,7 @@ func TestPageSize(t *testing.T) { } }) - ss.Step("List Page (exceeding)", func(ctx context.Context, t flowtest.Asserter) { + ss.Step("List Page - exceeding", func(ctx context.Context, t flowtest.Asserter) { pageSize := int64(50) req := &test_spb.FooListRequest{ Page: &list_j5pb.PageRequest{ @@ -326,7 +136,7 @@ func TestPageSize(t *testing.T) { } res := &test_spb.FooListResponse{} - err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err := queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err == nil { t.Fatal("expected error") } diff --git a/internal/integration/query_test/search_test.go b/internal/integration/query_test/search_test.go index 420ef4e..f5a4d57 100644 --- a/internal/integration/query_test/search_test.go +++ b/internal/integration/query_test/search_test.go @@ -1,188 +1,69 @@ package integration import ( - "context" + "fmt" "testing" + "time" - "github.com/google/uuid" - "github.com/pentops/flowtest" "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" + "github.com/pentops/protostate/pquery" "google.golang.org/protobuf/proto" ) func TestDynamicSearching(t *testing.T) { - ss, uu := NewFooUniverse(t) - sm := uu.SM - db := uu.DB - queryer := uu.Query - defer ss.RunSteps(t) + uu := NewSchemaUniverse(t) + var queryer *pquery.Lister - tenants := []string{uuid.NewString()} - setupFooListableData(ss, sm, tenants, 30) + uu.SetupFoo(t, 30, func(ii int, foo *TestObject) { + + weight := (10 + int64(ii)) + + foo.SetScalar(pquery.JSONPath("data", "characteristics", "weight"), weight) + createdAt := time.Now() + foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt) + foo.SetScalar(pquery.JSONPath("data", "field"), fmt.Sprintf("foo %d weighted %d", ii, weight)) + }) + queryer = uu.FooLister(t) t.Run("Simple Search Field", func(t *testing.T) { - ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Searches: []*list_j5pb.Search{ - { - Field: "data.field", - Value: "weighted 30", - }, + req := &test_spb.FooListRequest{ + Page: &list_j5pb.PageRequest{ + PageSize: proto.Int64(5), + }, + Query: &list_j5pb.QueryRequest{ + Searches: []*list_j5pb.Search{ + { + Field: "data.field", + Value: "weighted 30", }, }, + }, + } + res := &test_spb.FooListResponse{} + + err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) + if err != nil { + t.Fatal(err.Error()) + } + + if len(res.Foo) != 1 { + t.Fatalf("expected %d states, got %d", 1, len(res.Foo)) + } + + for ii, state := range res.Foo { + t.Logf("%d: %s", ii, state.Data.Field) + } + + for ii, state := range res.Foo { + if state.Data.Characteristics.Weight != int64(30-ii) { + t.Fatalf("expected weight %d, got %d", 30-ii, state.Data.Characteristics.Weight) } - res := &test_spb.FooListResponse{} - - err := queryer.List(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != 1 { - t.Fatalf("expected %d states, got %d", 1, len(res.Foo)) - } + } - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(30-ii) { - t.Fatalf("expected weight %d, got %d", 30-ii, state.Data.Characteristics.Weight) - } - } - - if res.Page != nil { - t.Fatalf("page response should be empty") - } - }) + if res.Page != nil { + t.Fatalf("page response should be empty") + } }) - /* - t.Run("Complex Search Field", func(t *testing.T) { - nextToken := "" - ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "profiles.place", - Type: &list_j5pb.Field_Range{ - Range: &list_j5pb.Range{ - Min: "15", - Max: "21", - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Profiles) - } - - if len(res.Foo) != 5 { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for _, state := range res.Foo { - matched := false - for _, profile := range state.Profiles { - if profile.Place >= 17 && profile.Place <= 21 { - matched = true - break - } - } - - if !matched { - t.Fatalf("expected at least one profile to match the filter") - } - } - - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - - nextToken = pageResp.GetNextToken() - }) - - ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - Token: &nextToken, - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "profiles.place", - Type: &list_j5pb.Field_Range{ - Range: &list_j5pb.Range{ - Min: "15", - Max: "21", - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Profiles) - } - - if len(res.Foo) != 2 { - t.Fatalf("expected %d states, got %d", 2, len(res.Foo)) - } - - for _, state := range res.Foo { - matched := false - for _, profile := range state.Profiles { - if profile.Place >= 15 && profile.Place <= 16 { - matched = true - break - } - } - - if !matched { - t.Fatalf("expected at least one profile to match the filter") - } - } - }) - }) - */ } diff --git a/internal/integration/query_test/universe.go b/internal/integration/query_test/universe.go index afb204d..9108ce9 100644 --- a/internal/integration/query_test/universe.go +++ b/internal/integration/query_test/universe.go @@ -15,6 +15,7 @@ import ( "github.com/pentops/j5/lib/j5schema" "github.com/pentops/pgtest.go/pgtest" "github.com/pentops/protostate/internal/dbconvert" + "github.com/pentops/protostate/internal/pgstore/pgmigrate" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" "github.com/pentops/protostate/pquery" @@ -183,7 +184,15 @@ func (uu *SchemaUniverse) FooLister(t flowtest.TB) *pquery.Lister { }, Method: method, } - var err error + + migrations, err := pgmigrate.IndexMigrations(listSpec) + if err != nil { + t.Fatalf("failed to get index migrations: %v", err) + } + if err := pgmigrate.RunMigrations(t.Context(), uu.DB, migrations); err != nil { + t.Fatalf("failed to run migrations: %v", err) + } + queryer, err := pquery.NewLister(listSpec) if err != nil { t.Fatalf("failed to create queryer: %w", err) diff --git a/internal/pgstore/pgmigrate/psm.go b/internal/pgstore/pgmigrate/psm.go index 50008fd..a5f5a69 100644 --- a/internal/pgstore/pgmigrate/psm.go +++ b/internal/pgstore/pgmigrate/psm.go @@ -13,6 +13,42 @@ import ( "github.com/pentops/sqrlx.go/sqrlx" ) +func IndexMigrations(spec pquery.ListSpec) ([]MigrationItem, error) { + + lr, err := pquery.BuildListReflection(spec.Method, spec.TableSpec) + if err != nil { + return nil, err + } + + allMigrations := make([]MigrationItem, 0) + indexes, err := buildIndexes(spec.TableName, spec.DataColumn, lr.ArrayObject()) + if err != nil { + return nil, fmt.Errorf("building indexes: %w", err) + } + + for _, index := range indexes { + allMigrations = append(allMigrations, index) + } + + return allMigrations, nil +} + +func RunMigrations(ctx context.Context, db sqrlx.Transactor, migrations []MigrationItem) error { + + return db.Transact(ctx, nil, func(ctx context.Context, tx sqrlx.Transaction) error { + for _, migration := range migrations { + statement, err := migration.ToSQL() + if err != nil { + return err + } + if _, err := tx.ExecRaw(ctx, statement); err != nil { + return err + } + } + return nil + }) +} + func BuildStateMachineMigrations(specs ...psm.QueryTableSpec) ([]byte, error) { allMigrations := make([]MigrationItem, 0, len(specs)*4) diff --git a/pquery/lister.go b/pquery/lister.go index 92167c6..9bbe5a3 100644 --- a/pquery/lister.go +++ b/pquery/lister.go @@ -123,6 +123,10 @@ type ListReflectionSet struct { dataColumn string } +func (ll *ListReflectionSet) ArrayObject() *j5schema.ObjectSchema { + return ll.arrayObject +} + func BuildListReflection(method *j5schema.MethodSchema, table TableSpec) (*ListReflectionSet, error) { return buildListReflection(method, table) } From c2c9a8bd09150e16e556e00f1cf218645106403c Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Tue, 29 Jul 2025 14:40:29 -0700 Subject: [PATCH 08/12] Final no PSM in Query Test --- .../integration/query_test/filter_test.go | 8 +- internal/integration/query_test/helpers.go | 93 ------- .../integration/query_test/pagination_test.go | 4 +- .../integration/query_test/psm_universe.go | 106 -------- .../integration/query_test/search_test.go | 3 +- internal/integration/query_test/setup.go | 126 --------- internal/integration/query_test/sort_test.go | 245 +++++++----------- internal/integration/query_test/token.go | 35 ++- internal/integration/query_test/universe.go | 77 +++--- pquery/lister_test.go | 2 +- 10 files changed, 156 insertions(+), 543 deletions(-) delete mode 100644 internal/integration/query_test/helpers.go delete mode 100644 internal/integration/query_test/psm_universe.go delete mode 100644 internal/integration/query_test/setup.go diff --git a/internal/integration/query_test/filter_test.go b/internal/integration/query_test/filter_test.go index c5895c1..21bcf92 100644 --- a/internal/integration/query_test/filter_test.go +++ b/internal/integration/query_test/filter_test.go @@ -25,12 +25,12 @@ func TestDefaultFiltering(t *testing.T) { var queryer *pquery.Lister ss.Setup(func(ctx context.Context, t flowtest.Asserter) error { + queryer = uu.FooLister(t) uu.SetupFoo(t, 10, func(ii int, foo *TestObject) { if ii < 2 { foo.SetScalar(pquery.JSONPath("status"), "DELETED") } }) - queryer = uu.FooLister(t) return nil }) @@ -80,6 +80,7 @@ func TestFilteringWithAuthScope(t *testing.T) { tenants := []string{tenantID1, tenantID2} ss.Setup(func(ctx context.Context, t flowtest.Asserter) error { + queryer = uu.FooLister(t) uu.SetupFoo(t, 20, func(idx int, foo *TestObject) { // recreating the old function logic tenantId := idx % len(tenants) @@ -100,7 +101,6 @@ func TestFilteringWithAuthScope(t *testing.T) { foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt.Format(time.RFC3339Nano)) }) - queryer = uu.FooLister(t) return nil }) @@ -172,8 +172,8 @@ func TestFilteringWithAuthScope(t *testing.T) { func TestDynamicFiltering(t *testing.T) { uu := NewSchemaUniverse(t) - var queryer *pquery.Lister + queryer := uu.FooLister(t) tenantID := uuid.NewString() uu.SetupFoo(t, 60, func(ii int, foo *TestObject) { weight := (10 + int64(ii)) @@ -227,8 +227,6 @@ func TestDynamicFiltering(t *testing.T) { } }) - queryer = uu.FooLister(t) - t.Run("Single Range Filter", func(t *testing.T) { req := &test_spb.FooListRequest{ Page: &list_j5pb.PageRequest{ diff --git a/internal/integration/query_test/helpers.go b/internal/integration/query_test/helpers.go deleted file mode 100644 index 90f4b09..0000000 --- a/internal/integration/query_test/helpers.go +++ /dev/null @@ -1,93 +0,0 @@ -package integration - -import ( - "context" - "fmt" - "log/slog" - "testing" - "time" - - "github.com/elgris/sqrl" - "github.com/google/uuid" - "github.com/pentops/flowtest" - "github.com/pentops/j5/gen/j5/auth/v1/auth_j5pb" - "github.com/pentops/log.go/log" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" - "k8s.io/utils/ptr" -) - -func silenceLogger() func() { - defaultLogger := log.DefaultLogger - log.DefaultLogger = log.NewCallbackLogger(func(level string, msg string, fields []slog.Attr) { - }) - return func() { - log.DefaultLogger = defaultLogger - } -} - -func printQuery(t flowtest.TB, query sqrl.Sqlizer) { - stmt, args, err := query.ToSql() - if err != nil { - t.Fatal(err.Error()) - } - t.Log(stmt, args) -} - -func setupFooListableData(ss *flowtest.Stepper[*testing.T], sm *test_pb.FooPSMDB, tenants []string, count int) map[string][]string { - ids := make(map[string][]string, len(tenants)) - - for ti := range tenants { - ids[tenants[ti]] = make([]string, 0, count) - for range count { - ids[tenants[ti]] = append(ids[tenants[ti]], uuid.NewString()) - } - } - - ss.Step("Create", func(ctx context.Context, t flowtest.Asserter) { - ti := 0 - for tenant, fooIDs := range ids { - tkn := &token{ - claim: &auth_j5pb.Claim{ - TenantType: "tenant", - TenantId: tenant, - }, - } - ctx = tkn.WithToken(ctx) - - restore := silenceLogger() - defer restore() - - for ii, fooID := range fooIDs { - tt := time.Now() - - event := newFooCreatedEvent(fooID, tenants[ti], func(c *test_pb.FooEventType_Created) { - c.Field = fmt.Sprintf("foo %d at %s (weighted %d, height %d, length %d)", ii, tt.Format(time.RFC3339Nano), (10+ii)*(ti+1), (50-ii)*(ti+1), (ii%2)*(ti+1)) - c.Weight = ptr.To((10 + int64(ii)) * (int64(ti) + 1)) - c.Height = ptr.To((50 - int64(ii)) * (int64(ti) + 1)) - c.Length = ptr.To((int64(ii%2) * (int64(ti) + 1))) - c.Profiles = []*test_pb.FooProfile{ - { - Name: fmt.Sprintf("profile %d", ii), - Place: int64(ii) + 50, - }, - { - Name: fmt.Sprintf("profile %d", ii), - Place: int64(ii) + 15, - }, - } - }) - - stateOut, err := sm.Transition(ctx, event) - if err != nil { - t.Fatalf("setup foo: %s (%#v)", err.Error(), event.Keys) - } - t.Equal(test_pb.FooStatus_ACTIVE, stateOut.Status) - t.Equal(tenants[ti], *stateOut.Keys.TenantId) - } - - ti++ - } - }) - - return ids -} diff --git a/internal/integration/query_test/pagination_test.go b/internal/integration/query_test/pagination_test.go index 3907507..faebc80 100644 --- a/internal/integration/query_test/pagination_test.go +++ b/internal/integration/query_test/pagination_test.go @@ -15,7 +15,7 @@ import ( func TestPagination(t *testing.T) { uu := NewSchemaUniverse(t) - var queryer *pquery.Lister + queryer := uu.FooLister(t) ss := NewStepper(t) defer ss.RunSteps(t) @@ -34,12 +34,10 @@ func TestPagination(t *testing.T) { foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt) foo.SetScalar(pquery.JSONPath("data", "field"), fmt.Sprintf("foo %d at %s", ii, createdAt.Format(time.RFC3339Nano))) }) - queryer = uu.FooLister(t) return nil }) - queryer = uu.FooLister(t) var pageResp *list_j5pb.PageResponse ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { diff --git a/internal/integration/query_test/psm_universe.go b/internal/integration/query_test/psm_universe.go deleted file mode 100644 index 988755d..0000000 --- a/internal/integration/query_test/psm_universe.go +++ /dev/null @@ -1,106 +0,0 @@ -package integration - -import ( - "context" - "testing" - - "github.com/pentops/flowtest" - "github.com/pentops/pgtest.go/pgtest" - "github.com/pentops/protostate/internal/pgstore/pgmigrate" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" - "github.com/pentops/protostate/pquery" - "github.com/pentops/protostate/psm" - "github.com/pentops/sqrlx.go/sqrlx" -) - -type FooUniverse struct { - SM *test_pb.FooPSMDB - Query *test_spb.FooPSMQuerySet - DB sqrlx.Transactor -} - -type universeSpec struct { - opts psm.StateQueryOptions -} - -type universeOption func(*universeSpec) - -func WithStateQueryOptions(opts psm.StateQueryOptions) universeOption { - return func(us *universeSpec) { - us.opts = opts - } -} - -func NewFooUniverse(t *testing.T, opts ...universeOption) (*flowtest.Stepper[*testing.T], *FooUniverse) { - t.Helper() - - spec := &universeSpec{ - opts: psm.StateQueryOptions{}, - } - - for _, opt := range opts { - opt(spec) - } - - smR, err := NewFooStateMachine() - if err != nil { - t.Fatal(err.Error()) - } - - conn := pgtest.GetTestDB(t, pgtest.WithSchemaName("query_test")) - db := sqrlx.NewPostgres(conn) - - specs := []psm.QueryTableSpec{ - smR.StateTableSpec(), - } - - if err := pgmigrate.CreateStateMachines(context.Background(), conn, specs...); err != nil { - t.Fatal(err.Error()) - } - - if err := pgmigrate.AddIndexes(context.Background(), conn, specs...); err != nil { - t.Fatal(err.Error()) - } - - sm := smR.WithDB(db) - - ss := flowtest.NewStepper[*testing.T](t.Name()) - defer ss.RunSteps(t) - - queryer, err := test_spb.NewFooPSMQuerySet(test_spb.DefaultFooPSMQuerySpec(sm.StateTableSpec()), spec.opts) - if err != nil { - t.Fatal(err.Error()) - } - queryer.SetQueryLogger(testLogger(t)) - - return ss, &FooUniverse{ - DB: db, - SM: sm, - Query: queryer, - } -} - -func (uu *FooUniverse) ListFoo(t flowtest.Asserter, req *test_spb.FooListRequest) *test_spb.FooListResponse { - t.Helper() - ctx := context.Background() - - resp := &test_spb.FooListResponse{} - err := uu.Query.List(ctx, uu.DB, req.J5Object(), resp.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - return resp -} - -func testLogger(t flowtest.TB) pquery.QueryLogger { - return func(query sqrlx.Sqlizer) { - queryString, args, err := query.ToSql() - if err != nil { - t.Logf("Query Error: %s", err.Error()) - return - } - t.Logf("Query %s; ARGS %#v", queryString, args) - } -} diff --git a/internal/integration/query_test/search_test.go b/internal/integration/query_test/search_test.go index f5a4d57..b082d6b 100644 --- a/internal/integration/query_test/search_test.go +++ b/internal/integration/query_test/search_test.go @@ -13,7 +13,7 @@ import ( func TestDynamicSearching(t *testing.T) { uu := NewSchemaUniverse(t) - var queryer *pquery.Lister + queryer := uu.FooLister(t) uu.SetupFoo(t, 30, func(ii int, foo *TestObject) { @@ -24,7 +24,6 @@ func TestDynamicSearching(t *testing.T) { foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt) foo.SetScalar(pquery.JSONPath("data", "field"), fmt.Sprintf("foo %d weighted %d", ii, weight)) }) - queryer = uu.FooLister(t) t.Run("Simple Search Field", func(t *testing.T) { req := &test_spb.FooListRequest{ diff --git a/internal/integration/query_test/setup.go b/internal/integration/query_test/setup.go deleted file mode 100644 index 03a7e7e..0000000 --- a/internal/integration/query_test/setup.go +++ /dev/null @@ -1,126 +0,0 @@ -package integration - -import ( - "fmt" - - "github.com/google/uuid" - "github.com/pentops/golib/gl" - "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" - "k8s.io/utils/ptr" -) - -func NewFooStateMachine() (*test_pb.FooPSM, error) { - sm, err := test_pb.FooPSMBuilder().BuildStateMachine() - if err != nil { - return nil, err - } - - sm.From(0). - OnEvent(test_pb.FooPSMEventCreated). - SetStatus(test_pb.FooStatus_ACTIVE). - Mutate(test_pb.FooPSMMutation(func( - data *test_pb.FooData, - event *test_pb.FooEventType_Created, - ) error { - data.Name = event.Name - data.Field = event.Field - data.Description = event.Description - data.Characteristics = &test_pb.FooCharacteristics{ - Weight: event.GetWeight(), - Height: event.GetHeight(), - Length: event.GetLength(), - } - data.Profiles = event.Profiles - return nil - })) - - // Testing Mutate() without OnEvent, the callback implies the event type. - sm.From(test_pb.FooStatus_ACTIVE). - Mutate(test_pb.FooPSMMutation(func( - data *test_pb.FooData, - event *test_pb.FooEventType_Updated, - ) error { - data.Field = event.Field - data.Name = event.Name - data.Description = event.Description - data.Characteristics = &test_pb.FooCharacteristics{ - Weight: event.GetWeight(), - Height: event.GetHeight(), - Length: event.GetLength(), - } - - return nil - })) - - sm.From(test_pb.FooStatus_ACTIVE). - OnEvent(test_pb.FooPSMEventDeleted). - SetStatus(test_pb.FooStatus_DELETED) - - return sm, nil - -} - -var metaTenant = uuid.NewString() - -func newFooCreatedEvent(fooID, tenantID string, mod ...func(c *test_pb.FooEventType_Created)) *test_pb.FooPSMEventSpec { - weight := int64(10) - created := &test_pb.FooEventType_Created{ - Name: "foo", - Field: fmt.Sprintf("weight: %d", weight), - Description: gl.Ptr("creation event for foo: " + fooID), - Weight: &weight, - } - - for _, m := range mod { - m(created) - } - - return newFooEvent(&test_pb.FooKeys{ - FooId: fooID, - TenantId: &tenantID, - MetaTenantId: metaTenant, - }, created) -} - -func newFooUpdatedEvent(fooID, tenantID string, mod ...func(u *test_pb.FooEventType_Updated)) *test_pb.FooPSMEventSpec { - weight := int64(20) - updated := &test_pb.FooEventType_Updated{ - Name: "foo", - Field: fmt.Sprintf("weight: %d", weight), - Description: ptr.To("update event for foo: " + fooID), - Weight: &weight, - } - - for _, m := range mod { - m(updated) - } - - return newFooEvent(&test_pb.FooKeys{ - FooId: fooID, - TenantId: &tenantID, - MetaTenantId: metaTenant, - }, updated) - -} - -func newFooEvent(keys *test_pb.FooKeys, et test_pb.FooPSMEvent) *test_pb.FooPSMEventSpec { - - if keys.MetaTenantId == "" { - panic("metaTenantId is required") - } - e := &test_pb.FooPSMEventSpec{ - Keys: keys, - Cause: &psm_j5pb.Cause{ - Type: &psm_j5pb.Cause_ExternalEvent{ - ExternalEvent: &psm_j5pb.ExternalEventCause{ - SystemName: "a", - EventName: "b", - }, - }, - }, - Event: et, - } - - return e -} diff --git a/internal/integration/query_test/sort_test.go b/internal/integration/query_test/sort_test.go index de27952..ec86f15 100644 --- a/internal/integration/query_test/sort_test.go +++ b/internal/integration/query_test/sort_test.go @@ -3,30 +3,61 @@ package integration import ( "context" "encoding/base64" + "fmt" "testing" + "time" "github.com/google/uuid" "github.com/pentops/flowtest" "github.com/pentops/j5/gen/j5/auth/v1/auth_j5pb" "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" + "github.com/pentops/protostate/pquery" "google.golang.org/protobuf/proto" ) func TestSortingWithAuthScope(t *testing.T) { - ss, uu := NewFooUniverse(t, WithStateQueryOptions(newTokenQueryStateOption())) - sm := uu.SM - db := uu.DB - queryer := uu.Query - var err error + uu := NewSchemaUniverse(t) + ss := NewStepper(t) defer ss.RunSteps(t) + var queryer *pquery.Lister + tenantID1 := uuid.NewString() tenantID2 := uuid.NewString() tenants := []string{tenantID1, tenantID2} - setupFooListableData(ss, sm, tenants, 10) + + ss.Setup(func(ctx context.Context, t flowtest.Asserter) error { + queryer = uu.FooLister(t, func(spec *pquery.TableSpec) { + spec.Auth = tokenAuth() + }) + uu.SetupFoo(t, 20, func(idx int, foo *TestObject) { + // recreating the old function logic + tenantId := idx % len(tenants) + tenantID := tenants[tenantId] + ii := idx / len(tenants) + ti := tenantId + + foo.SetScalar(pquery.JSONPath("tenantId"), tenantID) + + weight := ((10 + int64(ii)) * (int64(ti) + 1)) + height := ((50 - int64(ii)) * (int64(ti) + 1)) + length := (int64(ii%2) * (int64(ti) + 1)) + + foo.SetScalar(pquery.JSONPath("data", "characteristics", "weight"), weight) + foo.SetScalar(pquery.JSONPath("data", "characteristics", "height"), height) + foo.SetScalar(pquery.JSONPath("data", "characteristics", "length"), length) + createdAt := time.Now() + foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt.Format(time.RFC3339Nano)) + + label := fmt.Sprintf("foo %d (weighted %d, height %d, length %d)", ii, weight, height, length) + foo.SetScalar(pquery.JSONPath("data", "field"), label) + + }) + return nil + }) tkn := &token{ claim: &auth_j5pb.Claim{ @@ -51,7 +82,7 @@ func TestSortingWithAuthScope(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err := queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -66,11 +97,11 @@ func TestSortingWithAuthScope(t *testing.T) { for ii, state := range res.Foo { if state.Data.Characteristics.Weight != int64(10+ii) { - t.Fatalf("expected weight %d, got %d", 10+ii, state.Data.Characteristics.Weight) + t.Fatalf("%d: expected weight %d, got %d", 10+ii, ii, state.Data.Characteristics.Weight) } if *state.Keys.TenantId != tenantID1 { - t.Fatalf("expected tenant ID %s, got %s", tenantID1, state.Keys.TenantId) + t.Fatalf("%d: expected tenant ID %s, got %s", ii, tenantID1, state.Keys.TenantId) } } @@ -108,7 +139,7 @@ func TestSortingWithAuthScope(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -123,11 +154,11 @@ func TestSortingWithAuthScope(t *testing.T) { for ii, state := range res.Foo { if state.Data.Characteristics.Weight != int64(15+ii) { - t.Fatalf("expected weight %d, got %d", 15+ii, state.Data.Characteristics.Weight) + t.Fatalf("%d: expected weight %d, got %d", ii, 15+ii, state.Data.Characteristics.Weight) } if *state.Keys.TenantId != tenantID1 { - t.Fatalf("expected tenant ID %s, got %s", tenantID1, state.Keys.TenantId) + t.Fatalf("%d: expected tenant ID %s, got %s", ii, tenantID1, state.Keys.TenantId) } } @@ -137,129 +168,68 @@ func TestSortingWithAuthScope(t *testing.T) { }) } -func TestSortingWithAuthNoScope(t *testing.T) { - ss, uu := NewFooUniverse(t, WithStateQueryOptions(newTokenQueryStateOption())) - sm := uu.SM - db := uu.DB - queryer := uu.Query - var err error - defer ss.RunSteps(t) - - tenants := []string{uuid.NewString(), uuid.NewString()} - setupFooListableData(ss, sm, tenants, 30) - - tkn := &token{ - claim: &auth_j5pb.Claim{ - TenantType: "meta_tenant", - TenantId: metaTenant, - }, - } - - nextToken := "" - ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { - ctx = tkn.WithToken(ctx) - - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Sorts: []*list_j5pb.Sort{ - {Field: "data.characteristics.weight"}, - }, - }, - } - res := &test_spb.FooListResponse{} - - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) +func TestDynamicSorting(t *testing.T) { + uu := NewSchemaUniverse(t) + + queryer := uu.FooLister(t) + tenantID := uuid.NewString() + uu.SetupFoo(t, 30, func(ii int, foo *TestObject) { + weight := (10 + int64(ii)) + height := (50 - int64(ii)) + length := (int64(ii % 2)) + + foo.SetScalar(pquery.JSONPath("tenantId"), tenantID) + foo.SetScalar(pquery.JSONPath("data", "characteristics", "weight"), weight) + foo.SetScalar(pquery.JSONPath("data", "characteristics", "height"), height) + foo.SetScalar(pquery.JSONPath("data", "characteristics", "length"), length) + createdAt := time.Now() + foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt.Format(time.RFC3339Nano)) + + label := fmt.Sprintf("foo %d at %s (weighted %d, height %d, length %d)", ii, createdAt.Format(time.RFC3339Nano), weight, height, length) + foo.SetScalar(pquery.JSONPath("data", "field"), label) + + val, err := foo.GetOrCreateValue("data", "profiles") if err != nil { - t.Fatal(err.Error()) + t.Fatalf("failed to get or create profiles: %v", err) } - if len(res.Foo) != int(5) { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) + objArray, ok := val.AsArrayOfObject() + if !ok { + t.Fatalf("expected profiles to be an array of objects, got %T", val) } - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } + profile1, _ := objArray.NewObjectElement() + profile2, _ := objArray.NewObjectElement() - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(10+ii) { - t.Fatalf("expected weight %d, got %d", 10+ii, state.Data.Characteristics.Weight) - } + if err := SetScalar(profile1, pquery.JSONPath("name"), fmt.Sprintf("profile %d", ii)); err != nil { + t.Fatalf("failed to set profile name: %v", err) } - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") + if err := SetScalar(profile1, pquery.JSONPath("place"), int64(ii)+50); err != nil { + t.Fatalf("failed to set profile place: %v", err) } - nextToken = pageResp.GetNextToken() - }) - - ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { - ctx = tkn.WithToken(ctx) - - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - Token: &nextToken, - }, - Query: &list_j5pb.QueryRequest{ - Sorts: []*list_j5pb.Sort{ - {Field: "data.characteristics.weight"}, - }, - }, + if err := SetScalar(profile2, pquery.JSONPath("name"), fmt.Sprintf("profile %d", ii)); err != nil { + t.Fatalf("failed to set profile name: %v", err) } - res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) + if err := SetScalar(profile2, pquery.JSONPath("place"), int64(ii)+15); err != nil { + t.Fatalf("failed to set profile place: %v", err) } - if len(res.Foo) != int(5) { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(15+ii) { - t.Fatalf("expected weight %d, got %d", 15+ii, state.Data.Characteristics.Weight) - } - } - - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") + switch ii { + case 10: + foo.SetScalar(pquery.JSONPath("data", "shape", "circle", "radius"), int64(10)) + case 11: + foo.SetScalar(pquery.JSONPath("data", "shape", "square", "side"), int64(10)) } }) -} - -func TestDynamicSorting(t *testing.T) { t.Run("Top Level Field", func(t *testing.T) { - ss, uu := NewFooUniverse(t) - sm := uu.SM - db := uu.DB - queryer := uu.Query - var err error + ss := NewStepper(t) defer ss.RunSteps(t) + var err error - tenants := []string{uuid.NewString()} - setupFooListableData(ss, sm, tenants, 30) nextToken := "" ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { req := &test_spb.FooListRequest{ @@ -274,7 +244,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -319,7 +289,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -350,15 +320,10 @@ func TestDynamicSorting(t *testing.T) { }) t.Run("Nested Field", func(t *testing.T) { - ss, uu := NewFooUniverse(t) - sm := uu.SM - db := uu.DB - queryer := uu.Query - var err error + ss := NewStepper(t) defer ss.RunSteps(t) + var err error - tenants := []string{uuid.NewString()} - setupFooListableData(ss, sm, tenants, 30) nextToken := "" ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { req := &test_spb.FooListRequest{ @@ -373,7 +338,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -418,7 +383,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -449,15 +414,9 @@ func TestDynamicSorting(t *testing.T) { }) t.Run("Multiple Nested Fields", func(t *testing.T) { - ss, uu := NewFooUniverse(t) - sm := uu.SM - db := uu.DB - queryer := uu.Query - var err error + ss := NewStepper(t) defer ss.RunSteps(t) - - tenants := []string{uuid.NewString()} - setupFooListableData(ss, sm, tenants, 30) + var err error nextToken := "" ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { req := &test_spb.FooListRequest{ @@ -473,7 +432,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -530,7 +489,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -565,15 +524,9 @@ func TestDynamicSorting(t *testing.T) { }) t.Run("Descending", func(t *testing.T) { - ss, uu := NewFooUniverse(t) - sm := uu.SM - db := uu.DB - queryer := uu.Query - var err error + ss := NewStepper(t) defer ss.RunSteps(t) - - tenants := []string{uuid.NewString()} - setupFooListableData(ss, sm, tenants, 30) + var err error nextToken := "" ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { req := &test_spb.FooListRequest{ @@ -591,7 +544,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } @@ -639,7 +592,7 @@ func TestDynamicSorting(t *testing.T) { } res := &test_spb.FooListResponse{} - err = queryer.List(ctx, db, req.J5Object(), res.J5Object()) + err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) if err != nil { t.Fatal(err.Error()) } diff --git a/internal/integration/query_test/token.go b/internal/integration/query_test/token.go index ea9e410..d0ffa84 100644 --- a/internal/integration/query_test/token.go +++ b/internal/integration/query_test/token.go @@ -6,7 +6,6 @@ import ( "github.com/pentops/j5/gen/j5/auth/v1/auth_j5pb" "github.com/pentops/protostate/pquery" - "github.com/pentops/protostate/psm" ) type tokenCtxKey struct{} @@ -28,26 +27,24 @@ func TokenFromCtx(ctx context.Context) (*token, error) { return token, nil } -func newTokenQueryStateOption() psm.StateQueryOptions { +func tokenAuth() pquery.AuthProvider { fieldMap := map[string]string{ "tenant": "tenant_id", "meta_tenant": "meta_tenant_id", } - return psm.StateQueryOptions{ - Auth: pquery.AuthProviderFunc(func(ctx context.Context) (map[string]string, error) { - token, err := TokenFromCtx(ctx) - if err != nil { - return nil, err - } - - tt, ok := fieldMap[token.claim.TenantType] - if !ok { - return nil, fmt.Errorf("no field mapping for tenant type %s", token.claim.TenantType) - } - - return map[string]string{ - tt: token.claim.TenantId, - }, nil - }), - } + return pquery.AuthProviderFunc(func(ctx context.Context) (map[string]string, error) { + token, err := TokenFromCtx(ctx) + if err != nil { + return nil, err + } + + tt, ok := fieldMap[token.claim.TenantType] + if !ok { + return nil, fmt.Errorf("no field mapping for tenant type %s", token.claim.TenantType) + } + + return map[string]string{ + tt: token.claim.TenantId, + }, nil + }) } diff --git a/internal/integration/query_test/universe.go b/internal/integration/query_test/universe.go index 9108ce9..d735c85 100644 --- a/internal/integration/query_test/universe.go +++ b/internal/integration/query_test/universe.go @@ -19,10 +19,28 @@ import ( "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" "github.com/pentops/protostate/pquery" - "github.com/pentops/protostate/psm" "github.com/pentops/sqrlx.go/sqrlx" ) +func printQuery(t flowtest.TB, query sq.Sqlizer) { + stmt, args, err := query.ToSql() + if err != nil { + t.Fatal(err.Error()) + } + t.Log(stmt, args) +} + +func testLogger(t flowtest.TB) pquery.QueryLogger { + return func(query sqrlx.Sqlizer) { + queryString, args, err := query.ToSql() + if err != nil { + t.Logf("Query Error: %s", err.Error()) + return + } + t.Logf("Query %s; ARGS %#v", queryString, args) + } +} + func NewStepper(t *testing.T) *flowtest.Stepper[*testing.T] { return flowtest.NewStepper[*testing.T](t.Name()) } @@ -33,46 +51,12 @@ type SchemaUniverse struct { conn *sql.DB } -func NewSchemaUniverse(t *testing.T, opts ...universeOption) *SchemaUniverse { +func NewSchemaUniverse(t *testing.T) *SchemaUniverse { t.Helper() - spec := &universeSpec{ - opts: psm.StateQueryOptions{}, - } - - for _, opt := range opts { - opt(spec) - } - conn := pgtest.GetTestDB(t, pgtest.WithSchemaName("query_test")) db := sqrlx.NewPostgres(conn) - /* - smR, err := NewFooStateMachine() - if err != nil { - t.Fatal(err.Error()) - } - - specs := []psm.QueryTableSpec{ - smR.StateTableSpec(), - } - - if err := pgmigrate.CreateStateMachines(context.Background(), conn, specs...); err != nil { - t.Fatal(err.Error()) - } - - if err := pgmigrate.AddIndexes(context.Background(), conn, specs...); err != nil { - t.Fatal(err.Error()) - } - - sm := smR.WithDB(db) - - queryer, err := test_spb.NewFooPSMQuerySet(test_spb.DefaultFooPSMQuerySpec(sm.StateTableSpec()), spec.opts) - if err != nil { - t.Fatal(err.Error()) - } - queryer.SetQueryLogger(testLogger(t)) - */ return &SchemaUniverse{ DB: db, conn: conn, @@ -80,6 +64,7 @@ func NewSchemaUniverse(t *testing.T, opts ...universeOption) *SchemaUniverse { } func (uu *SchemaUniverse) Migrate(t flowtest.TB, commands ...string) { + t.Helper() for _, cmd := range commands { if _, err := uu.conn.Exec(cmd); err != nil { t.Fatal(err.Error()) @@ -115,11 +100,7 @@ func (to *TestObject) SetScalar(fieldPath pquery.JSONPathSpec, value any) { } func (uu *SchemaUniverse) SetupFoo(t flowtest.TB, count int, callback ...func(int, *TestObject)) { - uu.Migrate(t, ` - CREATE TABLE foo ( - foo_id char(36) NOT NULL, - state jsonb NOT NULL - )`) + t.Helper() if err := uu.DB.Transact(t.Context(), &sqrlx.TxOptions{ Isolation: sql.LevelDefault, }, func(ctx context.Context, tx sqrlx.Transaction) error { @@ -160,7 +141,15 @@ func (uu *SchemaUniverse) SetupFoo(t flowtest.TB, count int, callback ...func(in } -func (uu *SchemaUniverse) FooLister(t flowtest.TB) *pquery.Lister { +func (uu *SchemaUniverse) FooLister(t flowtest.TB, mods ...func(*pquery.TableSpec)) *pquery.Lister { + t.Helper() + uu.Migrate(t, ` + CREATE TABLE foo ( + foo_id char(36) NOT NULL, + state jsonb NOT NULL, + tenant_id text GENERATED ALWAYS AS (state->>'tenantId') STORED + )`) + requestSchema, ok := (&test_spb.FooListRequest{}).J5Object().RootSchema() if !ok { t.Fatal("failed to get request schema") @@ -185,6 +174,10 @@ func (uu *SchemaUniverse) FooLister(t flowtest.TB) *pquery.Lister { Method: method, } + for _, mod := range mods { + mod(&listSpec.TableSpec) + } + migrations, err := pgmigrate.IndexMigrations(listSpec) if err != nil { t.Fatalf("failed to get index migrations: %v", err) diff --git a/pquery/lister_test.go b/pquery/lister_test.go index 1cc9540..242732c 100644 --- a/pquery/lister_test.go +++ b/pquery/lister_test.go @@ -410,7 +410,7 @@ func TestBuildListReflection(t *testing.T) { `, }.toString(), nil, - "unknown field in response", + "unknown field", ) runSad("extra array field in response", composed{ From 474b97ec7b4eb3562040c140a7291b377e43c467 Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Tue, 29 Jul 2025 16:04:24 -0700 Subject: [PATCH 09/12] Prepare for merge with j5 --- internal/integration/universe_test.go | 5 +- internal/pgstore/pgmigrate/psm.go | 315 ------------------ internal/pgstore/psmpg/psm.go | 169 ++++++++++ .../pgstore/{pgmigrate => psmpg}/psm_test.go | 2 +- .../internal/integration}/filter_test.go | 0 .../internal/integration}/pagination_test.go | 0 .../internal/integration}/search_test.go | 0 .../internal/integration}/sort_test.go | 0 .../internal/integration}/token.go | 0 .../internal/integration}/universe.go | 37 +- pquery/lister.go | 4 + .../pgstore => pquery}/pgmigrate/builder.go | 40 +++ .../pgmigrate/migrations.go | 19 ++ pquery/pgmigrate/search.go | 127 +++++++ psm/state_query.go | 18 + psmigrate/migrate.go | 4 +- 16 files changed, 404 insertions(+), 336 deletions(-) delete mode 100644 internal/pgstore/pgmigrate/psm.go create mode 100644 internal/pgstore/psmpg/psm.go rename internal/pgstore/{pgmigrate => psmpg}/psm_test.go (97%) rename {internal/integration/query_test => pquery/internal/integration}/filter_test.go (100%) rename {internal/integration/query_test => pquery/internal/integration}/pagination_test.go (100%) rename {internal/integration/query_test => pquery/internal/integration}/search_test.go (100%) rename {internal/integration/query_test => pquery/internal/integration}/sort_test.go (100%) rename {internal/integration/query_test => pquery/internal/integration}/token.go (100%) rename {internal/integration/query_test => pquery/internal/integration}/universe.go (89%) rename {internal/pgstore => pquery}/pgmigrate/builder.go (79%) rename internal/pgstore/pgmigrate/printer.go => pquery/pgmigrate/migrations.go (71%) create mode 100644 pquery/pgmigrate/search.go diff --git a/internal/integration/universe_test.go b/internal/integration/universe_test.go index 361cb88..f0eee6e 100644 --- a/internal/integration/universe_test.go +++ b/internal/integration/universe_test.go @@ -8,7 +8,7 @@ import ( "github.com/pentops/flowtest" "github.com/pentops/log.go/log" "github.com/pentops/pgtest.go/pgtest" - "github.com/pentops/protostate/internal/pgstore/pgmigrate" + "github.com/pentops/protostate/internal/pgstore/psmpg" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" "github.com/pentops/protostate/psm" @@ -58,7 +58,6 @@ func setupUniverse(t flowtest.Asserter, uu *Universe) { } fooQuery, err := test_spb.NewFooPSMQuerySet(test_spb.DefaultFooPSMQuerySpec(sm.Foo.StateTableSpec()), psm.StateQueryOptions{}) - if err != nil { t.Fatal(err.Error()) } @@ -73,7 +72,7 @@ func setupUniverse(t flowtest.Asserter, uu *Universe) { uu.FooQuery = NewMiniFooController(db, fooQuery) uu.BarQuery = NewMiniBarController(db, barQuery) - if err := pgmigrate.CreateStateMachines(context.Background(), conn, + if err := psmpg.CreateStateMachines(context.Background(), conn, sm.Foo.StateTableSpec(), sm.Bar.StateTableSpec(), ); err != nil { diff --git a/internal/pgstore/pgmigrate/psm.go b/internal/pgstore/pgmigrate/psm.go deleted file mode 100644 index a5f5a69..0000000 --- a/internal/pgstore/pgmigrate/psm.go +++ /dev/null @@ -1,315 +0,0 @@ -package pgmigrate - -import ( - "context" - "fmt" - "strings" - - sq "github.com/elgris/sqrl" - "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" - "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/protostate/pquery" - "github.com/pentops/protostate/psm" - "github.com/pentops/sqrlx.go/sqrlx" -) - -func IndexMigrations(spec pquery.ListSpec) ([]MigrationItem, error) { - - lr, err := pquery.BuildListReflection(spec.Method, spec.TableSpec) - if err != nil { - return nil, err - } - - allMigrations := make([]MigrationItem, 0) - indexes, err := buildIndexes(spec.TableName, spec.DataColumn, lr.ArrayObject()) - if err != nil { - return nil, fmt.Errorf("building indexes: %w", err) - } - - for _, index := range indexes { - allMigrations = append(allMigrations, index) - } - - return allMigrations, nil -} - -func RunMigrations(ctx context.Context, db sqrlx.Transactor, migrations []MigrationItem) error { - - return db.Transact(ctx, nil, func(ctx context.Context, tx sqrlx.Transaction) error { - for _, migration := range migrations { - statement, err := migration.ToSQL() - if err != nil { - return err - } - if _, err := tx.ExecRaw(ctx, statement); err != nil { - return err - } - } - return nil - }) -} - -func BuildStateMachineMigrations(specs ...psm.QueryTableSpec) ([]byte, error) { - - allMigrations := make([]MigrationItem, 0, len(specs)*4) - - for _, spec := range specs { - stateTable, eventTable, err := BuildPSMTables(spec) - if err != nil { - return nil, err - } - allMigrations = append(allMigrations, stateTable) - - indexes, err := buildIndexes(spec.State.TableName, spec.State.Root.ColumnName, spec.StateType) - if err != nil { - return nil, err - } - for _, index := range indexes { - allMigrations = append(allMigrations, index) - } - - allMigrations = append(allMigrations, eventTable) - - indexes, err = buildIndexes(spec.Event.TableName, spec.Event.Root.ColumnName, spec.EventType) - if err != nil { - return nil, err - } - for _, index := range indexes { - allMigrations = append(allMigrations, index) - } - } - - fileData, err := PrintMigrations(allMigrations...) - if err != nil { - return nil, err - } - return fileData, nil -} - -func CreateStateMachines(ctx context.Context, conn sqrlx.Connection, specs ...psm.QueryTableSpec) error { - db, err := sqrlx.New(conn, sq.Dollar) - if err != nil { - return err - } - - tables := make([]*Table, 0, len(specs)) - for _, spec := range specs { - stateTable, eventTable, err := BuildPSMTables(spec) - if err != nil { - return err - } - tables = append(tables, stateTable, eventTable) - } - - return db.Transact(ctx, nil, func(ctx context.Context, tx sqrlx.Transaction) error { - if _, err := conn.BeginTx(ctx, nil); err != nil { - return err - } - - for _, table := range tables { - - statement, err := table.ToSQL() - if err != nil { - return err - } - _, err = tx.ExecRaw(ctx, statement) - if err != nil { - return err - } - } - return nil - }) -} - -type searchSpec struct { - tsvColumn string - tableName string - columnName string - path pquery.Path -} - -func (ss searchSpec) ToSQL() (string, error) { - statement := fmt.Sprintf("to_tsvector('english', jsonb_path_query_array(%s, '%s'))", ss.columnName, ss.path.JSONPathQuery()) - - lines := []string{} - - lines = append(lines, fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s tsvector GENERATED ALWAYS", ss.tableName, ss.tsvColumn)) - lines = append(lines, fmt.Sprintf(" AS (%s) STORED;", statement)) - lines = append(lines, "") - lines = append(lines, fmt.Sprintf("CREATE INDEX %s_%s_idx ON %s USING GIN (%s);", ss.tableName, ss.tsvColumn, ss.tableName, ss.tsvColumn)) - return strings.Join(lines, "\n"), nil - -} - -func (ss searchSpec) DownSQL() (string, error) { - return fmt.Sprintf("DROP INDEX %s_%s_idx;\nALTER TABLE %s DROP COLUMN %s;", ss.tableName, ss.tsvColumn, ss.tableName, ss.tsvColumn), nil -} - -func AddIndexes(ctx context.Context, conn sqrlx.Connection, specs ...psm.QueryTableSpec) error { - allIndexes := make([]searchSpec, 0) - for _, spec := range specs { - indexes, err := buildIndexes(spec.State.TableName, spec.State.Root.ColumnName, spec.StateType) - if err != nil { - return err - } - allIndexes = append(allIndexes, indexes...) - - indexes, err = buildIndexes(spec.Event.TableName, spec.Event.Root.ColumnName, spec.EventType) - if err != nil { - return err - } - allIndexes = append(allIndexes, indexes...) - } - - return writeIndexes(ctx, conn, allIndexes) -} - -func buildIndexes(tableName string, columnName string, rootType *j5schema.ObjectSchema) ([]searchSpec, error) { - - cols, err := pquery.TSVColumns(rootType) - if err != nil { - return nil, err - } - - specs := []searchSpec{} - - for _, col := range cols { - specs = append(specs, searchSpec{ - tsvColumn: col.ColumnName, - tableName: tableName, - columnName: columnName, - path: col.Path, - }) - - } - - return specs, nil - -} - -func writeIndexes(ctx context.Context, conn sqrlx.Connection, specs []searchSpec) error { - - db, err := sqrlx.New(conn, sq.Dollar) - if err != nil { - return err - } - - return db.Transact(ctx, nil, func(ctx context.Context, tx sqrlx.Transaction) error { - for _, spec := range specs { - - var count int - - err := tx.QueryRow(ctx, sq.Select("COUNT(column_name)"). - From("information_schema.columns"). - Where("table_schema = CURRENT_SCHEMA"). - Where(sq.Eq{"table_name": spec.tableName, "column_name": spec.tsvColumn})).Scan(&count) - if err != nil { - return err - } - if count > 0 { - return nil - } - - statement := fmt.Sprintf("to_tsvector('english', jsonb_path_query_array(%s, '%s'))", spec.columnName, spec.path.JSONPathQuery()) - - _, err = tx.ExecRaw(ctx, fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s tsvector GENERATED ALWAYS AS (%s) STORED;", spec.tableName, spec.tsvColumn, statement)) - if err != nil { - return err - } - - _, err = tx.ExecRaw(ctx, fmt.Sprintf("CREATE INDEX %s_%s_idx ON %s USING GIN (%s);", spec.tableName, spec.tsvColumn, spec.tableName, spec.tsvColumn)) - if err != nil { - return err - } - - } - return nil - }) - -} - -const ( - uuidType = ColumnType("uuid") - textType = ColumnType("text") - id62Type = ColumnType("char(22)") - intType = ColumnType("int") - timestamptzType = ColumnType("timestamptz") - jsonbType = ColumnType("jsonb") - dateType = ColumnType("date") -) - -func BuildPSMTables(spec psm.QueryTableSpec) (*Table, *Table, error) { - - stateTable := CreateTable(spec.State.TableName) - - eventTable := CreateTable(spec.Event.TableName). - Column(spec.Event.ID.ColumnName, uuidType, PrimaryKey) - - eventForeignKey := eventTable.ForeignKey("state", spec.State.TableName) - for _, key := range spec.KeyColumns { - format, err := fieldFormat(key.Schema) - if err != nil { - return nil, nil, fmt.Errorf("key %s: %w", key.ColumnName, err) - } - - if key.Primary { - stateTable.Column(key.ColumnName, format, PrimaryKey) - eventTable.Column(key.ColumnName, format, NotNull) - eventForeignKey.Column(key.ColumnName, key.ColumnName) - continue - } - if key.Required { - stateTable.Column(key.ColumnName, format, NotNull) - eventTable.Column(key.ColumnName, format, NotNull) - continue - } - stateTable.Column(key.ColumnName, format) - eventTable.Column(key.ColumnName, format) - } - - stateTable.Column(spec.State.Root.ColumnName, jsonbType, NotNull) - - eventTable.Column(spec.Event.Timestamp.ColumnName, timestamptzType, NotNull). - Column(spec.Event.Sequence.ColumnName, intType, NotNull). - Column(spec.Event.Root.ColumnName, jsonbType, NotNull). - Column(spec.Event.StateSnapshot.ColumnName, jsonbType, NotNull) - - state, err := stateTable.Build() - if err != nil { - return nil, nil, err - } - - event, err := eventTable.Build() - if err != nil { - return nil, nil, err - } - - return state, event, nil -} - -func fieldFormat(schema *schema_j5pb.Field) (ColumnType, error) { - - switch ft := schema.Type.(type) { - case *schema_j5pb.Field_String_: - return textType, nil - case *schema_j5pb.Field_Key: - if ft.Key.Format == nil { - return textType, nil - } - switch ft.Key.Format.Type.(type) { - case *schema_j5pb.KeyFormat_Custom_: - return textType, nil - case *schema_j5pb.KeyFormat_Uuid: - return uuidType, nil - case *schema_j5pb.KeyFormat_Id62: - return id62Type, nil - default: - return textType, nil - } - case *schema_j5pb.Field_Date: - return dateType, nil - default: - return textType, fmt.Errorf("unsupported type for key field %T", schema.Type) - } - -} diff --git a/internal/pgstore/psmpg/psm.go b/internal/pgstore/psmpg/psm.go new file mode 100644 index 0000000..052aab4 --- /dev/null +++ b/internal/pgstore/psmpg/psm.go @@ -0,0 +1,169 @@ +package psmpg + +import ( + "context" + "fmt" + + sq "github.com/elgris/sqrl" + "github.com/pentops/protostate/pquery/pgmigrate" + "github.com/pentops/protostate/psm" + "github.com/pentops/sqrlx.go/sqrlx" +) + +func BuildStateMachineMigrations(specs ...psm.QueryTableSpec) ([]byte, error) { + + allMigrations := make([]pgmigrate.MigrationItem, 0, len(specs)*4) + + for _, spec := range specs { + if err := spec.Validate(); err != nil { + return nil, fmt.Errorf("validate spec: %w", err) + } + stateTable, eventTable, err := BuildPSMTables(spec) + if err != nil { + return nil, err + } + allMigrations = append(allMigrations, stateTable, eventTable) + + stateList := spec.StateTable() + indexes, err := pgmigrate.IndexMigrations(stateList) + if err != nil { + return nil, err + } + + for _, index := range indexes { + allMigrations = append(allMigrations, index) + } + + eventList := spec.EventTable() + indexes, err = pgmigrate.IndexMigrations(eventList) + if err != nil { + return nil, err + } + + for _, index := range indexes { + allMigrations = append(allMigrations, index) + } + } + + fileData, err := pgmigrate.PrintMigrations(allMigrations...) + if err != nil { + return nil, err + } + return fileData, nil +} + +func CreateStateMachines(ctx context.Context, conn sqrlx.Connection, specs ...psm.QueryTableSpec) error { + db, err := sqrlx.New(conn, sq.Dollar) + if err != nil { + return err + } + + tables := make([]*pgmigrate.Table, 0, len(specs)) + for _, spec := range specs { + stateTable, eventTable, err := BuildPSMTables(spec) + if err != nil { + return err + } + tables = append(tables, stateTable, eventTable) + } + + return db.Transact(ctx, nil, func(ctx context.Context, tx sqrlx.Transaction) error { + if _, err := conn.BeginTx(ctx, nil); err != nil { + return err + } + + for _, table := range tables { + + statement, err := table.ToSQL() + if err != nil { + return err + } + _, err = tx.ExecRaw(ctx, statement) + if err != nil { + return err + } + } + return nil + }) +} + +func AddIndexes(ctx context.Context, conn sqrlx.Transactor, specs ...psm.QueryTableSpec) error { + indexes, err := BuildIndexes(specs...) + if err != nil { + return fmt.Errorf("build indexes: %w", err) + } + return pgmigrate.RunMigrations(ctx, conn, indexes) +} + +func BuildIndexes(specs ...psm.QueryTableSpec) ([]pgmigrate.MigrationItem, error) { + allIndexes := make([]pgmigrate.MigrationItem, 0) + for _, spec := range specs { + if err := spec.Validate(); err != nil { + return nil, fmt.Errorf("validate spec: %w", err) + } + stateList := spec.StateTable() + indexes, err := pgmigrate.IndexMigrations(stateList) + if err != nil { + return nil, err + } + allIndexes = append(allIndexes, indexes...) + + eventList := spec.EventTable() + indexes, err = pgmigrate.IndexMigrations(eventList) + if err != nil { + return nil, err + } + allIndexes = append(allIndexes, indexes...) + } + + return allIndexes, nil +} + +func BuildPSMTables(spec psm.QueryTableSpec) (*pgmigrate.Table, *pgmigrate.Table, error) { + + stateTable := pgmigrate.CreateTable(spec.State.TableName) + + eventTable := pgmigrate.CreateTable(spec.Event.TableName). + Column(spec.Event.ID.ColumnName, pgmigrate.UUID, pgmigrate.PrimaryKey) + + eventForeignKey := eventTable.ForeignKey("state", spec.State.TableName) + for _, key := range spec.KeyColumns { + format, err := pgmigrate.FieldFormat(key.Schema) + if err != nil { + return nil, nil, fmt.Errorf("key %s: %w", key.ColumnName, err) + } + + if key.Primary { + stateTable.Column(key.ColumnName, format, pgmigrate.PrimaryKey) + eventTable.Column(key.ColumnName, format, pgmigrate.NotNull) + eventForeignKey.Column(key.ColumnName, key.ColumnName) + continue + } + if key.Required { + stateTable.Column(key.ColumnName, format, pgmigrate.NotNull) + eventTable.Column(key.ColumnName, format, pgmigrate.NotNull) + continue + } + stateTable.Column(key.ColumnName, format) + eventTable.Column(key.ColumnName, format) + } + + stateTable.Column(spec.State.Root.ColumnName, pgmigrate.JSONB, pgmigrate.NotNull) + + eventTable.Column(spec.Event.Timestamp.ColumnName, pgmigrate.Timestamptz, pgmigrate.NotNull). + Column(spec.Event.Sequence.ColumnName, pgmigrate.Int, pgmigrate.NotNull). + Column(spec.Event.Root.ColumnName, pgmigrate.JSONB, pgmigrate.NotNull). + Column(spec.Event.StateSnapshot.ColumnName, pgmigrate.JSONB, pgmigrate.NotNull) + + state, err := stateTable.Build() + if err != nil { + return nil, nil, err + } + + event, err := eventTable.Build() + if err != nil { + return nil, nil, err + } + + return state, event, nil +} diff --git a/internal/pgstore/pgmigrate/psm_test.go b/internal/pgstore/psmpg/psm_test.go similarity index 97% rename from internal/pgstore/pgmigrate/psm_test.go rename to internal/pgstore/psmpg/psm_test.go index ea715c8..ee56853 100644 --- a/internal/pgstore/pgmigrate/psm_test.go +++ b/internal/pgstore/psmpg/psm_test.go @@ -1,4 +1,4 @@ -package pgmigrate +package psmpg import ( "testing" diff --git a/internal/integration/query_test/filter_test.go b/pquery/internal/integration/filter_test.go similarity index 100% rename from internal/integration/query_test/filter_test.go rename to pquery/internal/integration/filter_test.go diff --git a/internal/integration/query_test/pagination_test.go b/pquery/internal/integration/pagination_test.go similarity index 100% rename from internal/integration/query_test/pagination_test.go rename to pquery/internal/integration/pagination_test.go diff --git a/internal/integration/query_test/search_test.go b/pquery/internal/integration/search_test.go similarity index 100% rename from internal/integration/query_test/search_test.go rename to pquery/internal/integration/search_test.go diff --git a/internal/integration/query_test/sort_test.go b/pquery/internal/integration/sort_test.go similarity index 100% rename from internal/integration/query_test/sort_test.go rename to pquery/internal/integration/sort_test.go diff --git a/internal/integration/query_test/token.go b/pquery/internal/integration/token.go similarity index 100% rename from internal/integration/query_test/token.go rename to pquery/internal/integration/token.go diff --git a/internal/integration/query_test/universe.go b/pquery/internal/integration/universe.go similarity index 89% rename from internal/integration/query_test/universe.go rename to pquery/internal/integration/universe.go index d735c85..6632e3d 100644 --- a/internal/integration/query_test/universe.go +++ b/pquery/internal/integration/universe.go @@ -15,10 +15,10 @@ import ( "github.com/pentops/j5/lib/j5schema" "github.com/pentops/pgtest.go/pgtest" "github.com/pentops/protostate/internal/dbconvert" - "github.com/pentops/protostate/internal/pgstore/pgmigrate" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" "github.com/pentops/protostate/pquery" + "github.com/pentops/protostate/pquery/pgmigrate" "github.com/pentops/sqrlx.go/sqrlx" ) @@ -158,27 +158,25 @@ func (uu *SchemaUniverse) FooLister(t flowtest.TB, mods ...func(*pquery.TableSpe if !ok { t.Fatal("failed to get response schema") } - - method := &j5schema.MethodSchema{ - Request: requestSchema.(*j5schema.ObjectSchema), - Response: responseSchema.(*j5schema.ObjectSchema), + fooSchema, ok := (&test_pb.FooState{}).J5Object().RootSchema() + if !ok { + t.Fatal("failed to get foo schema") } - listSpec := pquery.ListSpec{ - TableSpec: pquery.TableSpec{ - TableName: "foo", - DataColumn: "state", - FallbackSortColumns: []pquery.ProtoField{ - pquery.NewJSONField("fooId", gl.Ptr("foo_id")), - }, + + tableSpec := pquery.TableSpec{ + TableName: "foo", + DataColumn: "state", + RootObject: fooSchema.(*j5schema.ObjectSchema), + FallbackSortColumns: []pquery.ProtoField{ + pquery.NewJSONField("fooId", gl.Ptr("foo_id")), }, - Method: method, } for _, mod := range mods { - mod(&listSpec.TableSpec) + mod(&tableSpec) } - migrations, err := pgmigrate.IndexMigrations(listSpec) + migrations, err := pgmigrate.IndexMigrations(tableSpec) if err != nil { t.Fatalf("failed to get index migrations: %v", err) } @@ -186,6 +184,15 @@ func (uu *SchemaUniverse) FooLister(t flowtest.TB, mods ...func(*pquery.TableSpe t.Fatalf("failed to run migrations: %v", err) } + method := &j5schema.MethodSchema{ + Request: requestSchema.(*j5schema.ObjectSchema), + Response: responseSchema.(*j5schema.ObjectSchema), + } + listSpec := pquery.ListSpec{ + TableSpec: tableSpec, + Method: method, + } + queryer, err := pquery.NewLister(listSpec) if err != nil { t.Fatalf("failed to create queryer: %w", err) diff --git a/pquery/lister.go b/pquery/lister.go index 9bbe5a3..4816d95 100644 --- a/pquery/lister.go +++ b/pquery/lister.go @@ -39,6 +39,7 @@ type TableSpec struct { AuthJoin []*LeftJoin DataColumn string // TODO: Replace with array Columns []Column + RootObject *j5schema.ObjectSchema // List of fields to sort by if no other unique sort is found. FallbackSortColumns []ProtoField @@ -51,6 +52,9 @@ func (ts *TableSpec) Validate() error { if ts.DataColumn == "" { return fmt.Errorf("data column must be set") } + if ts.RootObject == nil { + return fmt.Errorf("root object must be set") + } return nil } diff --git a/internal/pgstore/pgmigrate/builder.go b/pquery/pgmigrate/builder.go similarity index 79% rename from internal/pgstore/pgmigrate/builder.go rename to pquery/pgmigrate/builder.go index c2c6d73..7048f8a 100644 --- a/internal/pgstore/pgmigrate/builder.go +++ b/pquery/pgmigrate/builder.go @@ -3,6 +3,8 @@ package pgmigrate import ( "fmt" "strings" + + "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" ) type CreateTableBuilder struct { @@ -178,4 +180,42 @@ func (table *Table) ToSQL() (string, error) { } lines = append(lines, ");") return strings.Join(lines, "\n"), nil + +} + +const ( + uuidType = ColumnType("uuid") + textType = ColumnType("text") + id62Type = ColumnType("char(22)") + intType = ColumnType("int") + timestamptzType = ColumnType("timestamptz") + jsonbType = ColumnType("jsonb") + dateType = ColumnType("date") +) + +func FieldFormat(schema *schema_j5pb.Field) (ColumnType, error) { + + switch ft := schema.Type.(type) { + case *schema_j5pb.Field_String_: + return textType, nil + case *schema_j5pb.Field_Key: + if ft.Key.Format == nil { + return textType, nil + } + switch ft.Key.Format.Type.(type) { + case *schema_j5pb.KeyFormat_Custom_: + return textType, nil + case *schema_j5pb.KeyFormat_Uuid: + return uuidType, nil + case *schema_j5pb.KeyFormat_Id62: + return id62Type, nil + default: + return textType, nil + } + case *schema_j5pb.Field_Date: + return dateType, nil + default: + return textType, fmt.Errorf("unsupported type for key field %T", schema.Type) + } + } diff --git a/internal/pgstore/pgmigrate/printer.go b/pquery/pgmigrate/migrations.go similarity index 71% rename from internal/pgstore/pgmigrate/printer.go rename to pquery/pgmigrate/migrations.go index c5957db..22b113c 100644 --- a/internal/pgstore/pgmigrate/printer.go +++ b/pquery/pgmigrate/migrations.go @@ -2,8 +2,11 @@ package pgmigrate import ( "bytes" + "context" "fmt" "strings" + + "github.com/pentops/sqrlx.go/sqrlx" ) type MigrationItem interface { @@ -11,6 +14,22 @@ type MigrationItem interface { DownSQL() (string, error) } +func RunMigrations(ctx context.Context, db sqrlx.Transactor, migrations []MigrationItem) error { + + return db.Transact(ctx, nil, func(ctx context.Context, tx sqrlx.Transaction) error { + for _, migration := range migrations { + statement, err := migration.ToSQL() + if err != nil { + return err + } + if _, err := tx.ExecRaw(ctx, statement); err != nil { + return err + } + } + return nil + }) +} + func PrintMigrations(items ...MigrationItem) ([]byte, error) { p := newPrinter() p.p("-- +goose Up") diff --git a/pquery/pgmigrate/search.go b/pquery/pgmigrate/search.go new file mode 100644 index 0000000..6189981 --- /dev/null +++ b/pquery/pgmigrate/search.go @@ -0,0 +1,127 @@ +package pgmigrate + +import ( + "context" + "fmt" + "strings" + + sq "github.com/elgris/sqrl" + "github.com/pentops/j5/lib/j5schema" + "github.com/pentops/protostate/pquery" + "github.com/pentops/sqrlx.go/sqrlx" +) + +func IndexMigrations(spec pquery.TableSpec) ([]MigrationItem, error) { + allMigrations := make([]MigrationItem, 0) + indexes, err := buildIndexes(spec.TableName, spec.DataColumn, spec.RootObject) + if err != nil { + return nil, fmt.Errorf("building indexes: %w", err) + } + + for _, index := range indexes { + allMigrations = append(allMigrations, index) + } + + return allMigrations, nil +} + +func AddIndexes(ctx context.Context, conn sqrlx.Connection, specs ...pquery.TableSpec) error { + allIndexes := make([]searchSpec, 0) + for _, spec := range specs { + indexes, err := buildIndexes(spec.TableName, spec.DataColumn, spec.RootObject) + if err != nil { + return err + } + allIndexes = append(allIndexes, indexes...) + } + + return writeIndexes(ctx, conn, allIndexes) +} + +type searchSpec struct { + tsvColumn string + tableName string + columnName string + path pquery.Path +} + +func buildIndexes(tableName string, columnName string, rootType *j5schema.ObjectSchema) ([]searchSpec, error) { + + cols, err := pquery.TSVColumns(rootType) + if err != nil { + return nil, err + } + + specs := []searchSpec{} + + for _, col := range cols { + specs = append(specs, searchSpec{ + tsvColumn: col.ColumnName, + tableName: tableName, + columnName: columnName, + path: col.Path, + }) + + } + + return specs, nil + +} + +func (ss searchSpec) ToSQL() (string, error) { + statement := fmt.Sprintf("to_tsvector('english', jsonb_path_query_array(%s, '%s'))", ss.columnName, ss.path.JSONPathQuery()) + + lines := []string{} + + lines = append(lines, fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s tsvector GENERATED ALWAYS", ss.tableName, ss.tsvColumn)) + lines = append(lines, fmt.Sprintf(" AS (%s) STORED;", statement)) + lines = append(lines, "") + lines = append(lines, fmt.Sprintf("CREATE INDEX %s_%s_idx ON %s USING GIN (%s);", ss.tableName, ss.tsvColumn, ss.tableName, ss.tsvColumn)) + return strings.Join(lines, "\n"), nil + +} + +func (ss searchSpec) DownSQL() (string, error) { + return fmt.Sprintf("DROP INDEX %s_%s_idx;\nALTER TABLE %s DROP COLUMN %s;", ss.tableName, ss.tsvColumn, ss.tableName, ss.tsvColumn), nil +} + +func writeIndexes(ctx context.Context, conn sqrlx.Connection, specs []searchSpec) error { + + db, err := sqrlx.New(conn, sq.Dollar) + if err != nil { + return err + } + + return db.Transact(ctx, nil, func(ctx context.Context, tx sqrlx.Transaction) error { + for _, spec := range specs { + + var count int + + err := tx.QueryRow(ctx, sq.Select("COUNT(column_name)"). + From("information_schema.columns"). + Where("table_schema = CURRENT_SCHEMA"). + Where(sq.Eq{"table_name": spec.tableName, "column_name": spec.tsvColumn})).Scan(&count) + if err != nil { + return err + } + if count > 0 { + return nil + } + + statement := fmt.Sprintf("to_tsvector('english', jsonb_path_query_array(%s, '%s'))", spec.columnName, spec.path.JSONPathQuery()) + + _, err = tx.ExecRaw(ctx, fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s tsvector GENERATED ALWAYS AS (%s) STORED;", spec.tableName, spec.tsvColumn, statement)) + if err != nil { + return err + } + + _, err = tx.ExecRaw(ctx, fmt.Sprintf("CREATE INDEX %s_%s_idx ON %s USING GIN (%s);", spec.tableName, spec.tsvColumn, spec.tableName, spec.tsvColumn)) + if err != nil { + return err + } + + } + return nil + }) + +} diff --git a/psm/state_query.go b/psm/state_query.go index 00d8b57..cadfe2a 100644 --- a/psm/state_query.go +++ b/psm/state_query.go @@ -17,6 +17,22 @@ type QueryTableSpec struct { TableMap } +func (ts *QueryTableSpec) StateTable() pquery.TableSpec { + return pquery.TableSpec{ + TableName: ts.State.TableName, + DataColumn: ts.State.Root.ColumnName, + RootObject: ts.StateType, + } +} + +func (ts *QueryTableSpec) EventTable() pquery.TableSpec { + return pquery.TableSpec{ + TableName: ts.Event.TableName, + DataColumn: ts.Event.Root.ColumnName, + RootObject: ts.EventType, + } +} + // QuerySpec is the configuration for the query service side of the state // machine. Can be partially derived from the state machine table spec, but // contains types relating to the query service so cannot be fully derived. @@ -248,6 +264,7 @@ func BuildStateQuerySet( TableSpec: pquery.TableSpec{ TableName: smSpec.State.TableName, DataColumn: smSpec.State.Root.ColumnName, + RootObject: smSpec.StateType, FallbackSortColumns: statePrimaryKeys, Auth: getSpec.Auth, AuthJoin: getSpec.AuthJoin, @@ -274,6 +291,7 @@ func BuildStateQuerySet( TableSpec: pquery.TableSpec{ TableName: smSpec.Event.TableName, DataColumn: smSpec.Event.Root.ColumnName, + RootObject: smSpec.EventType, Auth: getSpec.Auth, AuthJoin: getSpec.AuthJoin, FallbackSortColumns: []pquery.ProtoField{ diff --git a/psmigrate/migrate.go b/psmigrate/migrate.go index dde64e6..f16cf9e 100644 --- a/psmigrate/migrate.go +++ b/psmigrate/migrate.go @@ -1,10 +1,10 @@ package psmigrate import ( - "github.com/pentops/protostate/internal/pgstore/pgmigrate" + "github.com/pentops/protostate/internal/pgstore/psmpg" "github.com/pentops/protostate/psm" ) func BuildStateMachineMigrations(specs ...psm.QueryTableSpec) ([]byte, error) { - return pgmigrate.BuildStateMachineMigrations(specs...) + return psmpg.BuildStateMachineMigrations(specs...) } From 9e4837ef756a64462767785b9d5811086bfec965 Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Tue, 29 Jul 2025 16:52:10 -0700 Subject: [PATCH 10/12] Remove pquery, to j5 --- go.mod | 10 +- go.sum | 2 - internal/pgstore/psmpg/psm.go | 2 +- internal/protogen/query/parse.go | 19 +- pquery/filter.go | 555 ----------- pquery/getter.go | 356 ------- pquery/internal/integration/filter_test.go | 903 ------------------ .../internal/integration/pagination_test.go | 142 --- pquery/internal/integration/search_test.go | 68 -- pquery/internal/integration/sort_test.go | 624 ------------ pquery/internal/integration/token.go | 50 - pquery/internal/integration/universe.go | 203 ---- pquery/lister.go | 891 ----------------- pquery/lister_test.go | 544 ----------- pquery/method.go | 30 - pquery/path.go | 410 -------- pquery/path_test.go | 136 --- pquery/pgmigrate/builder.go | 221 ----- pquery/pgmigrate/migrations.go | 93 -- pquery/pgmigrate/search.go | 127 --- pquery/query.go | 41 - pquery/search.go | 264 ----- pquery/search_test.go | 264 ----- pquery/sort.go | 189 ---- pquery/types.go | 1 - psm/state_query.go | 2 +- psm/table_map.go | 2 +- 27 files changed, 19 insertions(+), 6130 deletions(-) delete mode 100644 pquery/filter.go delete mode 100644 pquery/getter.go delete mode 100644 pquery/internal/integration/filter_test.go delete mode 100644 pquery/internal/integration/pagination_test.go delete mode 100644 pquery/internal/integration/search_test.go delete mode 100644 pquery/internal/integration/sort_test.go delete mode 100644 pquery/internal/integration/token.go delete mode 100644 pquery/internal/integration/universe.go delete mode 100644 pquery/lister.go delete mode 100644 pquery/lister_test.go delete mode 100644 pquery/method.go delete mode 100644 pquery/path.go delete mode 100644 pquery/path_test.go delete mode 100644 pquery/pgmigrate/builder.go delete mode 100644 pquery/pgmigrate/migrations.go delete mode 100644 pquery/pgmigrate/search.go delete mode 100644 pquery/query.go delete mode 100644 pquery/search.go delete mode 100644 pquery/search_test.go delete mode 100644 pquery/sort.go delete mode 100644 pquery/types.go diff --git a/go.mod b/go.mod index b8abf7e..22a45b3 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,6 @@ require ( github.com/elgris/sqrl v0.0.0-20210727210741-7e0198b30236 github.com/google/uuid v1.6.0 github.com/iancoleman/strcase v0.3.0 - github.com/lib/pq v1.10.9 github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21 github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 github.com/pentops/j5 v0.0.0-20250729201549-91d43d72f0bc @@ -17,7 +16,6 @@ require ( github.com/pentops/o5-messaging v0.0.0-20250619024104-7e07c29129f0 github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b github.com/pentops/sqrlx.go v0.0.0-20250520210217-2f46de329c7a - github.com/shopspring/decimal v1.4.0 github.com/stretchr/testify v1.10.0 google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 google.golang.org/grpc v1.72.2 @@ -32,21 +30,21 @@ require ( github.com/bufbuild/protocompile v0.14.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.18.0 // indirect - github.com/golang/protobuf v1.5.4 // indirect github.com/google/cel-go v0.25.0 // indirect - github.com/google/go-cmp v0.7.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect - github.com/jhump/protoreflect v1.17.0 // indirect + github.com/lib/pq v1.10.9 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pressly/goose v2.7.0+incompatible // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/stoewer/go-strcase v1.3.0 // indirect golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b // indirect golang.org/x/net v0.40.0 // indirect - golang.org/x/sync v0.14.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/text v0.25.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect ) + +replace github.com/pentops/j5 => /Users/daemonl/pentops/j5 diff --git a/go.sum b/go.sum index 80eeb49..ff801f4 100644 --- a/go.sum +++ b/go.sum @@ -77,8 +77,6 @@ github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21 h1:EIMCYtccNxV59a github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21/go.mod h1:vNp8crAKcH0f/sZU9frkmQLUeDsTIgMqV14kQtkAqC0= github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 h1:s5qtWT2/s79gy/wm3/bwvKYLK6u2AkW05JiLPqxraP0= github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63/go.mod h1:I58JIVvL1/nP4CEHGKGbBhvWIEA9mVkGeoviemaqanU= -github.com/pentops/j5 v0.0.0-20250729201549-91d43d72f0bc h1:ERDIagXUpaz9A7vaNU12U6ErIz9la9lqEUUYX0Uer04= -github.com/pentops/j5 v0.0.0-20250729201549-91d43d72f0bc/go.mod h1:i+eCdoCoDvULbEuPeoQm4q1YLw13oaJIlYMB+kaX6fM= github.com/pentops/log.go v0.0.16 h1:oxCuHSBOBPjfUVSXyOSEEdYUwytysj4T29/7T2FBp9Q= github.com/pentops/log.go v0.0.16/go.mod h1:yR34x8aMlvhdGvqgIU4+0MiLjJTKt0vpcgUnVN2nZV4= github.com/pentops/o5-messaging v0.0.0-20250619024104-7e07c29129f0 h1:aAt5rIhzFyF1RdT262LGHL0V4vBxcGCTWIeC1C0vZ20= diff --git a/internal/pgstore/psmpg/psm.go b/internal/pgstore/psmpg/psm.go index 052aab4..cf015c5 100644 --- a/internal/pgstore/psmpg/psm.go +++ b/internal/pgstore/psmpg/psm.go @@ -5,7 +5,7 @@ import ( "fmt" sq "github.com/elgris/sqrl" - "github.com/pentops/protostate/pquery/pgmigrate" + "github.com/pentops/j5/lib/j5query/pgmigrate" "github.com/pentops/protostate/psm" "github.com/pentops/sqrlx.go/sqrlx" ) diff --git a/internal/protogen/query/parse.go b/internal/protogen/query/parse.go index bbc8283..7d0cfb9 100644 --- a/internal/protogen/query/parse.go +++ b/internal/protogen/query/parse.go @@ -7,8 +7,8 @@ import ( "github.com/iancoleman/strcase" "github.com/pentops/j5/gen/j5/ext/v1/ext_j5pb" + pquery "github.com/pentops/j5/lib/j5query" "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/protostate/pquery" "github.com/pentops/protostate/psm" "google.golang.org/protobuf/compiler/protogen" "google.golang.org/protobuf/proto" @@ -35,20 +35,25 @@ func WalkFile(file *protogen.File) ([]*PSMQuerySet, error) { methodSet := NewQueryServiceGenerateSet(stateQuery.Entity, service) for _, method := range service.Methods { + var stateQuery *ext_j5pb.StateQueryMethodOptions methodOpt := proto.GetExtension(method.Desc.Options(), ext_j5pb.E_Method).(*ext_j5pb.MethodOptions) - if methodOpt == nil { + if methodOpt != nil { + stateQuery = methodOpt.GetStateQuery() + if stateQuery == nil { + return nil, fmt.Errorf("method %s does not have a state query type", method.Desc.Name()) + } + } else { name := string(method.Desc.Name()) - methodOpt := &ext_j5pb.MethodOptions{} if strings.HasPrefix(name, "Get") { - methodOpt.StateQuery = &ext_j5pb.StateQueryMethodOptions{ + stateQuery = &ext_j5pb.StateQueryMethodOptions{ Get: true, } } else if strings.HasPrefix(name, "List") && strings.HasSuffix(name, "Events") { - methodOpt.StateQuery = &ext_j5pb.StateQueryMethodOptions{ + stateQuery = &ext_j5pb.StateQueryMethodOptions{ ListEvents: true, } } else if strings.HasPrefix(name, "List") { - methodOpt.StateQuery = &ext_j5pb.StateQueryMethodOptions{ + stateQuery = &ext_j5pb.StateQueryMethodOptions{ List: true, } } else { @@ -56,7 +61,7 @@ func WalkFile(file *protogen.File) ([]*PSMQuerySet, error) { } } - if err := methodSet.AddMethod(method, methodOpt.StateQuery); err != nil { + if err := methodSet.AddMethod(method, stateQuery); err != nil { return nil, fmt.Errorf("adding method %s to %s: %w", method.Desc.Name(), service.Desc.FullName(), err) } } diff --git a/pquery/filter.go b/pquery/filter.go deleted file mode 100644 index 5e58efa..0000000 --- a/pquery/filter.go +++ /dev/null @@ -1,555 +0,0 @@ -package pquery - -import ( - "fmt" - "regexp" - "strconv" - "time" - - sq "github.com/elgris/sqrl" - "github.com/elgris/sqrl/pg" - "github.com/google/uuid" - "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" - "github.com/pentops/j5/lib/id62" - "github.com/pentops/j5/lib/j5schema" - "github.com/shopspring/decimal" - "google.golang.org/protobuf/types/known/timestamppb" -) - -type filterSpec struct { - *NestedField - filterVals []any -} - -func filtersForField(field *j5schema.ObjectProperty) ([]any, error) { - - switch bigType := field.Schema.(type) { - case *j5schema.EnumField: - schema := bigType.Schema() - - if bigType.ListRules == nil || bigType.ListRules.Filtering == nil || !bigType.ListRules.Filtering.Filterable { - return nil, nil - } - - vals := []any{} - for _, val := range bigType.ListRules.Filtering.DefaultFilters { - option := schema.OptionByName(val) - if option == nil { - return nil, fmt.Errorf("default filter value '%s' not found in enum '%s'", val, field.JSONName) - } - - vals = append(vals, option.Name()) - } - - return vals, nil - - case *j5schema.OneofField: - if bigType.ListRules == nil || bigType.ListRules.Filtering == nil || !bigType.ListRules.Filtering.Filterable { - return nil, nil - } - - vals := []any{} - for _, val := range bigType.ListRules.Filtering.DefaultFilters { - // The value is the name of the set field - vals = append(vals, val) - } - - return vals, nil - - case *j5schema.ScalarSchema: - j5Field := field.Schema.ToJ5Field() - - switch st := j5Field.Type.(type) { - case *schema_j5pb.Field_Float: - - if st.Float.ListRules == nil || st.Float.ListRules.Filtering == nil || !st.Float.ListRules.Filtering.Filterable { - return nil, nil - } - - var size int - switch st.Float.Format { - case schema_j5pb.FloatField_FORMAT_FLOAT32: - size = 32 - case schema_j5pb.FloatField_FORMAT_FLOAT64: - size = 64 - default: - return nil, fmt.Errorf("unknown float format for default filter (%s): %s", field.JSONName, st.Float.Format) - } - - vals := []any{} - for _, val := range st.Float.ListRules.Filtering.DefaultFilters { - - v, err := strconv.ParseFloat(val, size) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) - } - - vals = append(vals, v) - } - - return vals, nil - - case *schema_j5pb.Field_Integer: - if st.Integer.ListRules == nil || st.Integer.ListRules.Filtering == nil || !st.Integer.ListRules.Filtering.Filterable { - return nil, nil - } - - vals := []any{} - for _, val := range st.Integer.ListRules.Filtering.DefaultFilters { - var v any - var err error - - switch st.Integer.Format { - case schema_j5pb.IntegerField_FORMAT_INT32: - v, err = strconv.ParseInt(val, 10, 32) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) - } - - case schema_j5pb.IntegerField_FORMAT_INT64: - v, err = strconv.ParseInt(val, 10, 64) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) - } - - case schema_j5pb.IntegerField_FORMAT_UINT32: - v, err = strconv.ParseUint(val, 10, 32) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) - } - - case schema_j5pb.IntegerField_FORMAT_UINT64: - v, err = strconv.ParseUint(val, 10, 64) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) - } - - default: - return nil, fmt.Errorf("unknown integer format for default filter (%s): %s", field.JSONName, st.Integer.Format) - } - - vals = append(vals, v) - - } - return vals, nil - - case *schema_j5pb.Field_Bool: - if st.Bool.ListRules == nil || st.Bool.ListRules.Filtering == nil || !st.Bool.ListRules.Filtering.Filterable { - return nil, nil - } - vals := []any{} - for _, val := range st.Bool.ListRules.Filtering.DefaultFilters { - v, err := strconv.ParseBool(val) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) - } - - vals = append(vals, v) - } - return vals, nil - - case *schema_j5pb.Field_Key: - - if st.Key.ListRules == nil || st.Key.ListRules.Filtering == nil || !st.Key.ListRules.Filtering.Filterable { - return nil, nil - } - - vals := []any{} - for _, val := range st.Key.ListRules.Filtering.DefaultFilters { - vals = append(vals, val) - } - return vals, nil - - case *schema_j5pb.Field_Timestamp: - if st.Timestamp.ListRules == nil || st.Timestamp.ListRules.Filtering == nil || !st.Timestamp.ListRules.Filtering.Filterable { - return nil, nil - } - - vals := []any{} - for _, val := range st.Timestamp.ListRules.Filtering.DefaultFilters { - t, err := time.Parse(time.RFC3339, val) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) - } - vals = append(vals, timestamppb.New(t)) - } - return vals, nil - - case *schema_j5pb.Field_Date: - if st.Date.ListRules == nil || st.Date.ListRules.Filtering == nil || !st.Date.ListRules.Filtering.Filterable { - return nil, nil - } - vals := []any{} - for _, val := range st.Date.ListRules.Filtering.DefaultFilters { - t, err := time.Parse(time.DateOnly, val) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) - } - vals = append(vals, t) - } - return vals, nil - - case *schema_j5pb.Field_Decimal: - if st.Decimal.ListRules == nil || st.Decimal.ListRules.Filtering == nil || !st.Decimal.ListRules.Filtering.Filterable { - return nil, nil - } - - vals := []any{} - for _, val := range st.Decimal.ListRules.Filtering.DefaultFilters { - v, err := decimal.NewFromString(val) - if err != nil { - return nil, fmt.Errorf("error parsing default filter (%s): %w", field.JSONName, err) - } - vals = append(vals, v) - } - return vals, nil - case *schema_j5pb.Field_String_: - // no filters definable - return nil, nil - case *schema_j5pb.Field_Bytes: - return nil, nil - - default: - return nil, fmt.Errorf("unknown scalar type for default filter (%s): %T", field.JSONName, st) - } - case *j5schema.ObjectField: - // none - return nil, nil - case *j5schema.AnyField: - // none - return nil, nil - case *j5schema.MapField: - // none - return nil, nil - case *j5schema.ArrayField: - // none - return nil, nil - - default: - return nil, fmt.Errorf("unknown field type for filter rules %T", bigType) - } -} - -func buildDefaultFilters(columnName string, message *j5schema.ObjectSchema) ([]filterSpec, error) { - var filters []filterSpec - - err := WalkPathNodes(message, func(path Path) error { - field := path.LeafField() - if field == nil { - return nil - } - - vals, err := filtersForField(field) - if err != nil { - return fmt.Errorf("filters for field: %w", err) - } - - if len(vals) > 0 { - filters = append(filters, filterSpec{ - NestedField: &NestedField{ - Path: path, - RootColumn: columnName, - }, - filterVals: vals, - }) - } - return nil - }) - if err != nil { - return nil, fmt.Errorf("walk path nodes: %w", err) - } - - return filters, nil -} - -func (ll *ListReflectionSet) buildDynamicFilter(tableAlias string, filters []*list_j5pb.Filter) ([]sq.Sqlizer, error) { - out := []sq.Sqlizer{} - - for i := range filters { - switch filters[i].GetType().(type) { - case *list_j5pb.Filter_Field: - pathSpec := ParseJSONPathSpec(filters[i].GetField().GetName()) - spec, err := NewJSONPath(ll.arrayObject, pathSpec) - if err != nil { - return nil, fmt.Errorf("dynamic filter: find field: %w", err) - } - - biggerSpec := &NestedField{ - Path: *spec, - RootColumn: ll.dataColumn, - } - - o, err := ll.buildDynamicFilterField(tableAlias, biggerSpec, filters[i]) - if err != nil { - return nil, fmt.Errorf("dynamic filter: build field: %w", err) - } - - out = append(out, o) - case *list_j5pb.Filter_And: - f, err := ll.buildDynamicFilter(tableAlias, filters[i].GetAnd().GetFilters()) - if err != nil { - return nil, fmt.Errorf("dynamic filter: and: %w", err) - } - and := sq.And{} - and = append(and, f...) - - out = append(out, and) - case *list_j5pb.Filter_Or: - f, err := ll.buildDynamicFilter(tableAlias, filters[i].GetOr().GetFilters()) - if err != nil { - return nil, fmt.Errorf("dynamic filter: or: %w", err) - } - or := sq.Or{} - or = append(or, f...) - - out = append(out, or) - } - } - - return out, nil -} - -func (ll *ListReflectionSet) buildDynamicFilterField(tableAlias string, spec *NestedField, filter *list_j5pb.Filter) (sq.Sqlizer, error) { - var out sq.And - - if filter.GetField() == nil { - return nil, fmt.Errorf("dynamic filter: field is nil") - } - - switch ft := filter.GetField().GetType().Type.(type) { - case *list_j5pb.FieldType_Value: - val := ft.Value - - switch schema := spec.Path.LeafField().Schema.(type) { - case *j5schema.EnumField: - option := schema.Schema().OptionByName(val) - if option == nil { - return nil, fmt.Errorf("enum value '%s' not found in field '%s'", val, spec.Path.LeafField().JSONName) - } - val = option.Name() // Use the name of the option, not the value - } - - out = sq.And{sq.Expr( - fmt.Sprintf("jsonb_path_query_array(%s.%s, '%s') @> ?", - tableAlias, - spec.RootColumn, - spec.Path.JSONPathQuery(), - ), pg.JSONB(val))} - - case *list_j5pb.FieldType_Range: - min := ft.Range.GetMin() - max := ft.Range.GetMax() - - switch { - case min != "" && max != "": - exprStr := fmt.Sprintf("jsonb_path_query_array(%s.%s, '%s ?? (@ >= $min && @ <= $max)', jsonb_build_object('min', ?::text, 'max', ?::text)) <> '[]'::jsonb", tableAlias, spec.RootColumn, spec.Path.JSONPathQuery()) - out = sq.And{sq.Expr(exprStr, min, max)} - case min != "": - exprStr := fmt.Sprintf("jsonb_path_query_array(%s.%s, '%s ?? (@ >= $min)', jsonb_build_object('min', ?::text)) <> '[]'::jsonb", tableAlias, spec.RootColumn, spec.Path.JSONPathQuery()) - out = sq.And{sq.Expr(exprStr, min)} - case max != "": - exprStr := fmt.Sprintf("jsonb_path_query_array(%s.%s, '%s ?? (@ <= $max)', jsonb_build_object('max', ?::text)) <> '[]'::jsonb", tableAlias, spec.RootColumn, spec.Path.JSONPathQuery()) - out = sq.And{sq.Expr(exprStr, max)} - } - } - - return out, nil -} - -func validateQueryRequestFilters(message *j5schema.ObjectSchema, filters []*list_j5pb.Filter) error { - for i := range filters { - switch filters[i].GetType().(type) { - case *list_j5pb.Filter_Field: - return validateQueryRequestFilterField(message, filters[i].GetField()) - case *list_j5pb.Filter_And: - return validateQueryRequestFilters(message, filters[i].GetAnd().GetFilters()) - case *list_j5pb.Filter_Or: - return validateQueryRequestFilters(message, filters[i].GetOr().GetFilters()) - } - } - - return nil -} - -func validateQueryRequestFilterField(message *j5schema.ObjectSchema, filterField *list_j5pb.Field) error { - - jsonPath := ParseJSONPathSpec(filterField.GetName()) - spec, err := NewJSONPath(message, jsonPath) - if err != nil { - return fmt.Errorf("find field: %w", err) - } - - switch filterField.Type.Type.(type) { - case *list_j5pb.FieldType_Value: - err := validateFilterFieldValue(spec, filterField.Type.GetValue()) - if err != nil { - return fmt.Errorf("filter value: %w", err) - } - case *list_j5pb.FieldType_Range: - err := validateFilterFieldValue(spec, filterField.Type.GetRange().GetMin()) - if err != nil { - return fmt.Errorf("filter min value: %w", err) - } - - err = validateFilterFieldValue(spec, filterField.Type.GetRange().GetMax()) - if err != nil { - return fmt.Errorf("filter max value: %w", err) - } - } - - return nil -} - -func validateFilterFieldValue(path *Path, value string) error { - if value == "" { - return nil - } - - prop := path.LeafField() - - switch bigType := prop.Schema.(type) { - case *j5schema.EnumField: - schema := bigType.Schema() - if bigType.ListRules == nil || bigType.ListRules.Filtering == nil || !bigType.ListRules.Filtering.Filterable { - return fmt.Errorf("enum field '%s' is not filterable", prop.JSONName) - } - if schema.OptionByName(value) == nil { - return fmt.Errorf("enum value '%s' is not a valid option for field '%s'", value, prop.JSONName) - } - case *j5schema.OneofField: - if bigType.ListRules == nil || bigType.ListRules.Filtering == nil || !bigType.ListRules.Filtering.Filterable { - return fmt.Errorf("oneof field %q is not filterable", prop.FullName()) - } - oneof := bigType.OneofSchema() - if oneof.Properties.ByJSONName(value) == nil { - return fmt.Errorf("oneof value '%s' is not a valid option for field '%s'", value, prop.JSONName) - } - - case *j5schema.ScalarSchema: - j5Field := bigType.ToJ5Field() - switch st := j5Field.Type.(type) { - case *schema_j5pb.Field_Float: - if st.Float.ListRules == nil || st.Float.ListRules.Filtering == nil || !st.Float.ListRules.Filtering.Filterable { - return fmt.Errorf("float field '%s' is not filterable", prop.JSONName) - } - switch st.Float.Format { - case schema_j5pb.FloatField_FORMAT_FLOAT32: - _, err := strconv.ParseFloat(value, 32) - if err != nil { - return fmt.Errorf("parsing float32 value '%s' for field '%s': %w", value, prop.JSONName, err) - } - case schema_j5pb.FloatField_FORMAT_FLOAT64: - _, err := strconv.ParseFloat(value, 64) - if err != nil { - return fmt.Errorf("parsing float64 value '%s' for field '%s': %w", value, prop.JSONName, err) - } - default: - return fmt.Errorf("unknown float format '%s' for field '%s'", st.Float.Format, prop.JSONName) - } - case *schema_j5pb.Field_Integer: - if st.Integer.ListRules == nil || st.Integer.ListRules.Filtering == nil || !st.Integer.ListRules.Filtering.Filterable { - return fmt.Errorf("integer field '%s' is not filterable", prop.JSONName) - } - switch st.Integer.Format { - case schema_j5pb.IntegerField_FORMAT_INT32: - _, err := strconv.ParseInt(value, 10, 32) - if err != nil { - return fmt.Errorf("parsing int32 value '%s' for field '%s': %w", value, prop.JSONName, err) - } - case schema_j5pb.IntegerField_FORMAT_INT64: - _, err := strconv.ParseInt(value, 10, 64) - if err != nil { - return fmt.Errorf("parsing int64 value '%s' for field '%s': %w", value, prop.JSONName, err) - } - case schema_j5pb.IntegerField_FORMAT_UINT32: - _, err := strconv.ParseUint(value, 10, 32) - if err != nil { - return fmt.Errorf("parsing uint32 value '%s' for field '%s': %w", value, prop.JSONName, err) - } - case schema_j5pb.IntegerField_FORMAT_UINT64: - _, err := strconv.ParseUint(value, 10, 64) - if err != nil { - return fmt.Errorf("parsing uint64 value '%s' for field '%s': %w", value, prop.JSONName, err) - } - default: - return fmt.Errorf("unknown integer format '%s' for field '%s'", st.Integer.Format, prop.JSONName) - } - case *schema_j5pb.Field_Bool: - if st.Bool.ListRules == nil || st.Bool.ListRules.Filtering == nil || !st.Bool.ListRules.Filtering.Filterable { - return fmt.Errorf("bool field '%s' is not filterable", prop.JSONName) - } - _, err := strconv.ParseBool(value) - if err != nil { - return fmt.Errorf("parsing bool value '%s' for field '%s': %w", value, prop.JSONName, err) - } - case *schema_j5pb.Field_Key: - if st.Key.ListRules == nil || st.Key.ListRules.Filtering == nil || !st.Key.ListRules.Filtering.Filterable { - return fmt.Errorf("key field '%s' is not filterable", prop.JSONName) - } - switch ft := st.Key.Format.Type.(type) { - case *schema_j5pb.KeyFormat_Uuid: - if _, err := uuid.Parse(value); err != nil { - return fmt.Errorf("parsing uuid value '%s' for field '%s': %w", value, prop.JSONName, err) - } - - case *schema_j5pb.KeyFormat_Informal_: - // pass - case *schema_j5pb.KeyFormat_Id62: - if _, err := id62.Parse(value); err != nil { - return fmt.Errorf("parsing id62 value '%s' for field '%s': %w", value, prop.JSONName, err) - } - - case *schema_j5pb.KeyFormat_Custom_: - re, err := regexp.Compile(ft.Custom.Pattern) - if err != nil { - return fmt.Errorf("parsing custom key regex '%s' for field '%s': %w", ft.Custom.Pattern, prop.JSONName, err) - } - if !re.MatchString(value) { - return fmt.Errorf("value '%s' for field '%s' does not match custom key regex '%s'", value, prop.JSONName, ft.Custom.Pattern) - } - - default: - return fmt.Errorf("unknown key type for field '%s'", prop.JSONName) - } - case *schema_j5pb.Field_Timestamp: - if st.Timestamp.ListRules == nil || st.Timestamp.ListRules.Filtering == nil || !st.Timestamp.ListRules.Filtering.Filterable { - return fmt.Errorf("timestamp field '%s' is not filterable", prop.JSONName) - } - _, err := time.Parse(time.RFC3339, value) - if err != nil { - return fmt.Errorf("parsing timestamp value '%s' for field '%s': %w", value, prop.JSONName, err) - } - case *schema_j5pb.Field_Date: - if st.Date.ListRules == nil || st.Date.ListRules.Filtering == nil || !st.Date.ListRules.Filtering.Filterable { - return fmt.Errorf("date field '%s' is not filterable", prop.JSONName) - } - _, err := time.Parse("2006-01-02", value) - if err != nil { - _, err = time.Parse("2006-01", value) - if err != nil { - _, err = time.Parse("2006", value) - if err != nil { - return fmt.Errorf("parsing date value '%s' for field '%s': %w", value, prop.JSONName, err) - } - } - } - case *schema_j5pb.Field_Decimal: - if st.Decimal.ListRules == nil || st.Decimal.ListRules.Filtering == nil || !st.Decimal.ListRules.Filtering.Filterable { - return fmt.Errorf("decimal field '%s' is not filterable", prop.JSONName) - } - _, err := decimal.NewFromString(value) - if err != nil { - return fmt.Errorf("parsing decimal value '%s' for field '%s': %w", value, prop.JSONName, err) - } - case *schema_j5pb.Field_String_: - return fmt.Errorf("string field '%s' is not filterable", prop.JSONName) - default: - return fmt.Errorf("unknown scalar type for field '%s': %T", prop.JSONName, st) - } - default: - // If we don't know the type, we assume it's not filterable - return fmt.Errorf("unknown field type for filter validation: %T", bigType) - } - return nil -} diff --git a/pquery/getter.go b/pquery/getter.go deleted file mode 100644 index 779ca38..0000000 --- a/pquery/getter.go +++ /dev/null @@ -1,356 +0,0 @@ -package pquery - -import ( - "context" - "database/sql" - "errors" - "fmt" - "strings" - - sq "github.com/elgris/sqrl" - "github.com/lib/pq" - "github.com/pentops/j5/lib/j5codec" - "github.com/pentops/j5/lib/j5reflect" - "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/j5/lib/j5validate" - "github.com/pentops/protostate/internal/dbconvert" - "github.com/pentops/sqrlx.go/sqrlx" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -type GetRequest interface { - j5reflect.Object -} - -type GetResponse interface { - j5reflect.Object -} - -type GetSpec struct { - Method *j5schema.MethodSchema - - TableName string - DataColumn string - Auth AuthProvider - AuthJoin []*LeftJoin - - PrimaryKey func(j5reflect.Object) (map[string]any, error) - - StateResponseField string - - Join *GetJoinSpec -} - -// JoinConstraint defines a -// LEFT JOIN -// ON . = . -type JoinField struct { - JoinColumn string // The name of the column in the table being introduced - RootColumn string // The name of the column in the root table -} - -type JoinFields []JoinField - -func (jc JoinFields) Reverse() JoinFields { - out := make(JoinFields, 0, len(jc)) - for _, c := range jc { - out = append(out, JoinField{ - JoinColumn: c.RootColumn, - RootColumn: c.JoinColumn, - }) - } - return out -} - -func (jc JoinFields) SQL(rootAlias string, joinAlias string) string { - conditions := make([]string, 0, len(jc)) - for _, c := range jc { - conditions = append(conditions, fmt.Sprintf("%s.%s = %s.%s", - joinAlias, - c.JoinColumn, - rootAlias, - c.RootColumn, - )) - } - return strings.Join(conditions, " AND ") -} - -type GetJoinSpec struct { - TableName string - DataColumn string - On JoinFields - FieldInParent string -} - -func (gc GetJoinSpec) validate() error { - if gc.TableName == "" { - return fmt.Errorf("missing TableName") - } - if gc.DataColumn == "" { - return fmt.Errorf("missing DataColumn") - } - if gc.On == nil { - return fmt.Errorf("missing On") - } - - return nil -} - -type Getter struct { - method *j5schema.MethodSchema - - stateField *j5schema.ObjectProperty - - dataColumn string - tableName string - primaryKey func(j5reflect.Object) (map[string]any, error) - auth AuthProvider - authJoin []*LeftJoin - - queryLogger QueryLogger - - validator *j5validate.Validator - - join *getJoin -} - -type getJoin struct { - dataColumn string - tableName string - fieldInParent *j5schema.ObjectProperty // wraps the ListFooEventResponse type - on JoinFields -} - -func NewGetter(spec GetSpec) (*Getter, error) { - - if spec.Method == nil { - return nil, fmt.Errorf("missing Method") - } - - sc := &Getter{ - method: spec.Method, - dataColumn: spec.DataColumn, - tableName: spec.TableName, - primaryKey: spec.PrimaryKey, - auth: spec.Auth, - authJoin: spec.AuthJoin, - } - - // TODO: Use an annotation not a passed in name - defaultState := false - if spec.StateResponseField == "" { - defaultState = true - spec.StateResponseField = "state" - } - sc.stateField = spec.Method.Response.Properties.ByJSONName(spec.StateResponseField) - if sc.stateField == nil { - if defaultState { - return nil, fmt.Errorf("no 'state' field in proto message - did you mean to override StateResponseField?") - } - return nil, fmt.Errorf("no '%s' field in proto message", spec.StateResponseField) - } - - if spec.PrimaryKey == nil { - return nil, fmt.Errorf("missing PrimaryKey func") - } - - if spec.Join != nil { - - if err := spec.Join.validate(); err != nil { - return nil, fmt.Errorf("invalid join spec: %w", err) - } - - joinField := spec.Method.Response.Properties.ByJSONName(spec.Join.FieldInParent) - if joinField == nil { - return nil, fmt.Errorf("field %s not found in response message", spec.Join.FieldInParent) - } - - if _, ok := joinField.Schema.(*j5schema.ArrayField); !ok { - return nil, fmt.Errorf("field %s, in join spec, is not a list", spec.Join.FieldInParent) - } - - sc.join = &getJoin{ - tableName: spec.Join.TableName, - dataColumn: spec.Join.DataColumn, - fieldInParent: joinField, - on: spec.Join.On, - } - } - - sc.validator = j5validate.Global - - return sc, nil -} - -func (gc *Getter) SetQueryLogger(logger QueryLogger) { - gc.queryLogger = logger -} - -func (gc *Getter) Get(ctx context.Context, db Transactor, reqMsg, resMsg j5reflect.Object) error { - - err := assertObjectsMatch(gc.method, reqMsg, resMsg) - if err != nil { - return err - } - - as := newAliasSet() - rootAlias := as.Next(gc.tableName) - - if err := gc.validator.Validate(reqMsg); err != nil { - return err - } - - primaryKeyFields, err := gc.primaryKey(reqMsg) - if err != nil { - return err - } - - rootFilter, err := dbconvert.FieldsToEqMap(rootAlias, primaryKeyFields) - if err != nil { - return err - } - - selectQuery := sq. - Select(). - Column(fmt.Sprintf("%s.%s", rootAlias, gc.dataColumn)). - From(fmt.Sprintf("%s AS %s", gc.tableName, rootAlias)). - Where(rootFilter) - - for pkField := range rootFilter { - selectQuery.GroupBy(pkField) - } - - if gc.auth != nil { - authAlias := rootAlias - for _, join := range gc.authJoin { - priorAlias := authAlias - authAlias = as.Next(join.TableName) - selectQuery = selectQuery.LeftJoin(fmt.Sprintf( - "%s AS %s ON %s", - join.TableName, - authAlias, - join.On.SQL(priorAlias, authAlias), - )) - } - - authFilter, err := gc.auth.AuthFilter(ctx) - if err != nil { - return err - } - - if len(authFilter) > 0 { - claimFilter := map[string]any{} - for k, v := range authFilter { - claimFilter[fmt.Sprintf("%s.%s", authAlias, k)] = v - } - selectQuery.Where(claimFilter) - } - } - - if gc.join != nil { - joinAlias := as.Next(gc.join.tableName) - - selectQuery. - Column(fmt.Sprintf("ARRAY_AGG(%s.%s)", joinAlias, gc.join.dataColumn)). - LeftJoin(fmt.Sprintf( - "%s AS %s ON %s", - gc.join.tableName, - joinAlias, - gc.join.on.SQL(rootAlias, joinAlias), - )) - } - - var foundJSON []byte - var joinedJSON pq.StringArray - - if gc.queryLogger != nil { - gc.queryLogger(selectQuery) - } - - if err := db.Transact(ctx, &sqrlx.TxOptions{ - ReadOnly: true, - Retryable: true, - Isolation: sql.LevelReadCommitted, - }, func(ctx context.Context, tx sqrlx.Transaction) error { - row := tx.SelectRow(ctx, selectQuery) - - var err error - if gc.join != nil { - err = row.Scan(&foundJSON, &joinedJSON) - } else { - err = row.Scan(&foundJSON) - } - if err != nil { - return err - } - - return nil - }); err != nil { - - if errors.Is(err, sql.ErrNoRows) { - var pkDescription string - if len(primaryKeyFields) == 1 { - for _, v := range primaryKeyFields { - pkDescription = fmt.Sprintf("%v", v) - } - } else { - all := make([]string, 0, len(primaryKeyFields)) - for k, v := range primaryKeyFields { - all = append(all, fmt.Sprintf("%s=%v", k, v)) - } - pkDescription = strings.Join(all, ", ") - } - - return status.Errorf(codes.NotFound, "entity %s not found", pkDescription) - } - query, _, _ := selectQuery.ToSql() - - return fmt.Errorf("%s: %w", query, err) - } - - if foundJSON == nil { - return status.Error(codes.NotFound, "not found") - } - - stateMsg, err := resMsg.GetOrCreateValue(gc.stateField.JSONName) - if err != nil { - return err - } - - stateObj, ok := stateMsg.AsObject() - if !ok { - return fmt.Errorf("field %s in response message is not an object", gc.stateField.JSONName) - } - - err = j5codec.Global.JSONToReflect(foundJSON, stateObj) - if err != nil { - return err - } - - if gc.join != nil { - element, err := resMsg.GetOrCreateValue(gc.join.fieldInParent.JSONName) - if err != nil { - return err - } - elementList, ok := element.AsArrayOfContainer() - if !ok { - return fmt.Errorf("field %s in response message is not an array of containers", gc.join.fieldInParent.JSONName) - } - - for _, eventBytes := range joinedJSON { - if eventBytes == "" { - continue - } - - rowMessage, _ := elementList.NewContainerElement() - if err := j5codec.Global.JSONToReflect([]byte(eventBytes), rowMessage); err != nil { - return fmt.Errorf("joined json to reflect: %w", err) - } - } - - } - - return nil - -} diff --git a/pquery/internal/integration/filter_test.go b/pquery/internal/integration/filter_test.go deleted file mode 100644 index 21bcf92..0000000 --- a/pquery/internal/integration/filter_test.go +++ /dev/null @@ -1,903 +0,0 @@ -package integration - -import ( - "context" - "encoding/base64" - "fmt" - "testing" - "time" - - "github.com/google/uuid" - "github.com/pentops/flowtest" - "github.com/pentops/j5/gen/j5/auth/v1/auth_j5pb" - "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" - "github.com/pentops/protostate/pquery" - "google.golang.org/protobuf/proto" -) - -func TestDefaultFiltering(t *testing.T) { - uu := NewSchemaUniverse(t) - ss := NewStepper(t) - defer ss.RunSteps(t) - - var queryer *pquery.Lister - - ss.Setup(func(ctx context.Context, t flowtest.Asserter) error { - queryer = uu.FooLister(t) - uu.SetupFoo(t, 10, func(ii int, foo *TestObject) { - if ii < 2 { - foo.SetScalar(pquery.JSONPath("status"), "DELETED") - } - }) - return nil - }) - - ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(10), - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != 8 { - t.Fatalf("expected %d states, got %d", 8, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for _, state := range res.Foo { - if state.Status != test_pb.FooStatus_ACTIVE { - t.Fatalf("expected status %s, got %s", test_pb.FooStatus_ACTIVE, state.Status) - } - } - - if res.Page != nil { - t.Fatalf("page response should be empty") - } - }) -} - -func TestFilteringWithAuthScope(t *testing.T) { - uu := NewSchemaUniverse(t) - ss := NewStepper(t) - defer ss.RunSteps(t) - - var queryer *pquery.Lister - - tenantID1 := uuid.NewString() - tenantID2 := uuid.NewString() - - tenants := []string{tenantID1, tenantID2} - - ss.Setup(func(ctx context.Context, t flowtest.Asserter) error { - queryer = uu.FooLister(t) - uu.SetupFoo(t, 20, func(idx int, foo *TestObject) { - // recreating the old function logic - tenantId := idx % len(tenants) - tenantID := tenants[tenantId] - ii := idx / len(tenants) - ti := tenantId - - foo.SetScalar(pquery.JSONPath("tenantId"), tenantID) - - weight := ((10 + int64(ii)) * (int64(ti) + 1)) - height := ((50 - int64(ii)) * (int64(ti) + 1)) - length := (int64(ii%2) * (int64(ti) + 1)) - - foo.SetScalar(pquery.JSONPath("data", "characteristics", "weight"), weight) - foo.SetScalar(pquery.JSONPath("data", "characteristics", "height"), height) - foo.SetScalar(pquery.JSONPath("data", "characteristics", "length"), length) - createdAt := time.Now() - foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt.Format(time.RFC3339Nano)) - - }) - return nil - }) - - tkn := &token{ - claim: &auth_j5pb.Claim{ - TenantType: "tenant", - TenantId: tenantID1, - }, - } - - ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { - ctx = tkn.WithToken(ctx) - - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.characteristics.weight", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Min: "12", - Max: "15", - }, - }, - }, - }, - }, - }, - }, - }, - } - - res := &test_spb.FooListResponse{} - err := queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != 4 { - t.Fatalf("expected %d states, got %d", 4, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s (%s)", ii, state.Data.Field, state.Metadata.CreatedAt.AsTime().Format(time.RFC3339Nano)) - } - - for ii, state := range res.Foo { - if *state.Keys.TenantId != tenantID1 { - t.Errorf("expected tenant ID %s, got %s", tenantID1, state.Keys.TenantId) - } - if state.Data.Characteristics.Weight != int64(15-ii) { - t.Errorf("expected weight %d, got %d", 15-ii, state.Data.Characteristics.Weight) - } - - } - - if res.Page != nil { - t.Fatalf("page response should be empty") - } - - }) -} - -func TestDynamicFiltering(t *testing.T) { - uu := NewSchemaUniverse(t) - - queryer := uu.FooLister(t) - tenantID := uuid.NewString() - uu.SetupFoo(t, 60, func(ii int, foo *TestObject) { - weight := (10 + int64(ii)) - height := (50 - int64(ii)) - length := (int64(ii % 2)) - - foo.SetScalar(pquery.JSONPath("tenantId"), tenantID) - foo.SetScalar(pquery.JSONPath("data", "characteristics", "weight"), weight) - foo.SetScalar(pquery.JSONPath("data", "characteristics", "height"), height) - foo.SetScalar(pquery.JSONPath("data", "characteristics", "length"), length) - createdAt := time.Now() - foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt.Format(time.RFC3339Nano)) - - label := fmt.Sprintf("foo %d at %s (weighted %d, height %d, length %d)", ii, createdAt.Format(time.RFC3339Nano), weight, height, length) - foo.SetScalar(pquery.JSONPath("data", "field"), label) - - val, err := foo.GetOrCreateValue("data", "profiles") - if err != nil { - t.Fatalf("failed to get or create profiles: %v", err) - } - - objArray, ok := val.AsArrayOfObject() - if !ok { - t.Fatalf("expected profiles to be an array of objects, got %T", val) - } - - profile1, _ := objArray.NewObjectElement() - profile2, _ := objArray.NewObjectElement() - - if err := SetScalar(profile1, pquery.JSONPath("name"), fmt.Sprintf("profile %d", ii)); err != nil { - t.Fatalf("failed to set profile name: %v", err) - } - - if err := SetScalar(profile1, pquery.JSONPath("place"), int64(ii)+50); err != nil { - t.Fatalf("failed to set profile place: %v", err) - } - - if err := SetScalar(profile2, pquery.JSONPath("name"), fmt.Sprintf("profile %d", ii)); err != nil { - t.Fatalf("failed to set profile name: %v", err) - } - - if err := SetScalar(profile2, pquery.JSONPath("place"), int64(ii)+15); err != nil { - t.Fatalf("failed to set profile place: %v", err) - } - - switch ii { - case 10: - foo.SetScalar(pquery.JSONPath("data", "shape", "circle", "radius"), int64(10)) - case 11: - foo.SetScalar(pquery.JSONPath("data", "shape", "square", "side"), int64(10)) - } - }) - - t.Run("Single Range Filter", func(t *testing.T) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.characteristics.weight", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Min: "12", - Max: "15", - }, - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != 4 { - t.Fatalf("expected %d states, got %d", 4, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(15-ii) { - t.Fatalf("expected weight %d, got %d", 15-ii, state.Data.Characteristics.Weight) - } - } - - if res.Page != nil { - t.Fatalf("page response should be empty") - } - }) - - t.Run("Min Range Filter", func(t *testing.T) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.characteristics.weight", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Min: "12", - }, - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != 5 { - for _, foo := range res.Foo { - t.Logf("Foo: %s, Weight: %d", foo.Data.Field, foo.Data.Characteristics.Weight) - } - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for _, state := range res.Foo { - if state.Data.Characteristics.Weight < int64(12) { - t.Fatalf("expected weights greater than or equal to %d, got %d", 12, state.Data.Characteristics.Weight) - } - } - }) - - t.Run("Max Range Filter", func(t *testing.T) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.characteristics.weight", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Max: "15", - }, - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != 5 { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for _, state := range res.Foo { - if state.Data.Characteristics.Weight > int64(15) { - t.Fatalf("expected weight less than or equal to %d, got %d", 15, state.Data.Characteristics.Weight) - } - } - }) - - t.Run("Multi Range Filter", func(t *testing.T) { - nextToken := "" - query := &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Or{ - Or: &list_j5pb.Or{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.characteristics.weight", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Min: "12", - Max: "20", - }, - }, - }, - }, - }, - }, - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.characteristics.height", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Min: "16", - Max: "18", - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - } - - { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(10), - }, - Query: query, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - if len(res.Foo) != 10 { - t.Fatalf("expected %d states, got %d", 10, len(res.Foo)) - } - - for ii, state := range res.Foo[:3] { - if state.Data.Characteristics.Weight != int64(44-ii) { - t.Fatalf("expected weight %d, got %d", 44-ii, state.Data.Characteristics.Weight) - } - } - - for ii, state := range res.Foo[3:] { - if state.Data.Characteristics.Weight != int64(20-ii) { - t.Fatalf("expected weight %d, got %d", 20-ii, state.Data.Characteristics.Weight) - } - } - - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - - nextToken = pageResp.GetNextToken() - - } - - { - dec, err := base64.StdEncoding.DecodeString(nextToken) - if err != nil { - t.Fatalf("failed to decode next token: %v", err) - } - t.Log(string(dec)) - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(10), - Token: &nextToken, - }, - Query: query, - } - res := &test_spb.FooListResponse{} - - err = queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - if len(res.Foo) != 2 { - t.Fatalf("expected %d states, got %d", 2, len(res.Foo)) - } - - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(13-ii) { - t.Fatalf("expected weight %d, got %d", 13-ii, state.Data.Characteristics.Weight) - } - } - - if res.Page != nil { - t.Fatalf("page response should be empty") - } - } - }) - - t.Run("Flattened filterable fields", func(t *testing.T) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "tenantId", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: tenantID, - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err) - } - - if len(res.Foo) == 0 { - t.Fatalf("expected to receive results filtered by tenantId, but got none") - } - - for _, foo := range res.Foo { - if *foo.Keys.TenantId != tenantID { - t.Fatalf("expected tenantId %s, got %s", tenantID, *foo.Keys.TenantId) - } - } - }) - - t.Run("Non filterable fields", func(t *testing.T) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "foo_id", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: "d34d826f-afe3-410d-8326-4e9af3f09467", - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err == nil { - t.Fatalf("expected error, got nil") - } - }) - - t.Run("Enum values - Short", func(t *testing.T) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "status", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: "ACTIVE", - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err) - } - - if len(res.Foo) != 5 { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for _, state := range res.Foo { - if state.Status != test_pb.FooStatus_ACTIVE { - t.Fatalf("expected status %s, got %s", test_pb.FooStatus_ACTIVE, state.Status) - } - } - }) - - t.Run("Enum values - Full", func(t *testing.T) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "status", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: "FOO_STATUS_ACTIVE", - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err) - } - - if len(res.Foo) != 5 { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for _, state := range res.Foo { - if state.Status != test_pb.FooStatus_ACTIVE { - t.Fatalf("expected status %s, got %s", test_pb.FooStatus_ACTIVE, state.Status) - } - } - - }) - - t.Run("List Page bad enum name", func(t *testing.T) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "status", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: "FOO_STATUS_UNUSED", - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err == nil { - t.Fatal("expected error, got nil") - } - }) - - t.Run("Single Complex Range Filter", func(t *testing.T) { - nextToken := "" - { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.profiles.place", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Min: "15", - Max: "21", - }, - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Profiles) - } - - if len(res.Foo) != 5 { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for _, state := range res.Foo { - matched := false - for _, profile := range state.Data.Profiles { - if profile.Place >= 17 && profile.Place <= 21 { - matched = true - break - } - } - - if !matched { - t.Fatalf("expected at least one profile to match the filter") - } - } - - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - - nextToken = pageResp.GetNextToken() - } - - { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - Token: &nextToken, - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.profiles.place", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Range{ - Range: &list_j5pb.Range{ - Min: "15", - Max: "21", - }, - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Profiles) - } - - if len(res.Foo) != 2 { - t.Fatalf("expected %d states, got %d", 2, len(res.Foo)) - } - - for _, state := range res.Foo { - matched := false - for _, profile := range state.Data.Profiles { - if profile.Place >= 15 && profile.Place <= 16 { - matched = true - break - } - } - - if !matched { - t.Fatalf("expected at least one profile to match the filter") - } - } - } - }) - - t.Run("Oneof filter - happy", func(t *testing.T) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - // match the JSON encoding - // { - // "event": { - // "!type": "created" - // "created": {...} - // } - // ... - // } - Name: "data.shape.!type", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: "circle", - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err) - } - - if len(res.Foo) != 1 { - t.Fatalf("expected %d states, got %d", 1, len(res.Foo)) - } - - for ii, event := range res.Foo { - switch event.Data.Shape.Type.(type) { - case *test_pb.FooData_Shape_Circle_: - default: - t.Fatalf("expected event to be of type %T, got %T", &test_pb.FooData_Shape_Circle_{}, event.Data.Shape.Type) - } - - t.Logf("%d: %s", ii, event.Data.Shape) - } - }) - - t.Run("Oneof filter - bad name", func(t *testing.T) { - req := &test_spb.FooEventsRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Filters: []*list_j5pb.Filter{ - { - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "data.shape.!type", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: "damaged", - }, - }, - }, - }, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err == nil { - t.Fatal("expected error, got nil") - } - }) -} diff --git a/pquery/internal/integration/pagination_test.go b/pquery/internal/integration/pagination_test.go deleted file mode 100644 index faebc80..0000000 --- a/pquery/internal/integration/pagination_test.go +++ /dev/null @@ -1,142 +0,0 @@ -package integration - -import ( - "context" - "fmt" - "testing" - "time" - - "github.com/google/uuid" - "github.com/pentops/flowtest" - "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" - "github.com/pentops/protostate/pquery" -) - -func TestPagination(t *testing.T) { - uu := NewSchemaUniverse(t) - queryer := uu.FooLister(t) - - ss := NewStepper(t) - defer ss.RunSteps(t) - - ss.Setup(func(ctx context.Context, t flowtest.Asserter) error { - tenantID := uuid.NewString() - - uu.SetupFoo(t, 30, func(ii int, foo *TestObject) { - - foo.SetScalar(pquery.JSONPath("tenantId"), tenantID) - - weight := (10 + int64(ii)) - - foo.SetScalar(pquery.JSONPath("data", "characteristics", "weight"), weight) - createdAt := time.Now() - foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt) - foo.SetScalar(pquery.JSONPath("data", "field"), fmt.Sprintf("foo %d at %s", ii, createdAt.Format(time.RFC3339Nano))) - }) - return nil - - }) - - var pageResp *list_j5pb.PageResponse - - ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{} - res := &test_spb.FooListResponse{} - if err := queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()); err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != 20 { - t.Fatalf("expected 20 states, got %d", len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - pageResp = res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - }) - - ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - Token: pageResp.NextToken, - }, - } - res := &test_spb.FooListResponse{} - - query, err := queryer.BuildQuery(ctx, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - printQuery(t, query) - - err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - if len(res.Foo) != 10 { - t.Fatalf("expected 10 states, got %d", len(res.Foo)) - } - }) - - ss.Step("List Page - Short", func(ctx context.Context, t flowtest.Asserter) { - pageSize := int64(5) - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: &pageSize, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != int(pageSize) { - t.Fatalf("expected %d states, got %d", pageSize, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - pageResp = res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - }) - - ss.Step("List Page - exceeding", func(ctx context.Context, t flowtest.Asserter) { - pageSize := int64(50) - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: &pageSize, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err == nil { - t.Fatal("expected error") - } - }) -} diff --git a/pquery/internal/integration/search_test.go b/pquery/internal/integration/search_test.go deleted file mode 100644 index b082d6b..0000000 --- a/pquery/internal/integration/search_test.go +++ /dev/null @@ -1,68 +0,0 @@ -package integration - -import ( - "fmt" - "testing" - "time" - - "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" - "github.com/pentops/protostate/pquery" - "google.golang.org/protobuf/proto" -) - -func TestDynamicSearching(t *testing.T) { - uu := NewSchemaUniverse(t) - queryer := uu.FooLister(t) - - uu.SetupFoo(t, 30, func(ii int, foo *TestObject) { - - weight := (10 + int64(ii)) - - foo.SetScalar(pquery.JSONPath("data", "characteristics", "weight"), weight) - createdAt := time.Now() - foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt) - foo.SetScalar(pquery.JSONPath("data", "field"), fmt.Sprintf("foo %d weighted %d", ii, weight)) - }) - - t.Run("Simple Search Field", func(t *testing.T) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Searches: []*list_j5pb.Search{ - { - Field: "data.field", - Value: "weighted 30", - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(t.Context(), uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != 1 { - t.Fatalf("expected %d states, got %d", 1, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(30-ii) { - t.Fatalf("expected weight %d, got %d", 30-ii, state.Data.Characteristics.Weight) - } - } - - if res.Page != nil { - t.Fatalf("page response should be empty") - } - }) - -} diff --git a/pquery/internal/integration/sort_test.go b/pquery/internal/integration/sort_test.go deleted file mode 100644 index ec86f15..0000000 --- a/pquery/internal/integration/sort_test.go +++ /dev/null @@ -1,624 +0,0 @@ -package integration - -import ( - "context" - "encoding/base64" - "fmt" - "testing" - "time" - - "github.com/google/uuid" - "github.com/pentops/flowtest" - "github.com/pentops/j5/gen/j5/auth/v1/auth_j5pb" - "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" - "github.com/pentops/protostate/pquery" - "google.golang.org/protobuf/proto" -) - -func TestSortingWithAuthScope(t *testing.T) { - - uu := NewSchemaUniverse(t) - ss := NewStepper(t) - defer ss.RunSteps(t) - - var queryer *pquery.Lister - - tenantID1 := uuid.NewString() - tenantID2 := uuid.NewString() - - tenants := []string{tenantID1, tenantID2} - - ss.Setup(func(ctx context.Context, t flowtest.Asserter) error { - queryer = uu.FooLister(t, func(spec *pquery.TableSpec) { - spec.Auth = tokenAuth() - }) - uu.SetupFoo(t, 20, func(idx int, foo *TestObject) { - // recreating the old function logic - tenantId := idx % len(tenants) - tenantID := tenants[tenantId] - ii := idx / len(tenants) - ti := tenantId - - foo.SetScalar(pquery.JSONPath("tenantId"), tenantID) - - weight := ((10 + int64(ii)) * (int64(ti) + 1)) - height := ((50 - int64(ii)) * (int64(ti) + 1)) - length := (int64(ii%2) * (int64(ti) + 1)) - - foo.SetScalar(pquery.JSONPath("data", "characteristics", "weight"), weight) - foo.SetScalar(pquery.JSONPath("data", "characteristics", "height"), height) - foo.SetScalar(pquery.JSONPath("data", "characteristics", "length"), length) - createdAt := time.Now() - foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt.Format(time.RFC3339Nano)) - - label := fmt.Sprintf("foo %d (weighted %d, height %d, length %d)", ii, weight, height, length) - foo.SetScalar(pquery.JSONPath("data", "field"), label) - - }) - return nil - }) - - tkn := &token{ - claim: &auth_j5pb.Claim{ - TenantType: "tenant", - TenantId: tenantID1, - }, - } - - nextToken := "" - ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { - ctx = tkn.WithToken(ctx) - - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Sorts: []*list_j5pb.Sort{ - {Field: "data.characteristics.weight"}, - }, - }, - } - res := &test_spb.FooListResponse{} - - err := queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != int(5) { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(10+ii) { - t.Fatalf("%d: expected weight %d, got %d", 10+ii, ii, state.Data.Characteristics.Weight) - } - - if *state.Keys.TenantId != tenantID1 { - t.Fatalf("%d: expected tenant ID %s, got %s", ii, tenantID1, state.Keys.TenantId) - } - } - - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - - nextToken = pageResp.GetNextToken() - }) - - ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { - ctx = tkn.WithToken(ctx) - - pageTokenBytes, err := base64.StdEncoding.DecodeString(nextToken) - if err != nil { - t.Fatalf("failed to decode next token: %v", err) - } - t.Logf("Page Token: %s", string(pageTokenBytes)) - - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - Token: &nextToken, - }, - Query: &list_j5pb.QueryRequest{ - Sorts: []*list_j5pb.Sort{ - {Field: "data.characteristics.weight"}, - }, - }, - } - res := &test_spb.FooListResponse{} - - err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != int(5) { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(15+ii) { - t.Fatalf("%d: expected weight %d, got %d", ii, 15+ii, state.Data.Characteristics.Weight) - } - - if *state.Keys.TenantId != tenantID1 { - t.Fatalf("%d: expected tenant ID %s, got %s", ii, tenantID1, state.Keys.TenantId) - } - } - - if res.Page != nil { - t.Fatalf("page response should be empty") - } - }) -} - -func TestDynamicSorting(t *testing.T) { - uu := NewSchemaUniverse(t) - - queryer := uu.FooLister(t) - tenantID := uuid.NewString() - uu.SetupFoo(t, 30, func(ii int, foo *TestObject) { - weight := (10 + int64(ii)) - height := (50 - int64(ii)) - length := (int64(ii % 2)) - - foo.SetScalar(pquery.JSONPath("tenantId"), tenantID) - foo.SetScalar(pquery.JSONPath("data", "characteristics", "weight"), weight) - foo.SetScalar(pquery.JSONPath("data", "characteristics", "height"), height) - foo.SetScalar(pquery.JSONPath("data", "characteristics", "length"), length) - createdAt := time.Now() - foo.SetScalar(pquery.JSONPath("metadata", "createdAt"), createdAt.Format(time.RFC3339Nano)) - - label := fmt.Sprintf("foo %d at %s (weighted %d, height %d, length %d)", ii, createdAt.Format(time.RFC3339Nano), weight, height, length) - foo.SetScalar(pquery.JSONPath("data", "field"), label) - - val, err := foo.GetOrCreateValue("data", "profiles") - if err != nil { - t.Fatalf("failed to get or create profiles: %v", err) - } - - objArray, ok := val.AsArrayOfObject() - if !ok { - t.Fatalf("expected profiles to be an array of objects, got %T", val) - } - - profile1, _ := objArray.NewObjectElement() - profile2, _ := objArray.NewObjectElement() - - if err := SetScalar(profile1, pquery.JSONPath("name"), fmt.Sprintf("profile %d", ii)); err != nil { - t.Fatalf("failed to set profile name: %v", err) - } - - if err := SetScalar(profile1, pquery.JSONPath("place"), int64(ii)+50); err != nil { - t.Fatalf("failed to set profile place: %v", err) - } - - if err := SetScalar(profile2, pquery.JSONPath("name"), fmt.Sprintf("profile %d", ii)); err != nil { - t.Fatalf("failed to set profile name: %v", err) - } - - if err := SetScalar(profile2, pquery.JSONPath("place"), int64(ii)+15); err != nil { - t.Fatalf("failed to set profile place: %v", err) - } - - switch ii { - case 10: - foo.SetScalar(pquery.JSONPath("data", "shape", "circle", "radius"), int64(10)) - case 11: - foo.SetScalar(pquery.JSONPath("data", "shape", "square", "side"), int64(10)) - } - }) - - t.Run("Top Level Field", func(t *testing.T) { - ss := NewStepper(t) - defer ss.RunSteps(t) - var err error - - nextToken := "" - ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Sorts: []*list_j5pb.Sort{ - {Field: "metadata.createdAt"}, - }, - }, - } - res := &test_spb.FooListResponse{} - - err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != int(5) { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(10+ii) { - t.Fatalf("expected weight %d, got %d", 10+ii, state.Data.Characteristics.Weight) - } - } - - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - - nextToken = pageResp.GetNextToken() - }) - - ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - Token: &nextToken, - }, - Query: &list_j5pb.QueryRequest{ - Sorts: []*list_j5pb.Sort{ - {Field: "metadata.createdAt"}, - }, - }, - } - res := &test_spb.FooListResponse{} - - err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != int(5) { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(15+ii) { - t.Fatalf("expected weight %d, got %d", 15+ii, state.Data.Characteristics.Weight) - } - } - - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - }) - }) - - t.Run("Nested Field", func(t *testing.T) { - ss := NewStepper(t) - defer ss.RunSteps(t) - var err error - - nextToken := "" - ss.Step("List Page 1", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Sorts: []*list_j5pb.Sort{ - {Field: "data.characteristics.weight"}, - }, - }, - } - res := &test_spb.FooListResponse{} - - err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != int(5) { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(10+ii) { - t.Fatalf("expected weight %d, got %d", 10+ii, state.Data.Characteristics.Weight) - } - } - - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - - nextToken = pageResp.GetNextToken() - }) - - ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - Token: &nextToken, - }, - Query: &list_j5pb.QueryRequest{ - Sorts: []*list_j5pb.Sort{ - {Field: "data.characteristics.weight"}, - }, - }, - } - res := &test_spb.FooListResponse{} - - err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != int(5) { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(15+ii) { - t.Fatalf("expected weight %d, got %d", 15+ii, state.Data.Characteristics.Weight) - } - } - - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - }) - }) - - t.Run("Multiple Nested Fields", func(t *testing.T) { - ss := NewStepper(t) - defer ss.RunSteps(t) - var err error - nextToken := "" - ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Sorts: []*list_j5pb.Sort{ - {Field: "data.characteristics.length"}, - {Field: "data.characteristics.weight"}, - }, - }, - } - res := &test_spb.FooListResponse{} - - err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != int(5) { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - if res.Foo[0].Data.Characteristics.Weight != int64(10) { - t.Fatalf("expected list to start with weight %d, got %d", 10, res.Foo[0].Data.Characteristics.Weight) - } - - for _, state := range res.Foo { - if state.Data.Characteristics.Weight%2 != 0 { - t.Fatalf("expected even number weight, got %d", state.Data.Characteristics.Weight) - } - } - - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - - nextToken = pageResp.GetNextToken() - }) - - ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { - - pageTokenBytes, err := base64.StdEncoding.DecodeString(nextToken) - if err != nil { - t.Fatalf("failed to decode next token: %v", err) - } - t.Logf("Page Token: %s", string(pageTokenBytes)) - - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - Token: &nextToken, - }, - Query: &list_j5pb.QueryRequest{ - Sorts: []*list_j5pb.Sort{ - {Field: "data.characteristics.length"}, - {Field: "data.characteristics.weight"}, - }, - }, - } - res := &test_spb.FooListResponse{} - - err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != int(5) { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - if res.Foo[0].Data.Characteristics.Weight != int64(20) { - t.Fatalf("expected list to start with weight %d, got %d", 20, res.Foo[0].Data.Characteristics.Weight) - } - - for _, state := range res.Foo { - if state.Data.Characteristics.Weight%2 != 0 { - t.Fatalf("expected even number weight, got %d", state.Data.Characteristics.Weight) - } - } - - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - }) - }) - - t.Run("Descending", func(t *testing.T) { - ss := NewStepper(t) - defer ss.RunSteps(t) - var err error - nextToken := "" - ss.Step("List Page", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - }, - Query: &list_j5pb.QueryRequest{ - Sorts: []*list_j5pb.Sort{ - { - Field: "data.characteristics.weight", - Descending: true, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != int(5) { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(39-ii) { - t.Fatalf("expected weight %d, got %d", 39-ii, state.Data.Characteristics.Weight) - } - } - - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - - nextToken = pageResp.GetNextToken() - }) - - ss.Step("List Page 2", func(ctx context.Context, t flowtest.Asserter) { - req := &test_spb.FooListRequest{ - Page: &list_j5pb.PageRequest{ - PageSize: proto.Int64(5), - Token: &nextToken, - }, - Query: &list_j5pb.QueryRequest{ - Sorts: []*list_j5pb.Sort{ - { - Field: "data.characteristics.weight", - Descending: true, - }, - }, - }, - } - res := &test_spb.FooListResponse{} - - err = queryer.List(ctx, uu.DB, req.J5Object(), res.J5Object()) - if err != nil { - t.Fatal(err.Error()) - } - - if len(res.Foo) != int(5) { - t.Fatalf("expected %d states, got %d", 5, len(res.Foo)) - } - - for ii, state := range res.Foo { - t.Logf("%d: %s", ii, state.Data.Field) - } - - for ii, state := range res.Foo { - if state.Data.Characteristics.Weight != int64(34-ii) { - t.Fatalf("expected weight %d, got %d", 34-ii, state.Data.Characteristics.Weight) - } - } - - pageResp := res.Page - - if pageResp.GetNextToken() == "" { - t.Fatalf("NextToken should not be empty") - } - if pageResp.NextToken == nil { - t.Fatalf("Should not be the final page") - } - }) - }) -} diff --git a/pquery/internal/integration/token.go b/pquery/internal/integration/token.go deleted file mode 100644 index d0ffa84..0000000 --- a/pquery/internal/integration/token.go +++ /dev/null @@ -1,50 +0,0 @@ -package integration - -import ( - "context" - "fmt" - - "github.com/pentops/j5/gen/j5/auth/v1/auth_j5pb" - "github.com/pentops/protostate/pquery" -) - -type tokenCtxKey struct{} - -type token struct { - claim *auth_j5pb.Claim -} - -func (t *token) WithToken(ctx context.Context) context.Context { - return context.WithValue(ctx, tokenCtxKey{}, t) -} - -func TokenFromCtx(ctx context.Context) (*token, error) { - token, exists := ctx.Value(tokenCtxKey{}).(*token) - if !exists { - return nil, fmt.Errorf("no token found") - } - - return token, nil -} - -func tokenAuth() pquery.AuthProvider { - fieldMap := map[string]string{ - "tenant": "tenant_id", - "meta_tenant": "meta_tenant_id", - } - return pquery.AuthProviderFunc(func(ctx context.Context) (map[string]string, error) { - token, err := TokenFromCtx(ctx) - if err != nil { - return nil, err - } - - tt, ok := fieldMap[token.claim.TenantType] - if !ok { - return nil, fmt.Errorf("no field mapping for tenant type %s", token.claim.TenantType) - } - - return map[string]string{ - tt: token.claim.TenantId, - }, nil - }) -} diff --git a/pquery/internal/integration/universe.go b/pquery/internal/integration/universe.go deleted file mode 100644 index 6632e3d..0000000 --- a/pquery/internal/integration/universe.go +++ /dev/null @@ -1,203 +0,0 @@ -package integration - -import ( - "context" - "database/sql" - "fmt" - "strings" - "testing" - - sq "github.com/elgris/sqrl" - "github.com/pentops/flowtest" - "github.com/pentops/golib/gl" - "github.com/pentops/j5/lib/id62" - "github.com/pentops/j5/lib/j5reflect" - "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/pgtest.go/pgtest" - "github.com/pentops/protostate/internal/dbconvert" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" - "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" - "github.com/pentops/protostate/pquery" - "github.com/pentops/protostate/pquery/pgmigrate" - "github.com/pentops/sqrlx.go/sqrlx" -) - -func printQuery(t flowtest.TB, query sq.Sqlizer) { - stmt, args, err := query.ToSql() - if err != nil { - t.Fatal(err.Error()) - } - t.Log(stmt, args) -} - -func testLogger(t flowtest.TB) pquery.QueryLogger { - return func(query sqrlx.Sqlizer) { - queryString, args, err := query.ToSql() - if err != nil { - t.Logf("Query Error: %s", err.Error()) - return - } - t.Logf("Query %s; ARGS %#v", queryString, args) - } -} - -func NewStepper(t *testing.T) *flowtest.Stepper[*testing.T] { - return flowtest.NewStepper[*testing.T](t.Name()) -} - -type SchemaUniverse struct { - DB sqrlx.Transactor - - conn *sql.DB -} - -func NewSchemaUniverse(t *testing.T) *SchemaUniverse { - t.Helper() - - conn := pgtest.GetTestDB(t, pgtest.WithSchemaName("query_test")) - db := sqrlx.NewPostgres(conn) - - return &SchemaUniverse{ - DB: db, - conn: conn, - } -} - -func (uu *SchemaUniverse) Migrate(t flowtest.TB, commands ...string) { - t.Helper() - for _, cmd := range commands { - if _, err := uu.conn.Exec(cmd); err != nil { - t.Fatal(err.Error()) - } - } -} - -func SetScalar(obj j5reflect.Object, fieldPath pquery.JSONPathSpec, value any) error { - field, err := obj.GetOrCreateValue(fieldPath...) - if err != nil { - return err - } - scalar, ok := field.AsScalar() - if !ok { - return fmt.Errorf("field %s is not a scalar", strings.Join(fieldPath, ".")) - } - if err := scalar.SetGoValue(value); err != nil { - return err - } - return nil -} - -type TestObject struct { - j5reflect.Object - t flowtest.TB -} - -func (to *TestObject) SetScalar(fieldPath pquery.JSONPathSpec, value any) { - to.t.Helper() - if err := SetScalar(to.Object, fieldPath, value); err != nil { - to.t.Fatal(err.Error()) - } -} - -func (uu *SchemaUniverse) SetupFoo(t flowtest.TB, count int, callback ...func(int, *TestObject)) { - t.Helper() - if err := uu.DB.Transact(t.Context(), &sqrlx.TxOptions{ - Isolation: sql.LevelDefault, - }, func(ctx context.Context, tx sqrlx.Transaction) error { - for ii := range count { - id := id62.NewString() - foo := &test_pb.FooState{} - fooRefl := foo.J5Object() - if err := SetScalar(fooRefl, pquery.JSONPath("fooId"), id); err != nil { - return err - } - if err := SetScalar(fooRefl, pquery.JSONPath("data", "field"), fmt.Sprintf("Foo %d", ii)); err != nil { - return err - } - - if err := SetScalar(fooRefl, pquery.JSONPath("status"), "ACTIVE"); err != nil { - return err - } - - for _, cb := range callback { - cb(ii, &TestObject{t: t, Object: fooRefl}) - } - - fooJSON, err := dbconvert.MarshalJ5(fooRefl) - if err != nil { - return fmt.Errorf("marshal: %w", err) - } - - _, err = tx.Insert(ctx, sq.Insert("foo").Columns("foo_id", "state").Values(id, fooJSON)) - if err != nil { - return err - } - - } - return nil - }); err != nil { - t.Fatal(err.Error()) - } - -} - -func (uu *SchemaUniverse) FooLister(t flowtest.TB, mods ...func(*pquery.TableSpec)) *pquery.Lister { - t.Helper() - uu.Migrate(t, ` - CREATE TABLE foo ( - foo_id char(36) NOT NULL, - state jsonb NOT NULL, - tenant_id text GENERATED ALWAYS AS (state->>'tenantId') STORED - )`) - - requestSchema, ok := (&test_spb.FooListRequest{}).J5Object().RootSchema() - if !ok { - t.Fatal("failed to get request schema") - } - responseSchema, ok := (&test_spb.FooListResponse{}).J5Object().RootSchema() - if !ok { - t.Fatal("failed to get response schema") - } - fooSchema, ok := (&test_pb.FooState{}).J5Object().RootSchema() - if !ok { - t.Fatal("failed to get foo schema") - } - - tableSpec := pquery.TableSpec{ - TableName: "foo", - DataColumn: "state", - RootObject: fooSchema.(*j5schema.ObjectSchema), - FallbackSortColumns: []pquery.ProtoField{ - pquery.NewJSONField("fooId", gl.Ptr("foo_id")), - }, - } - - for _, mod := range mods { - mod(&tableSpec) - } - - migrations, err := pgmigrate.IndexMigrations(tableSpec) - if err != nil { - t.Fatalf("failed to get index migrations: %v", err) - } - if err := pgmigrate.RunMigrations(t.Context(), uu.DB, migrations); err != nil { - t.Fatalf("failed to run migrations: %v", err) - } - - method := &j5schema.MethodSchema{ - Request: requestSchema.(*j5schema.ObjectSchema), - Response: responseSchema.(*j5schema.ObjectSchema), - } - listSpec := pquery.ListSpec{ - TableSpec: tableSpec, - Method: method, - } - - queryer, err := pquery.NewLister(listSpec) - if err != nil { - t.Fatalf("failed to create queryer: %w", err) - } - queryer.SetQueryLogger(testLogger(t)) - return queryer - -} diff --git a/pquery/lister.go b/pquery/lister.go deleted file mode 100644 index 4816d95..0000000 --- a/pquery/lister.go +++ /dev/null @@ -1,891 +0,0 @@ -package pquery - -import ( - "context" - "database/sql" - "encoding/base64" - "encoding/json" - "fmt" - "strings" - "time" - - sq "github.com/elgris/sqrl" - "github.com/elgris/sqrl/pg" - "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" - "github.com/pentops/j5/j5types/date_j5t" - "github.com/pentops/j5/lib/j5codec" - "github.com/pentops/j5/lib/j5reflect" - "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/j5/lib/j5validate" - "github.com/pentops/log.go/log" - "github.com/pentops/sqrlx.go/sqrlx" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -type ListRequest interface { - j5reflect.Object -} - -type ListResponse interface { - j5reflect.Object -} - -type TableSpec struct { - TableName string - - Auth AuthProvider - AuthJoin []*LeftJoin - - DataColumn string // TODO: Replace with array Columns []Column - RootObject *j5schema.ObjectSchema - - // List of fields to sort by if no other unique sort is found. - FallbackSortColumns []ProtoField -} - -func (ts *TableSpec) Validate() error { - if ts.TableName == "" { - return fmt.Errorf("table name must be set") - } - if ts.DataColumn == "" { - return fmt.Errorf("data column must be set") - } - if ts.RootObject == nil { - return fmt.Errorf("root object must be set") - } - return nil -} - -// ProtoField represents a field within a the root data. -type ProtoField struct { - // path from the root object to this field - pathInRoot JSONPathSpec - - // optional column name when the field is also stored as a scalar directly - valueColumn *string -} - -func NewJSONField(protoPath string, columnName *string) ProtoField { - pp := ParseJSONPathSpec(protoPath) - return ProtoField{ - valueColumn: columnName, - pathInRoot: pp, - } -} - -type Column struct { - Name string - - // The point within the root element which is stored in the column. An empty - // path means this stores the root element, - MountPoint *Path -} - -type ListSpec struct { - Method *j5schema.MethodSchema - - TableSpec - RequestFilter func(j5reflect.Object) (map[string]any, error) -} - -func (ls *ListSpec) Validate() error { - if ls.Method == nil { - return fmt.Errorf("list spec must have a method") - } - if err := ls.TableSpec.Validate(); err != nil { - return fmt.Errorf("validate table spec: %w", err) - } - return nil -} - -type QueryLogger func(sqrlx.Sqlizer) - -type ListReflectionSet struct { - method *j5schema.MethodSchema - defaultPageSize uint64 - - arrayField *j5schema.ObjectProperty - arrayObject *j5schema.ObjectSchema - - pageResponseField *j5schema.ObjectProperty - pageRequestField *j5schema.ObjectProperty - queryRequestField *j5schema.ObjectProperty - - defaultSortFields []sortSpec - tieBreakerFields []sortSpec - - defaultFilterFields []filterSpec - RequestFilterFields []*j5schema.ObjectProperty - - tsvColumnMap map[string]string // map[JSON Path]PGColumName - - // TODO: This should be an array/map of columns to data types, allowing - // multiple JSONB values, as well as cached field values direcrly on the - // table - dataColumn string -} - -func (ll *ListReflectionSet) ArrayObject() *j5schema.ObjectSchema { - return ll.arrayObject -} - -func BuildListReflection(method *j5schema.MethodSchema, table TableSpec) (*ListReflectionSet, error) { - return buildListReflection(method, table) -} - -func buildListReflection(method *j5schema.MethodSchema, table TableSpec) (*ListReflectionSet, error) { - - var err error - ll := ListReflectionSet{ - method: method, - defaultPageSize: uint64(20), - dataColumn: table.DataColumn, - } - - for _, field := range method.Response.Properties { - switch ft := field.Schema.(type) { - case *j5schema.ObjectField: - - if ft.ObjectSchema().FullName() == "j5.list.v1.PageResponse" { - ll.pageResponseField = field - continue - } - - case *j5schema.ArrayField: - - objectField, ok := ft.ItemSchema.(*j5schema.ObjectField) - if !ok { - return nil, fmt.Errorf("array field %s must be an object field, got %s", field.FullName(), ft.ItemSchema.FullName()) - } - - if ll.arrayField != nil { - return nil, fmt.Errorf("multiple repeated fields (%s and %s)", ll.arrayField.FullName(), field.FullName()) - } - - ll.arrayField = field - ll.arrayObject = objectField.ObjectSchema() - continue - - } - - return nil, fmt.Errorf("unknown field in list response: '%s' of type %s", field.FullName(), field.Schema.TypeName()) - } - - if ll.arrayField == nil { - return nil, fmt.Errorf("no repeated field in response, %s must have a repeated message", method.Response.FullName()) - } - - if ll.pageResponseField == nil { - return nil, fmt.Errorf("no page field in response, %s must have a j5.list.v1.PageResponse", method.Response.FullName()) - } - - err = validateListAnnotations(ll.arrayObject) - if err != nil { - return nil, fmt.Errorf("validate list annotations on %s: %w", ll.arrayField.FullName(), err) - } - - ll.defaultSortFields, err = buildDefaultSorts(ll.dataColumn, ll.arrayObject) - if err != nil { - return nil, fmt.Errorf("default sorts: %w", err) - } - - ll.tieBreakerFields, err = buildTieBreakerFields(ll.dataColumn, method.Request, ll.arrayObject, table.FallbackSortColumns) - if err != nil { - return nil, fmt.Errorf("tie breaker fields: %w", err) - } - - if len(ll.defaultSortFields) == 0 && len(ll.tieBreakerFields) == 0 { - return nil, fmt.Errorf("no default sort field found, %s must have at least one field annotated as default sort, or specify a tie breaker in %s", ll.arrayField.FullName(), method.Request.FullName()) - } - - f, err := buildDefaultFilters(ll.dataColumn, ll.arrayObject) - if err != nil { - return nil, fmt.Errorf("default filters: %w", err) - } - - ll.defaultFilterFields = f - - ll.tsvColumnMap, err = buildTsvColumnMap(ll.arrayObject) - if err != nil { - return nil, fmt.Errorf("build tsv column map: %w", err) - } - - for _, field := range method.Request.Properties { - switch ft := field.Schema.(type) { - case *j5schema.ObjectField: - switch ft.ObjectSchema().FullName() { - case "j5.list.v1.PageRequest": - ll.pageRequestField = field - case "j5.list.v1.QueryRequest": - ll.queryRequestField = field - default: - return nil, fmt.Errorf("unknown field in request: '%s' of type %s", field.FullName(), field.Schema.TypeName()) - } - - case *j5schema.ScalarSchema: - ll.RequestFilterFields = append(ll.RequestFilterFields, field) - - default: - return nil, fmt.Errorf("unsupported filter field in request: '%s' of type %s", field.FullName(), field.Schema.TypeName()) - } - } - - if ll.pageRequestField == nil { - return nil, fmt.Errorf("no page field in request, %s must have a j5.list.v1.PageRequest", method.Request.FullName()) - } - - if ll.queryRequestField == nil { - return nil, fmt.Errorf("no query field in request, %s must have a j5.list.v1.QueryRequest", method.Request.FullName()) - } - - arraySchema := ll.arrayField.Schema.(*j5schema.ArrayField) - if arraySchema.Rules != nil { - if arraySchema.Rules.MaxItems != nil { - ll.defaultPageSize = *arraySchema.Rules.MaxItems - } - } - - return &ll, nil -} - -type Lister struct { - ListReflectionSet - - tableName string - - queryLogger QueryLogger - - auth AuthProvider - authJoin []*LeftJoin - - requestFilter func(j5reflect.Object) (map[string]any, error) - - validator *j5validate.Validator -} - -func NewLister(spec ListSpec) (*Lister, error) { - ll := &Lister{ - tableName: spec.TableName, - auth: spec.Auth, - authJoin: spec.AuthJoin, - } - - if err := spec.Validate(); err != nil { - return nil, fmt.Errorf("validate list spec: %w", err) - } - - listFields, err := buildListReflection(spec.Method, spec.TableSpec) - if err != nil { - return nil, err - } - ll.ListReflectionSet = *listFields - - ll.requestFilter = spec.RequestFilter - - ll.validator = j5validate.Global - - return ll, nil -} - -func (ll *Lister) SetQueryLogger(logger QueryLogger) { - ll.queryLogger = logger -} - -func (ll *Lister) List(ctx context.Context, db Transactor, req, res j5reflect.Object) error { - if err := ll.validator.Validate(req); err != nil { - return fmt.Errorf("validating request %s: %w", req.SchemaName(), err) - } - - pageSize, err := ll.getPageSize(req) - if err != nil { - return fmt.Errorf("get page size: %w", err) - } - - selectQuery, err := ll.BuildQuery(ctx, req, res) - if err != nil { - return fmt.Errorf("build query: %w", err) - } - - txOpts := &sqrlx.TxOptions{ - ReadOnly: true, - Retryable: true, - Isolation: sql.LevelReadCommitted, - } - - var jsonRows = make([][]byte, 0, pageSize) - err = db.Transact(ctx, txOpts, func(ctx context.Context, tx sqrlx.Transaction) error { - rows, err := tx.Query(ctx, selectQuery) - if err != nil { - return fmt.Errorf("run select: %w", err) - } - defer rows.Close() - - for rows.Next() { - var json []byte - if err := rows.Scan(&json); err != nil { - return fmt.Errorf("row scan: %w", err) - } - - jsonRows = append(jsonRows, json) - } - - return rows.Err() - }) - if err != nil { - stmt, _, _ := selectQuery.ToSql() - log.WithField(ctx, "query", stmt).Error("list query") - return fmt.Errorf("list query: %w", err) - } - - if ll.queryLogger != nil { - ll.queryLogger(selectQuery) - } - - listField, err := res.GetOrCreateValue(ll.arrayField.JSONName) - if err != nil { - return err - } - list, ok := listField.AsArrayOfObject() - if !ok { - return fmt.Errorf("field %s in response is not an array", ll.arrayField.FullName()) - } - - var nextToken string - for idx, rowBytes := range jsonRows { - rowMessage, _ := list.NewObjectElement() - - err := j5codec.Global.JSONToReflect(rowBytes, rowMessage) - if err != nil { - return fmt.Errorf("unmarshal into %s from %s: %w", rowMessage.SchemaName(), string(rowBytes), err) - } - - if idx >= int(pageSize) { - // TODO: This works but the token is huge. - // The eventual solution will need to look at - // the sorting and filtering of the query and either encode them - // directly, or encode a subset of the message as required. - - nextToken, err = encodePageToken(rowMessage, selectQuery.sortFields) - if err != nil { - return fmt.Errorf("encode page token: %w", err) - } - break - } - } - - // TODO: This drops the pagination token from the list. - // Consider adding support to J5Reflect to create the object element without - // attaching it to the array - if list.Length() > int(pageSize) { - list.Truncate(int(pageSize)) - } - - if nextToken != "" { - pageRes, err := res.GetOrCreateValue(ll.pageResponseField.JSONName) - if err != nil { - return err - } - - cont, ok := pageRes.AsContainer() - if !ok { - return fmt.Errorf("field %s in response is not a container", ll.pageResponseField.FullName()) - } - - nextTokenVal, err := cont.GetOrCreateValue("nextToken") - if err != nil { - return fmt.Errorf("get nextToken field in response: %w", err) - } - nextTokenScalar, ok := nextTokenVal.AsScalar() - if !ok { - return fmt.Errorf("field %s in response is not a scalar", nextTokenVal.FullTypeName()) - } - if err := nextTokenScalar.SetGoValue(nextToken); err != nil { - return fmt.Errorf("set nextToken field in response: %w", err) - } - } - - return nil -} - -func encodePageToken(rowMessage j5reflect.Object, sortFields []sortSpec) (string, error) { - vals := map[string]any{} - for _, sortField := range sortFields { - fieldVal, _, err := sortField.Path.GetValue(rowMessage) - if err != nil { - return "", fmt.Errorf("sort field %s: %w", sortField.errorName(), err) - } - fmt.Printf("Set Sort Field %s to %v\n", sortField.errorName(), fieldVal) - vals[sortField.Path.IDPath()] = fieldVal - - } - valJSON, err := json.Marshal(vals) - if err != nil { - return "", fmt.Errorf("marshal page token values: %w", err) - } - return base64.StdEncoding.EncodeToString(valJSON), nil -} - -func decodePageToken(token string, sortFields []sortSpec) (map[string]any, error) { - vals := map[string]any{} - jsonBytes, err := base64.StdEncoding.DecodeString(token) - if err != nil { - return nil, fmt.Errorf("decode page token: %w", err) - } - dec := json.NewDecoder(strings.NewReader(string(jsonBytes))) - dec.UseNumber() - err = dec.Decode(&vals) - if err != nil { - return nil, fmt.Errorf("unmarshal page token: %w", err) - } - if len(vals) != len(sortFields) { - return nil, fmt.Errorf("page token has %d values, expected %d", len(vals), len(sortFields)) - } - - return vals, nil -} - -func fieldAs[T any](obj j5reflect.Object, path ...string) (val T, ok bool, err error) { - - endField, ok, err := obj.GetField(path...) - if err != nil || !ok { - return val, ok, err - } - - objField, ok := endField.AsObject() - if !ok { - return val, false, fmt.Errorf("field %s in %s not an object", path, obj.SchemaName()) - } - - if !objField.IsSet() { - return val, false, nil - } - - val, ok = objField.ProtoReflect().Interface().(T) - if !ok { - return val, false, fmt.Errorf("field %s in %s not of type %T", path, obj.SchemaName(), (*T)(nil)) - } - - return val, true, nil - -} - -type Query struct { - *sq.SelectBuilder - sortFields []sortSpec -} - -func (ll *Lister) BuildQuery(ctx context.Context, req j5reflect.Object, res j5reflect.Object) (*Query, error) { - - err := assertObjectsMatch(ll.method, req, res) - if err != nil { - return nil, err - } - - as := newAliasSet() - tableAlias := as.Next(ll.tableName) - - selectQuery := sq.Select(fmt.Sprintf("%s.%s", tableAlias, ll.dataColumn)). - From(fmt.Sprintf("%s AS %s", ll.tableName, tableAlias)) - - sortFields := ll.defaultSortFields - sortFields = append(sortFields, ll.tieBreakerFields...) - - filterFields := []sq.Sqlizer{} - if ll.requestFilter != nil { - filter, err := ll.requestFilter(req) - if err != nil { - return nil, err - } - - and := sq.And{} - for k := range filter { - and = append(and, sq.Expr(fmt.Sprintf("%s.%s = ?", tableAlias, k), filter[k])) - } - - if len(and) > 0 { - selectQuery.Where(and) - } - } - - reqQuery, ok, err := fieldAs[*list_j5pb.QueryRequest](req, ll.queryRequestField.JSONName) - if err != nil { - return nil, err - } - if ok { - if err := ll.validateQueryRequest(reqQuery); err != nil { - return nil, status.Errorf(codes.InvalidArgument, "query validation: %s", err) - } - - querySorts := reqQuery.GetSorts() - if len(querySorts) > 0 { - dynSorts, err := ll.buildDynamicSortSpec(querySorts) - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "build sorts: %s", err) - } - - sortFields = dynSorts - } - - queryFilters := reqQuery.GetFilters() - if len(queryFilters) > 0 { - dynFilters, err := ll.buildDynamicFilter(tableAlias, queryFilters) - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "build filters: %s", err) - } - - filterFields = append(filterFields, dynFilters...) - } - - querySearches := reqQuery.GetSearches() - if len(querySearches) > 0 { - searchFilters, err := ll.buildDynamicSearches(tableAlias, querySearches) - if err != nil { - return nil, status.Errorf(codes.InvalidArgument, "build searches: %s", err) - } - - filterFields = append(filterFields, searchFilters...) - } - } - - for i := range filterFields { - selectQuery.Where(filterFields[i]) - } - - // apply default filters if no filters have been requested - if ll.defaultFilterFields != nil && len(filterFields) == 0 { - and := sq.And{} - for _, spec := range ll.defaultFilterFields { - or := sq.Or{} - for _, val := range spec.filterVals { - or = append(or, sq.Expr(fmt.Sprintf("jsonb_path_query_array(%s.%s, '%s') @> ?", tableAlias, ll.dataColumn, spec.Path.JSONPathQuery()), pg.JSONB(val))) - } - - and = append(and, or) - } - - if len(and) > 0 { - selectQuery.Where(and) - } - } - - for _, sortField := range sortFields { - direction := "ASC" - if sortField.desc { - direction = "DESC" - } - selectQuery.OrderBy(fmt.Sprintf("%s %s", sortField.Selector(tableAlias), direction)) - } - - if ll.auth != nil { - authAlias := tableAlias - for _, join := range ll.authJoin { - priorAlias := authAlias - authAlias = as.Next(join.TableName) - selectQuery = selectQuery.LeftJoin(fmt.Sprintf( - "%s AS %s ON %s", - join.TableName, - authAlias, - join.On.SQL(priorAlias, authAlias), - )) - } - - authFilter, err := ll.auth.AuthFilter(ctx) - if err != nil { - return nil, err - } - - if len(authFilter) > 0 { - claimFilter := map[string]any{} - for k, v := range authFilter { - claimFilter[fmt.Sprintf("%s.%s", authAlias, k)] = v - } - selectQuery.Where(claimFilter) - } - } - - pageSize, err := ll.getPageSize(req) - if err != nil { - return nil, err - } - - selectQuery.Limit(pageSize + 1) - - reqPage, ok, err := fieldAs[*list_j5pb.PageRequest](req, ll.pageRequestField.JSONName) - if err != nil { - return nil, fmt.Errorf("get page request field: %w", err) - } - if ok && reqPage.GetToken() != "" { - - filter, err := ll.addPageFilter(reqPage.GetToken(), sortFields, tableAlias) - if err != nil { - return nil, err - } - selectQuery.Where(filter) - - } - - return &Query{ - SelectBuilder: selectQuery, - sortFields: sortFields, - }, nil -} - -func (ll *Lister) addPageFilter(token string, sortFields []sortSpec, tableAlias string) (sq.Sqlizer, error) { - - lhsFields := make([]string, 0, len(sortFields)) - rhsValues := make([]any, 0, len(sortFields)) - rhsPlaceholders := make([]string, 0, len(sortFields)) - - pageFields, err := decodePageToken(token, sortFields) - if err != nil { - return nil, fmt.Errorf("decode page token: %w", err) - } - - for _, sortField := range sortFields { - rowSelecter := sortField.Selector(tableAlias) - valuePlaceholder := "?" - - dbVal, ok := pageFields[sortField.Path.IDPath()] - if !ok { - return nil, fmt.Errorf("page token missing value for sort field %s", sortField.errorName()) - } - - switch schema := sortField.Path.leafField.Schema.(type) { - //case *j5schema.EnumField: - - case *j5schema.ScalarSchema: - - ft := schema.ToJ5Field() - - switch ft := ft.Type.(type) { - case *schema_j5pb.Field_String_, *schema_j5pb.Field_Key: - dbVal, ok = dbVal.(string) - if !ok { - return nil, fmt.Errorf("sort field %s is a string, but value is not a string", sortField.errorName()) - } - if sortField.desc { - // String fields aren't valid for sorting, they can only be used - // for the tie-breaker so the order itself is not important, only - // that it is consistent - return nil, fmt.Errorf("sort field %s is a string, strings cannot be sorted DESC", sortField.errorName()) - } - - case *schema_j5pb.Field_Integer: - number, ok := dbVal.(json.Number) - if !ok { - stringVal, ok := dbVal.(string) - if !ok { - return nil, fmt.Errorf("sort field %s is an integer, but value is %+v", sortField.errorName(), dbVal) - } - number = json.Number(stringVal) - } - int64val, err := number.Int64() - if err != nil { - return nil, fmt.Errorf("sort field %s is an integer, but value is not a valid integer: %w", sortField.errorName(), err) - } - - switch ft.Integer.Format { - case schema_j5pb.IntegerField_FORMAT_INT32, schema_j5pb.IntegerField_FORMAT_UINT32: - - if sortField.desc { - int64val = int64val * -1 - rowSelecter = fmt.Sprintf("-1 * (%s)::integer", rowSelecter) - } - - case schema_j5pb.IntegerField_FORMAT_INT64, schema_j5pb.IntegerField_FORMAT_UINT64: - - if sortField.desc { - int64val = int64val * -1 - rowSelecter = fmt.Sprintf("-1 * (%s)::bigint", rowSelecter) - } - - default: - panic(fmt.Sprintf("unknown integer format %s for field %s", ft.Integer.Format, sortField.errorName())) - } - dbVal = int64val - - case *schema_j5pb.Field_Float: - number, ok := dbVal.(json.Number) - if !ok { - stringVal, ok := dbVal.(string) - if !ok { - return nil, fmt.Errorf("sort field %s is a float, but value is not a number", sortField.errorName()) - } - number = json.Number(stringVal) - } - float64val, err := number.Float64() - if err != nil { - return nil, fmt.Errorf("sort field %s is a float, but value is not a valid float: %w", sortField.errorName(), err) - } - switch ft.Float.Format { - case schema_j5pb.FloatField_FORMAT_FLOAT32: - if sortField.desc { - float64val = float64val * -1 - rowSelecter = fmt.Sprintf("-1 * (%s)::real", rowSelecter) - } - - case schema_j5pb.FloatField_FORMAT_FLOAT64: - if sortField.desc { - float64val = float64val * -1 - rowSelecter = fmt.Sprintf("-1 * (%s)::double precision", rowSelecter) - } - } - dbVal = float64val - - case *schema_j5pb.Field_Bool: - if sortField.desc { - dbVal = !dbVal.(bool) - rowSelecter = fmt.Sprintf("NOT (%s)::boolean", rowSelecter) - } - - case *schema_j5pb.Field_Timestamp: - - stringVal, ok := dbVal.(string) - if !ok { - return nil, fmt.Errorf("sort field %s is a timestamp, but value is not a string", sortField.errorName()) - } - ts, err := time.Parse(time.RFC3339, stringVal) - if err != nil { - return nil, fmt.Errorf("sort field %s is a timestamp, but value is not a valid timestamp: %w", sortField.errorName(), err) - } - - intVal := ts.Round(time.Microsecond).UnixMicro() - // Go rounds half-up. - // Postgres is undocumented, but can only handle - // microseconds. - rowSelecter = fmt.Sprintf("(EXTRACT(epoch FROM (%s)::timestamp) * 1000000)::bigint", rowSelecter) - if sortField.desc { - intVal = intVal * -1 - rowSelecter = fmt.Sprintf("-1 * %s", rowSelecter) - } - dbVal = intVal - - case *schema_j5pb.Field_Date: - stringVal, ok := dbVal.(string) - if !ok { - return nil, fmt.Errorf("sort field %s is a date, but value is not a string", sortField.errorName()) - } - - date, err := date_j5t.DateFromString(stringVal) - if err != nil { - return nil, fmt.Errorf("sort field %s is a date, but value is not a valid date: %w", sortField.errorName(), err) - } - intVal := date.AsTime(time.UTC).Round(time.Microsecond).Unix() - - rowSelecter = fmt.Sprintf("(EXTRACT(epoch FROM (%s)::date))::bigint", rowSelecter) - if sortField.desc { - intVal = intVal * -1 - rowSelecter = fmt.Sprintf("-1 * %s", rowSelecter) - } - dbVal = intVal - - } - - default: - return nil, fmt.Errorf("sort field %s is not a scalar or enum", sortField.errorName()) - } - - lhsFields = append(lhsFields, rowSelecter) - rhsValues = append(rhsValues, dbVal) - rhsPlaceholders = append(rhsPlaceholders, valuePlaceholder) - } - - // From https://www.postgresql.org/docs/current/functions-comparisons.html#ROW-WISE-COMPARISON - // >> for the <, <=, > and >= cases, the row elements are compared left-to-right, stopping as soon - // >> as an unequal or null pair of elements is found. If either of this pair of elements is null, - // >> the result of the row comparison is unknown (null); otherwise comparison of this pair of elements - // >> determines the result. For example, ROW(1,2,NULL) < ROW(1,3,0) yields true, not null, because the - // >> third pair of elements are not considered. - // - // This means that we can use the row comparison with the same fields as - // the sort fields to exclude the exact record we want, rather than the - // filter being applied to all fields equally which takes out valid - // records. - // `(1, 30) >= (1, 20)` is true, so is `1 >= 1 AND 30 >= 20` - // `(2, 10) >= (1, 20)` is also true, but `2 >= 1 AND 10 >= 20` is false - // Since the tuple comparison starts from the left and stops at the first term. - // - // The downside is that we have to negate the values to sort in reverse - // order, as we don't get an operator per term. This gets strange for - // some data types and will create some crazy indexes. - // - // TODO: Optimize the cases when the order is ASC and therefore we don't - // need to flip, but also the cases where we can just reverse the whole - // comparison and reverse all flips to simplify, noting again that it - // does not actually matter in which order the string field is sorted... - // or don't because indexes. - - return sq.Expr( - fmt.Sprintf("(%s) >= (%s)", - strings.Join(lhsFields, ","), - strings.Join(rhsPlaceholders, ","), - ), rhsValues...), nil - -} -func (ll *Lister) getPageSize(req j5reflect.Object) (uint64, error) { - pageField, ok, err := req.GetField(ll.pageRequestField.JSONName, "pageSize") - if err != nil { - return 0, fmt.Errorf("get page request field %s: %w", ll.pageRequestField.JSONName, err) - } - if !ok { - return ll.defaultPageSize, nil - } - - val, ok := pageField.AsScalar() - if !ok { - return 0, fmt.Errorf("field %s in request is not a scalar", ll.pageRequestField.JSONName) - } - intVal, err := val.ToGoValue() - if err != nil { - return 0, fmt.Errorf("convert page request field %s to Go value: %w", ll.pageRequestField.JSONName, err) - } - - int64Val, ok := intVal.(int64) - if !ok { - return 0, fmt.Errorf("field %s in request is not an int64, got %T", ll.pageRequestField.JSONName, intVal) - } - - returnVal := uint64(int64Val) - if returnVal > ll.defaultPageSize { - return 0, fmt.Errorf("page size %d exceeds maximum of %d", val, ll.defaultPageSize) - } - return returnVal, nil -} - -func validateListAnnotations(object *j5schema.ObjectSchema) error { - /* - err := validateSortsAnnotations(object.Properties) - if err != nil { - return fmt.Errorf("sort: %w", err) - }*/ - - /* - err = validateSearchesAnnotations(object) - if err != nil { - return fmt.Errorf("search: %w", err) - }*/ - - return nil -} - -func (ll *Lister) validateQueryRequest(query *list_j5pb.QueryRequest) error { - err := validateQueryRequestSorts(ll.arrayObject, query.GetSorts()) - if err != nil { - return fmt.Errorf("sort validation: %w", err) - } - - err = validateQueryRequestFilters(ll.arrayObject, query.GetFilters()) - if err != nil { - return fmt.Errorf("filter validation: %w", err) - } - - /* - err = validateQueryRequestSearches(ll.arrayObject, query.GetSearches()) - if err != nil { - return fmt.Errorf("search validation: %w", err) - }*/ - - return nil -} diff --git a/pquery/lister_test.go b/pquery/lister_test.go deleted file mode 100644 index 242732c..0000000 --- a/pquery/lister_test.go +++ /dev/null @@ -1,544 +0,0 @@ -package pquery - -import ( - "strings" - "testing" - - "github.com/pentops/flowtest/prototest" - "github.com/pentops/golib/gl" - "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - "github.com/pentops/j5/lib/j5schema" - "github.com/stretchr/testify/assert" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" -) - -type composed struct { - FooListRequest string - FooListResponse string - Foo string -} - -func (c composed) toString() string { - out := "" - if c.FooListRequest != "" { - out += "message FooListRequest {\n" + c.FooListRequest + "\n}\n" - } else { - out += `message FooListRequest { - j5.list.v1.PageRequest page = 1; - j5.list.v1.QueryRequest query = 2; - - option (j5.list.v1.list_request) = { - sort_tiebreaker: ["id"] - }; - }` - } - - if c.FooListResponse != "" { - out += "message FooListResponse {\n" + c.FooListResponse + "\n}\n" - } else { - out += `message FooListResponse { - repeated Foo foos = 1; - j5.list.v1.PageResponse page = 2; - }` - } - - if c.Foo != "" { - out += "message Foo {\n" + c.Foo + "\n}\n" - } else { - out += `message Foo { - string id = 1; - }` - } - return out -} - -type J5Proto struct { - proto.Message -} - -type tableMod func(t testing.TB, spec *TableSpec, req, res protoreflect.MessageDescriptor) - -func testListReflection(t testing.TB, input string, spec tableMod) (*ListReflectionSet, error) { - t.Helper() - pdf := prototest.DescriptorsFromSource(t, map[string]string{ - "test.proto": ` - syntax = "proto3"; - - package test; - - // Import everything which may be used - import "j5/ext/v1/annotations.proto"; - import "j5/list/v1/page.proto"; - import "j5/list/v1/query.proto"; - import "j5/list/v1/annotations.proto"; - import "j5/types/date/v1/date.proto"; - import "buf/validate/validate.proto"; - import "google/protobuf/timestamp.proto"; - - service FooService { - rpc FooList(FooListRequest) returns (FooListResponse) {} - } - - ` + input, - }) - - requestDesc := pdf.MessageByName(t, "test.FooListRequest") - responseDesc := pdf.MessageByName(t, "test.FooListResponse") - table := &TableSpec{ - DataColumn: "data", - } - if spec != nil { - spec(t, table, requestDesc, responseDesc) - } - - schemaSet := j5schema.NewSchemaCache() - requestObj, err := schemaSet.Schema(requestDesc) - if err != nil { - return nil, err - } - responseObj, err := schemaSet.Schema(responseDesc) - if err != nil { - return nil, err - } - ms := &j5schema.MethodSchema{ - Request: requestObj.(*j5schema.ObjectSchema), - Response: responseObj.(*j5schema.ObjectSchema), - } - - return buildListReflection(ms, *table) -} - -func TestBuildListReflection(t *testing.T) { - - runHappy := func(name string, input string, spec tableMod, callback func(*testing.T, *ListReflectionSet)) { - t.Helper() - t.Run(name, func(t *testing.T) { - t.Helper() - set, err := testListReflection(t, input, spec) - - if err != nil { - t.Fatal(err) - } - callback(t, set) - }) - } - runSad := func(name string, input string, spec tableMod, wantError string) { - t.Helper() - t.Run(name, func(t *testing.T) { - t.Helper() - _, err := testListReflection(t, input, spec) - if err == nil { - t.Fatal("expected error") - } - if !strings.Contains(err.Error(), wantError) { - t.Errorf("expected error to contain '%s', got '%s'", wantError, err.Error()) - } - }) - } - - // Successes - - runHappy("full success", ` - message FooListRequest { - j5.list.v1.PageRequest page = 1; - j5.list.v1.QueryRequest query = 2; - - option (j5.list.v1.list_request) = { - sort_tiebreaker: ["id"] - }; - } - - message FooListResponse { - repeated Foo foos = 1; - j5.list.v1.PageResponse page = 2; - } - - message Foo { - string id = 1; - } - `, - nil, - func(t *testing.T, lr *ListReflectionSet) { - if len(lr.tieBreakerFields) != 1 { - t.Error("expected one sort tiebreaker") - } else { - field := lr.tieBreakerFields[0] - assert.Equal(t, "->>'id'", field.Path.JSONBArrowPath()) - } - assert.Equal(t, uint64(20), lr.defaultPageSize) - }) - - runHappy("dynamic filter", ` - message FooListRequest { - j5.list.v1.PageRequest page = 1; - j5.list.v1.QueryRequest query = 2; - option (j5.list.v1.list_request) = { - sort_tiebreaker: ["val"] - }; - } - - message FooListResponse { - repeated Foo foos = 1; - j5.list.v1.PageResponse page = 2; - } - - message Foo { - int64 val = 1 [(j5.list.v1.field).int64.filtering.filterable = true]; - } - `, nil, func(t *testing.T, lr *ListReflectionSet) { - - statements, err := lr.buildDynamicFilter("ALIAS", []*list_j5pb.Filter{{ - Type: &list_j5pb.Filter_Field{ - Field: &list_j5pb.Field{ - Name: "val", - Type: &list_j5pb.FieldType{ - Type: &list_j5pb.FieldType_Value{ - Value: "e10", - }, - }, - }, - }, - }}) - - if err != nil { - t.Fatal(err) - } - - if len(statements) != 1 { - t.Fatal("expected one statement, got", len(statements)) - } - statement := statements[0] - - txt, params, err := statement.ToSql() - if err != nil { - t.Fatal(err) - } - t.Logf("SQL: %s, Params: %v", txt, params) - want := "(jsonb_path_query_array(ALIAS.data, '$.val') @> ?::jsonb)" - if txt != want { - t.Errorf("SQL mismatch:\n got: %s\nwant: %s", txt, want) - } - - }) - - runHappy("override default page size by validation", ` - message FooListRequest { - j5.list.v1.PageRequest page = 1; - j5.list.v1.QueryRequest query = 2; - - option (j5.list.v1.list_request) = { - sort_tiebreaker: ["id"] - }; - } - - message FooListResponse { - repeated Foo foos = 1 [ - (buf.validate.field).repeated.max_items = 10 - ]; - j5.list.v1.PageResponse page = 2; - } - - message Foo { - string id = 1; - } - `, - nil, - func(t *testing.T, lr *ListReflectionSet) { - assert.EqualValues(t, int(10), int(lr.defaultPageSize)) - }) - - runHappy("tie breaker fallback", composed{ - FooListRequest: ` - j5.list.v1.PageRequest page = 1; - j5.list.v1.QueryRequest query = 2; - `, - }.toString(), - func(t testing.TB, table *TableSpec, req, res protoreflect.MessageDescriptor) { - table.FallbackSortColumns = []ProtoField{{ - valueColumn: gl.Ptr("id"), - pathInRoot: JSONPathSpec{"id"}, - }} - }, - func(t *testing.T, lr *ListReflectionSet) { - if len(lr.tieBreakerFields) != 1 { - t.Error("expected one sort tiebreaker") - } else { - field := lr.tieBreakerFields[0] - assert.Equal(t, "->>'id'", field.Path.JSONBArrowPath()) - } - }) - - runHappy("sort by bar", ` - message FooListRequest { - j5.list.v1.PageRequest page = 1; - j5.list.v1.QueryRequest query = 2; - - option (j5.list.v1.list_request) = { - sort_tiebreaker: ["bar.id"] - }; - } - - message FooListResponse { - repeated Foo foos = 1; - j5.list.v1.PageResponse page = 2; - } - - message Foo { - string id = 1; - Bar bar = 2; - } - - message Bar { - string id = 1; - } - `, - nil, - func(t *testing.T, lr *ListReflectionSet) { - if len(lr.tieBreakerFields) != 1 { - t.Error("expected one sort tiebreaker") - } else { - field := lr.tieBreakerFields[0] - assert.Equal(t, "->'bar'->>'id'", field.Path.JSONBArrowPath()) - assert.Equal(t, "$.bar.id", field.Path.JSONPathQuery()) - } - }) - - runHappy("sort by bar by walking", ` - message FooListRequest { - j5.list.v1.PageRequest page = 1; - j5.list.v1.QueryRequest query = 2; - - option (j5.list.v1.list_request) = { - }; - } - - message FooListResponse { - repeated Foo foos = 1; - j5.list.v1.PageResponse page = 2; - } - - message Foo { - string id = 1; - Bar bar = 2; - } - - message Bar { - string id = 1; - google.protobuf.Timestamp timestamp = 2 [ - (j5.list.v1.field).timestamp = { - sorting: { - default_sort: true - } - } - ]; - } - `, - nil, - func(t *testing.T, lr *ListReflectionSet) { - if len(lr.tieBreakerFields) != 0 { - t.Error("expected no sort tiebreaker") - } - - if len(lr.defaultSortFields) != 1 { - t.Error("expected one sort tiebreaker, got", len(lr.tieBreakerFields)) - } else { - field := lr.defaultSortFields[0] - assert.Equal(t, "->'bar'->>'timestamp'", field.Path.JSONBArrowPath()) - assert.Equal(t, "$.bar.timestamp", field.Path.JSONPathQuery()) - } - }) - - runHappy("filter by bar date", ` - message FooListRequest { - j5.list.v1.PageRequest page = 1; - j5.list.v1.QueryRequest query = 2; - - option (j5.list.v1.list_request) = { - sort_tiebreaker: ["bar.id"] - }; - } - - message FooListResponse { - repeated Foo foos = 1; - j5.list.v1.PageResponse page = 2; - } - - message Foo { - string id = 1; - Bar bar = 2; - } - - message Bar { - string id = 1; - j5.types.date.v1.Date date = 2 [ - (j5.list.v1.field).date.filtering = { - filterable: true, - default_filters: ["2025-01-01"] - } - ]; - } - `, - nil, - func(t *testing.T, lr *ListReflectionSet) { - if len(lr.defaultFilterFields) != 1 { - t.Error("expected one filter field, got", len(lr.defaultFilterFields)) - } else { - field := lr.defaultFilterFields[0] - assert.Equal(t, "->'bar'->>'date'", field.Path.JSONBArrowPath()) - assert.Equal(t, "$.bar.date", field.Path.JSONPathQuery()) - } - }) - - // Response Errors - - runSad("non message field in response", composed{ - FooListResponse: ` - repeated Foo foos = 1; - j5.list.v1.PageResponse page = 2; - Foo dangling = 3; - `, - }.toString(), - nil, - "unknown field") - - runSad("non message in response", composed{ - FooListResponse: ` - repeated Foo foos = 1; - j5.list.v1.PageResponse page = 2; - string dangling = 3; - `, - }.toString(), - nil, - "unknown field", - ) - - runSad("extra array field in response", composed{ - FooListResponse: ` - repeated Foo foos = 1; - j5.list.v1.PageResponse page = 2; - repeated Foo dangling = 3; - `, - }.toString(), - nil, - "multiple repeated") - - runSad("no array field in response", composed{ - FooListResponse: ` - j5.list.v1.PageResponse page = 2; - `, - }.toString(), - nil, - "does not contain a repeated message field", - ) - - runSad("no page field in response", composed{ - FooListResponse: ` - repeated Foo foos = 1; - `, - }.toString(), - nil, - "no page field in response", - ) - - // Request Errors - - runSad("no fallback sort field", composed{ - FooListRequest: ` - j5.list.v1.PageRequest page = 1; - j5.list.v1.QueryRequest query = 2; - `, - }.toString(), - nil, - "no default sort field", - ) - - runSad("tie breaker not in response", composed{ - FooListRequest: ` - j5.list.v1.PageRequest page = 1; - j5.list.v1.QueryRequest query = 2; - option (j5.list.v1.list_request) = { - sort_tiebreaker: ["missing"] - }; - `, - }.toString(), - nil, - `unknown proto field "missing"`, - ) - - runSad("no page field", composed{ - FooListRequest: ` - j5.list.v1.QueryRequest query = 2; - - option (j5.list.v1.list_request) = { - sort_tiebreaker: ["id"] - }; - `, - }.toString(), - nil, - "no page field in request", - ) - - runSad("no query field", composed{ - FooListRequest: ` - j5.list.v1.PageRequest page = 1; - - option (j5.list.v1.list_request) = { - sort_tiebreaker: ["id"] - }; - `, - }.toString(), - nil, - "no query field in request", - ) - - runSad("repeated field sort", ` - message FooListRequest { - j5.list.v1.PageRequest page = 1; - j5.list.v1.QueryRequest query = 2; - } - - message FooListResponse { - repeated Foo foos = 1; - j5.list.v1.PageResponse page = 2; - } - - message Foo { - string id = 1; - int64 seq = 2 [(j5.list.v1.field).int64.sorting = {sortable: true, default_sort: true}]; - repeated int64 weight = 3 [(j5.list.v1.field).int64.sorting.sortable = true]; - } - `, - nil, - "list constraints not supported for arrays", - ) - - /* - Test Change: Repeated isn't walked for sorting, but isn't invalid in case Profile is used in another non-array context. - runSad("repeated sub field sort", ` - message FooListRequest { - j5.list.v1.PageRequest page = 1; - j5.list.v1.QueryRequest query = 2; - } - - message FooListResponse { - repeated Foo foos = 1; - j5.list.v1.PageResponse page = 2; - } - - message Foo { - string id = 1; - int64 seq = 2 [(j5.list.v1.field).int64.sorting = {sortable: true, default_sort: true}]; - repeated Profile profiles = 3; - } - - message Profile { - string name = 1; - int64 weight = 2 [(j5.list.v1.field).int64.sorting.sortable = true]; - } - `, - nil, - "sorting not allowed on subfield of repeated parent", - ) - */ -} diff --git a/pquery/method.go b/pquery/method.go deleted file mode 100644 index b3b6dbb..0000000 --- a/pquery/method.go +++ /dev/null @@ -1,30 +0,0 @@ -package pquery - -import ( - "fmt" - - "github.com/pentops/j5/lib/j5reflect" - "github.com/pentops/j5/lib/j5schema" -) - -func assertSchemaMatch(wantSchema *j5schema.ObjectSchema, gotObject j5reflect.Object) error { - wantSchemaName := wantSchema.FullName() - gotSchemaName := gotObject.SchemaName() - if wantSchemaName != gotSchemaName { - return fmt.Errorf("provided object is type %s, expects %s", - gotSchemaName, - wantSchemaName, - ) - } - return nil -} -func assertObjectsMatch(ms *j5schema.MethodSchema, req, res j5reflect.Object) error { - if err := assertSchemaMatch(ms.Request, req); err != nil { - return fmt.Errorf("request object: %w", err) - } - if err := assertSchemaMatch(ms.Response, res); err != nil { - return fmt.Errorf("response object: %w", err) - } - - return nil -} diff --git a/pquery/path.go b/pquery/path.go deleted file mode 100644 index 7e6a9e1..0000000 --- a/pquery/path.go +++ /dev/null @@ -1,410 +0,0 @@ -package pquery - -import ( - "fmt" - "strings" - - "github.com/pentops/j5/lib/j5reflect" - "github.com/pentops/j5/lib/j5schema" -) - -type Table interface { - TableName() string -} - -type NestedField struct { - - // The column containing the element JSONB - RootColumn string - - // The path from the column root to the node - Path Path - - // ValueColumn contains the value of the field directly in the table in - // addition to nested within the JSONB data. - ValueColumn *string -} - -func (nf *NestedField) ProtoChild(name string) (*NestedField, error) { - pathChild, err := nf.Path.Child(name) - if err != nil { - return nil, err - } - return &NestedField{ - RootColumn: nf.RootColumn, - Path: *pathChild, - }, nil -} - -func (nf *NestedField) Selector(inTable string) string { - if nf.ValueColumn != nil { - return fmt.Sprintf("%s.%s", inTable, *nf.ValueColumn) - } - if len(nf.Path.path) == 0 { - return fmt.Sprintf("%s.%s", inTable, nf.RootColumn) - } - return fmt.Sprintf("%s.%s%s", inTable, nf.RootColumn, nf.Path.JSONBArrowPath()) -} - -type pathNode struct { - name string - field *j5schema.ObjectProperty -} - -type Path struct { - root *j5schema.ObjectSchema - path []pathNode - - leafField *j5schema.ObjectProperty - leafOneof *j5schema.OneofSchema // If the leaf is a oneof, this is the oneof schema -} - -func (pp Path) Root() *j5schema.ObjectSchema { - return pp.root -} - -func (pp Path) LeafField() *j5schema.ObjectProperty { - return pp.leafField -} - -func (pp Path) LeafOneof() *j5schema.OneofSchema { - return pp.leafOneof -} - -func (pp Path) Child(name string) (*Path, error) { - if pp.leafField == nil { - return nil, fmt.Errorf("child requires a message field") - } - - var propSet j5schema.PropertySet - - switch st := pp.leafField.Schema.(type) { - case *j5schema.ObjectField: - propSet = st.ObjectSchema().Properties - case *j5schema.OneofField: - propSet = st.OneofSchema().Properties - default: - return nil, fmt.Errorf("child requires a message field") - } - - field := propSet.ByJSONName(name) - if field == nil { - return nil, fmt.Errorf("field %s not found in message %s", name, pp.leafField.FullName()) - } - return &Path{ - root: pp.root, - path: append(pp.path, pathNode{ - name: name, - field: field, - }), - }, nil -} - -// IDPath uniquely identifies the path within a specific root type context -func (pp Path) IDPath() string { - return pp.pathNodeNames() -} - -func (pp Path) DebugName() string { - return fmt.Sprintf("%s:%s", pp.root.FullName(), pp.pathNodeNames()) -} - -func (pp Path) pathNodeNames() string { - names := make([]string, 0, len(pp.path)) - for _, node := range pp.path { - names = append(names, string(node.name)) - } - return strings.Join(names, ".") -} - -func (pp *Path) pathParts() []string { - parts := make([]string, 0, len(pp.path)) - for _, node := range pp.path { - parts = append(parts, node.field.JSONName) - } - return parts -} - -func (pp *Path) JSONBArrowPath() string { - elements := make([]string, 0, len(pp.path)) - end, path := pp.path[len(pp.path)-1], pp.path[:len(pp.path)-1] - for _, part := range path { - if obj, ok := part.field.Schema.(*j5schema.ObjectField); ok { - if obj.Flatten { - continue // Flattened fields are not in the JSONB tree - } - } - if part.field == nil { - panic(fmt.Sprintf("invalid path: %v", pp.DebugName())) - } - - switch part.field.Schema.(type) { - case *j5schema.MapField: - panic("map fields not supported by JSONBArrowPath()") - case *j5schema.ArrayField: - panic("array fields not supported by JSONBArrowPath()") - } - elements = append(elements, fmt.Sprintf("->'%s'", part.field.JSONName)) - } - - return fmt.Sprintf("%s->>'%s'", strings.Join(elements, ""), end.field.JSONName) -} - -func (pp *Path) JSONPathQuery() string { - elements := make([]string, 1, len(pp.path)+1) - elements[0] = "$" // Used sometimes, not always? - for _, part := range pp.path { - if obj, ok := part.field.Schema.(*j5schema.ObjectField); ok { - if obj.Flatten { - continue // Flattened fields are not in the JSONB tree - } - } - if part.field == nil { - panic(fmt.Sprintf("invalid path: %v", pp.DebugName())) - } - elements = append(elements, fmt.Sprintf(".%s", part.field.JSONName)) - switch part.field.Schema.(type) { - case *j5schema.MapField: - panic("map fields not supported by JSONBArrowPath()") - case *j5schema.ArrayField: - elements = append(elements, "[*]") - } - } - - if pp.leafOneof != nil { - // Can't use quotes, must escape the ! - elements = append(elements, `.\!type`) - } - - return strings.Join(elements, "") -} - -// WalkPathNodes visits every field in the message tree other than the root -// message itself, calling the callback for each. -func WalkPathNodes(rootMessage *j5schema.ObjectSchema, callback func(Path) error) error { - root := &Path{ - root: rootMessage, - } - return root.walk(rootMessage.Properties, callback) -} - -func (pp Path) walk(props j5schema.PropertySet, callback func(Path) error) error { - // walks only fields, not oneofs. - for _, field := range props { - fieldPath := append(pp.path, pathNode{ - name: field.JSONName, - field: field, - }) - fieldPathSpec := Path{ - root: pp.root, - path: fieldPath, - leafField: field, - } - - if err := callback(fieldPathSpec); err != nil { - return err - } - - switch ft := field.Schema.(type) { - case *j5schema.ObjectField: - if err := fieldPathSpec.walk(ft.ObjectSchema().Properties, callback); err != nil { - return fmt.Errorf("walking %s: %w", field.JSONName, err) - } - - case *j5schema.OneofField: - if err := fieldPathSpec.walk(ft.OneofSchema().Properties, callback); err != nil { - return fmt.Errorf("walking %s: %w", field.JSONName, err) - } - } - } - - return nil - -} - -// Like ProtoPathSpec but uses JSON field names -type JSONPathSpec []string - -func JSONPath(path ...string) JSONPathSpec { - return JSONPathSpec(path) -} - -func ParseJSONPathSpec(path string) JSONPathSpec { - return JSONPathSpec(strings.Split(path, ".")) -} - -func (jp JSONPathSpec) String() string { - return strings.Join(jp, ".") -} - -func NewJSONPath(rootMessage *j5schema.ObjectSchema, fieldPath JSONPathSpec) (*Path, error) { - return newPath(rootMessage, []string(fieldPath), clientPath) -} - -func NewOuterPath(rootMessage *j5schema.ObjectSchema, fieldPath JSONPathSpec) (*Path, error) { - return newPath(rootMessage, []string(fieldPath), outerPath) -} - -type pathType int - -const ( - clientPath pathType = iota // the JSON path from the client side, flattened fields are anonymous - outerPath // flattened fields included with their name -) - -type hasPropertySet interface { - AllProperties() j5schema.PropertySet - ClientProperties() j5schema.PropertySet - FullName() string -} - -func objectProperty(obj hasPropertySet, pathElem string, pt pathType) (*j5schema.ObjectProperty, error) { - switch pt { - case clientPath: - prop := obj.ClientProperties().ByJSONName(pathElem) - if prop == nil { - return nil, fmt.Errorf("client property %q not found in %s", pathElem, obj.FullName()) - } - return prop, nil - case outerPath: - prop := obj.AllProperties().ByJSONName(pathElem) - if prop == nil { - return nil, fmt.Errorf("property %q not found in %s", pathElem, obj.FullName()) - } - return prop, nil - - default: - return nil, fmt.Errorf("unknown path type %d", pt) - } - -} - -func newPath(rootMessage *j5schema.ObjectSchema, fieldPath []string, pathType pathType) (*Path, error) { - - if len(fieldPath) == 0 { - return nil, fmt.Errorf("fieldPath must have at least one element") - } - - pathSpec := &Path{ - root: rootMessage, - path: make([]pathNode, 0, len(fieldPath)), - } - - var walkMessage j5schema.RootSchema - walkMessage = rootMessage - var pathElem string - walkPath := fieldPath - - for { - pathElem, walkPath = walkPath[0], walkPath[1:] - var node pathNode - switch walkMessage := walkMessage.(type) { - case *j5schema.ObjectSchema: - field, err := objectProperty(walkMessage, pathElem, pathType) - if err != nil { - return nil, err - } - node = pathNode{ - name: pathElem, - field: field, - } - - case *j5schema.OneofSchema: - - if len(walkPath) == 0 && pathElem == "!type" { - // Very Special Edge Case: Oneof wrapper types allow the client to - // filter based on the type of the oneof. So the oneof can be at the - // end of the path, and the field can be the oneof wrapper type. - pathSpec.leafOneof = walkMessage - return pathSpec, nil - } - - field, err := objectProperty(walkMessage, pathElem, pathType) - if err != nil { - return nil, err - } - - node = pathNode{ - name: pathElem, - field: field, - } - default: - return nil, fmt.Errorf("field %q in %s.%s is not an object or oneof, no such field %q", pathElem, rootMessage.FullName(), strings.Join(fieldPath, "."), strings.Join(walkPath, ".")) - } - - pathSpec.path = append(pathSpec.path, node) - pathSpec.leafField = node.field - - if len(walkPath) == 0 { - return pathSpec, nil - } - - switch nextType := node.field.Schema.(type) { - case *j5schema.ObjectField: - walkMessage = nextType.ObjectSchema() - case *j5schema.OneofField: - walkMessage = nextType.OneofSchema() - case j5schema.RootSchema: - walkMessage = nextType - case *j5schema.ArrayField: - items := nextType.ItemSchema - switch items := items.(type) { - case *j5schema.ObjectField: - walkMessage = items.ObjectSchema() - case *j5schema.OneofField: - walkMessage = items.OneofSchema() - default: - return nil, fmt.Errorf("array field %q in %s.%s is an array, but not of an object or oneof, no such field %q", pathElem, rootMessage.FullName(), strings.Join(fieldPath, "."), strings.Join(walkPath, ".")) - } - case *j5schema.MapField: - return nil, fmt.Errorf("map fields not supported in path %q", strings.Join(fieldPath, ".")) - default: - return nil, fmt.Errorf("field %q in %s.%s is a scalar, no such field %q", pathElem, rootMessage.FullName(), strings.Join(fieldPath, "."), strings.Join(walkPath, ".")) - } - } - -} - -func (pp *Path) GetValue(msg j5reflect.Object) (any, bool, error) { - if len(pp.path) == 0 { - return nil, false, fmt.Errorf("empty path") - } - - lastField, _, err := msg.GetField(pp.pathParts()...) - if err != nil { - return nil, false, fmt.Errorf("getting field %s: %w", pp.pathNodeNames(), err) - } - - if pp.leafOneof != nil { - // this is a type filter. - oneof, ok := lastField.AsOneof() - if !ok { - return nil, false, fmt.Errorf("field %s is not a oneof", pp.pathNodeNames()) - } - - field, ok, err := oneof.GetOne() - if err != nil { - return nil, false, fmt.Errorf("getting oneof field %s: %w", pp.pathNodeNames(), err) - } - if !ok { - return nil, false, nil // No value set in the oneof - } - - val := field.NameInParent() - return val, true, nil - } - - scalar, ok := lastField.AsScalar() - if !ok { - return nil, false, fmt.Errorf("field %s is not a scalar", pp.pathNodeNames()) - } - - goVal, err := scalar.ToGoValue() - if err != nil { - return nil, false, fmt.Errorf("converting field %s to Go value: %w", pp.pathNodeNames(), err) - } - if goVal == nil { - return nil, false, nil // No value set in the scalar - } - return goVal, true, nil -} diff --git a/pquery/path_test.go b/pquery/path_test.go deleted file mode 100644 index a21e5f0..0000000 --- a/pquery/path_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package pquery - -import ( - "testing" - - "github.com/pentops/flowtest/prototest" - "github.com/pentops/j5/lib/j5schema" - "github.com/stretchr/testify/assert" -) - -func TestFindFieldSpec(t *testing.T) { - descFiles := prototest.DescriptorsFromSource(t, map[string]string{ - "test.proto": ` - syntax = "proto3"; - - - package test; - - message Foo { - string id = 1; - Profile profile = 2; - } - - message Profile { - int64 weight = 1; - ProfileType type = 2; - } - - message ProfileType { - oneof type { - Card card = 2; - } - } - - - message Card { - int64 size = 1; - } - `}) - - fooDesc := descFiles.MessageByName(t, "test.Foo") - fooSchema, err := j5schema.Global.Schema(fooDesc) - if err != nil { - t.Fatal(err) - } - fooObject, ok := fooSchema.(*j5schema.ObjectSchema) - if !ok { - t.Fatal("expected Foo to be an ObjectSchema") - } - - t.Run("Find Field", func(t *testing.T) { - - tcs := []struct { - path JSONPathSpec - isOneof bool - }{ - {path: JSONPathSpec{"id"}}, - {path: JSONPathSpec{"profile", "weight"}}, - {path: JSONPathSpec{"profile", "type"}}, - {path: JSONPathSpec{"profile", "type", "!type"}, isOneof: true}, - {path: JSONPathSpec{"profile", "type", "card", "size"}}, - } - - for _, tc := range tcs { - t.Run(tc.path.String()+" JSON", func(t *testing.T) { - spec, err := NewJSONPath(fooObject, tc.path) - if err != nil { - t.Fatal(err) - } - - if tc.isOneof { - if spec.leafOneof == nil { - t.Fatal("missing oneof field") - } - if spec.leafField == nil { - t.Fatal("missing leaf field in oneof.type") - } - } else { - if spec.leafField == nil { - t.Fatal("missing leaf field") - } - name := tc.path[len(tc.path)-1] - assert.Equal(t, string(spec.leafField.JSONName), name) - } - }) - } - }) - - t.Run("Sad Path", func(t *testing.T) { - tcs := []struct { - path JSONPathSpec - }{ - {path: JSONPathSpec{"foo"}}, - {path: JSONPathSpec{"profile", "weight", "size"}}, - } - - for _, tc := range tcs { - t.Run(tc.path.String()+" JSON Sad", func(t *testing.T) { - _, err := NewJSONPath(fooObject, tc.path) - if err == nil { - t.Fatal("expected an error") - } - }) - } - }) - - t.Run("Walk", func(t *testing.T) { - - expectedNodes := map[string]struct{}{ - "$.id": {}, - "$.profile": {}, - "$.profile.weight": {}, - "$.profile.type": {}, - "$.profile.type.card": {}, - "$.profile.type.card.size": {}, - } - - err := WalkPathNodes(fooObject, func(node Path) error { - pathQuery := node.JSONPathQuery() - t.Log(pathQuery) - _, ok := expectedNodes[pathQuery] - if !ok { - t.Fatalf("unexpected node %s", pathQuery) - } - delete(expectedNodes, pathQuery) - - return nil - }) - if err != nil { - t.Fatal(err) - } - for node := range expectedNodes { - t.Errorf("expected node %s", node) - } - }) -} diff --git a/pquery/pgmigrate/builder.go b/pquery/pgmigrate/builder.go deleted file mode 100644 index 7048f8a..0000000 --- a/pquery/pgmigrate/builder.go +++ /dev/null @@ -1,221 +0,0 @@ -package pgmigrate - -import ( - "fmt" - "strings" - - "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" -) - -type CreateTableBuilder struct { - name string - columns []*column - foreignKeys []*ForeignKeyBuilder -} - -func CreateTable(name string) *CreateTableBuilder { - return &CreateTableBuilder{ - name: name, - } -} - -func (t *CreateTableBuilder) Column(name string, typ ColumnType, options ...ColumnOption) *CreateTableBuilder { - column := &column{ - name: name, - typeName: typ, - flags: []string{}, - } - for _, opt := range options { - opt(column) - } - t.columns = append(t.columns, column) - return t -} - -func (t *CreateTableBuilder) ForeignKey(name, tableName string) *ForeignKeyBuilder { - fk := &ForeignKeyBuilder{ - name: name, - tableName: tableName, - } - t.foreignKeys = append(t.foreignKeys, fk) - return fk -} - -type ForeignKeyBuilder struct { - name string - tableName string - columns []ColumnPair -} - -type ColumnPair struct { - local, foreign string -} - -func (fk *ForeignKeyBuilder) Column(localName, remoteName string) *ForeignKeyBuilder { - fk.columns = append(fk.columns, ColumnPair{local: localName, foreign: remoteName}) - return fk -} - -type column struct { - name string - - primaryKey bool // Multi Primary Key is possible - notNull bool - - typeName ColumnType - flags []string -} - -type ColumnOption func(*column) - -// PrimaryKey adds this column as a primary key, if there are multiple -// primary keys, they will be added as a composite key -func PrimaryKey(c *column) { - c.primaryKey = true -} - -func NotNull(c *column) { - c.notNull = true -} - -type ColumnType string - -const ( - UUID ColumnType = "uuid" - Text ColumnType = "text" - Timestamptz ColumnType = "timestamptz" - JSONB ColumnType = "jsonb" - Int ColumnType = "int" -) - -func (t *CreateTableBuilder) Build() (*Table, error) { - table := &Table{ - Name: t.name, - } - - for _, col := range t.columns { - column := Column{ - Name: col.name, - Type: string(col.typeName), - } - if col.primaryKey { - table.PrimaryKey = append(table.PrimaryKey, col.name) - } - if col.notNull { - column.Flags = append(column.Flags, "NOT NULL") - } - table.Columns = append(table.Columns, column) - } - - for _, fk := range t.foreignKeys { - foreignKey := ForeignKey{ - Name: fk.name, - TableName: fk.tableName, - Columns: fk.columns, - } - - table.ForeignKeys = append(table.ForeignKeys, foreignKey) - } - - return table, nil -} - -type Table struct { - Name string - Columns []Column - PrimaryKey []string - ForeignKeys []ForeignKey -} - -type Column struct { - Name string - Type string - Flags []string -} - -type ForeignKey struct { - Name string - TableName string - Columns []ColumnPair -} - -func (tt *Table) DownSQL() (string, error) { - return fmt.Sprintf("DROP TABLE %s;", tt.Name), nil -} - -func (table *Table) ToSQL() (string, error) { - clauses := make([]string, 0) - - for _, col := range table.Columns { - line := make([]string, 2+len(col.Flags)) - line[0] = col.Name - line[1] = col.Type - copy(line[2:], col.Flags) - clauses = append(clauses, strings.Join(line, " ")) - } - - if len(table.PrimaryKey) > 0 { - clauses = append(clauses, fmt.Sprintf("CONSTRAINT %s_pk PRIMARY KEY (%s)", table.Name, strings.Join(table.PrimaryKey, ", "))) - } - - for _, fk := range table.ForeignKeys { - localColumns := make([]string, 0, len(fk.Columns)) - remoteColumns := make([]string, 0, len(fk.Columns)) - for _, col := range fk.Columns { - localColumns = append(localColumns, col.local) - remoteColumns = append(remoteColumns, col.foreign) - } - - clauses = append(clauses, fmt.Sprintf("CONSTRAINT %s_fk_%s FOREIGN KEY (%s) REFERENCES %s(%s)", table.Name, fk.Name, strings.Join(localColumns, ", "), fk.TableName, strings.Join(remoteColumns, ", "))) - } - - lines := make([]string, 1, len(clauses)+2) - lines[0] = fmt.Sprintf("CREATE TABLE %s (", table.Name) - for idx, clause := range clauses { - suffix := "," - if idx == len(clauses)-1 { - suffix = "" - } - lines = append(lines, " "+clause+suffix) - } - lines = append(lines, ");") - return strings.Join(lines, "\n"), nil - -} - -const ( - uuidType = ColumnType("uuid") - textType = ColumnType("text") - id62Type = ColumnType("char(22)") - intType = ColumnType("int") - timestamptzType = ColumnType("timestamptz") - jsonbType = ColumnType("jsonb") - dateType = ColumnType("date") -) - -func FieldFormat(schema *schema_j5pb.Field) (ColumnType, error) { - - switch ft := schema.Type.(type) { - case *schema_j5pb.Field_String_: - return textType, nil - case *schema_j5pb.Field_Key: - if ft.Key.Format == nil { - return textType, nil - } - switch ft.Key.Format.Type.(type) { - case *schema_j5pb.KeyFormat_Custom_: - return textType, nil - case *schema_j5pb.KeyFormat_Uuid: - return uuidType, nil - case *schema_j5pb.KeyFormat_Id62: - return id62Type, nil - default: - return textType, nil - } - case *schema_j5pb.Field_Date: - return dateType, nil - default: - return textType, fmt.Errorf("unsupported type for key field %T", schema.Type) - } - -} diff --git a/pquery/pgmigrate/migrations.go b/pquery/pgmigrate/migrations.go deleted file mode 100644 index 22b113c..0000000 --- a/pquery/pgmigrate/migrations.go +++ /dev/null @@ -1,93 +0,0 @@ -package pgmigrate - -import ( - "bytes" - "context" - "fmt" - "strings" - - "github.com/pentops/sqrlx.go/sqrlx" -) - -type MigrationItem interface { - ToSQL() (string, error) - DownSQL() (string, error) -} - -func RunMigrations(ctx context.Context, db sqrlx.Transactor, migrations []MigrationItem) error { - - return db.Transact(ctx, nil, func(ctx context.Context, tx sqrlx.Transaction) error { - for _, migration := range migrations { - statement, err := migration.ToSQL() - if err != nil { - return err - } - if _, err := tx.ExecRaw(ctx, statement); err != nil { - return err - } - } - return nil - }) -} - -func PrintMigrations(items ...MigrationItem) ([]byte, error) { - p := newPrinter() - p.p("-- +goose Up") - p.setGap() - for _, table := range items { - val, err := table.ToSQL() - if err != nil { - return nil, err - } - p.p(val) - p.setGap() - } - p.p("-- +goose Down") - p.setGap() - for idx := len(items) - 1; idx >= 0; idx-- { - item := items[idx] - downVal, err := item.DownSQL() - if err != nil { - return nil, err - } - if len(strings.Split(downVal, "\n")) > 1 { - p.setGap() - p.p(downVal) - p.setGap() - } else { - p.p(downVal) - } - } - - return p.bytes(), nil -} - -type printer struct { - buf bytes.Buffer - gap bool -} - -func newPrinter() *printer { - return &printer{ - buf: bytes.Buffer{}, - } -} - -func (p *printer) setGap() { - p.gap = true -} - -func (p *printer) p(elem ...any) { - if p.gap { - fmt.Fprintln(&p.buf) - p.gap = false - } - for _, elem := range elem { - fmt.Fprint(&p.buf, elem) - } - fmt.Fprintln(&p.buf) -} - -func (p *printer) bytes() []byte { - return p.buf.Bytes() -} diff --git a/pquery/pgmigrate/search.go b/pquery/pgmigrate/search.go deleted file mode 100644 index 6189981..0000000 --- a/pquery/pgmigrate/search.go +++ /dev/null @@ -1,127 +0,0 @@ -package pgmigrate - -import ( - "context" - "fmt" - "strings" - - sq "github.com/elgris/sqrl" - "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/protostate/pquery" - "github.com/pentops/sqrlx.go/sqrlx" -) - -func IndexMigrations(spec pquery.TableSpec) ([]MigrationItem, error) { - allMigrations := make([]MigrationItem, 0) - indexes, err := buildIndexes(spec.TableName, spec.DataColumn, spec.RootObject) - if err != nil { - return nil, fmt.Errorf("building indexes: %w", err) - } - - for _, index := range indexes { - allMigrations = append(allMigrations, index) - } - - return allMigrations, nil -} - -func AddIndexes(ctx context.Context, conn sqrlx.Connection, specs ...pquery.TableSpec) error { - allIndexes := make([]searchSpec, 0) - for _, spec := range specs { - indexes, err := buildIndexes(spec.TableName, spec.DataColumn, spec.RootObject) - if err != nil { - return err - } - allIndexes = append(allIndexes, indexes...) - } - - return writeIndexes(ctx, conn, allIndexes) -} - -type searchSpec struct { - tsvColumn string - tableName string - columnName string - path pquery.Path -} - -func buildIndexes(tableName string, columnName string, rootType *j5schema.ObjectSchema) ([]searchSpec, error) { - - cols, err := pquery.TSVColumns(rootType) - if err != nil { - return nil, err - } - - specs := []searchSpec{} - - for _, col := range cols { - specs = append(specs, searchSpec{ - tsvColumn: col.ColumnName, - tableName: tableName, - columnName: columnName, - path: col.Path, - }) - - } - - return specs, nil - -} - -func (ss searchSpec) ToSQL() (string, error) { - statement := fmt.Sprintf("to_tsvector('english', jsonb_path_query_array(%s, '%s'))", ss.columnName, ss.path.JSONPathQuery()) - - lines := []string{} - - lines = append(lines, fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s tsvector GENERATED ALWAYS", ss.tableName, ss.tsvColumn)) - lines = append(lines, fmt.Sprintf(" AS (%s) STORED;", statement)) - lines = append(lines, "") - lines = append(lines, fmt.Sprintf("CREATE INDEX %s_%s_idx ON %s USING GIN (%s);", ss.tableName, ss.tsvColumn, ss.tableName, ss.tsvColumn)) - return strings.Join(lines, "\n"), nil - -} - -func (ss searchSpec) DownSQL() (string, error) { - return fmt.Sprintf("DROP INDEX %s_%s_idx;\nALTER TABLE %s DROP COLUMN %s;", ss.tableName, ss.tsvColumn, ss.tableName, ss.tsvColumn), nil -} - -func writeIndexes(ctx context.Context, conn sqrlx.Connection, specs []searchSpec) error { - - db, err := sqrlx.New(conn, sq.Dollar) - if err != nil { - return err - } - - return db.Transact(ctx, nil, func(ctx context.Context, tx sqrlx.Transaction) error { - for _, spec := range specs { - - var count int - - err := tx.QueryRow(ctx, sq.Select("COUNT(column_name)"). - From("information_schema.columns"). - Where("table_schema = CURRENT_SCHEMA"). - Where(sq.Eq{"table_name": spec.tableName, "column_name": spec.tsvColumn})).Scan(&count) - if err != nil { - return err - } - if count > 0 { - return nil - } - - statement := fmt.Sprintf("to_tsvector('english', jsonb_path_query_array(%s, '%s'))", spec.columnName, spec.path.JSONPathQuery()) - - _, err = tx.ExecRaw(ctx, fmt.Sprintf("ALTER TABLE %s ADD COLUMN %s tsvector GENERATED ALWAYS AS (%s) STORED;", spec.tableName, spec.tsvColumn, statement)) - if err != nil { - return err - } - - _, err = tx.ExecRaw(ctx, fmt.Sprintf("CREATE INDEX %s_%s_idx ON %s USING GIN (%s);", spec.tableName, spec.tsvColumn, spec.tableName, spec.tsvColumn)) - if err != nil { - return err - } - - } - return nil - }) - -} diff --git a/pquery/query.go b/pquery/query.go deleted file mode 100644 index 3252bb5..0000000 --- a/pquery/query.go +++ /dev/null @@ -1,41 +0,0 @@ -package pquery - -import ( - "context" - "fmt" - - "github.com/pentops/sqrlx.go/sqrlx" -) - -type aliasSet int - -func (as *aliasSet) Next(name string) string { - *as++ - return fmt.Sprintf("_%s__a%d", name, *as) -} - -func newAliasSet() *aliasSet { - return new(aliasSet) -} - -type Transactor interface { - Transact(ctx context.Context, opts *sqrlx.TxOptions, callback sqrlx.Callback) error -} - -type AuthProvider interface { - AuthFilter(ctx context.Context) (map[string]string, error) -} - -type AuthProviderFunc func(ctx context.Context) (map[string]string, error) - -func (f AuthProviderFunc) AuthFilter(ctx context.Context) (map[string]string, error) { - return f(ctx) -} - -// LeftJoin is a specification for joining in the form -// ON . =
. -// Main is defined in the outer struct holding this LeftJoin -type LeftJoin struct { - TableName string - On JoinFields -} diff --git a/pquery/search.go b/pquery/search.go deleted file mode 100644 index a9aebad..0000000 --- a/pquery/search.go +++ /dev/null @@ -1,264 +0,0 @@ -package pquery - -import ( - "fmt" - "regexp" - "strings" - - sq "github.com/elgris/sqrl" - "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" - "github.com/pentops/j5/lib/j5schema" -) - -/* - func validateSearchesAnnotations(props []*j5schema.ObjectProperty) error { - _, err := validateSearchesAnnotationsInner(props) - if err != nil { - return fmt.Errorf("search validation: %w", err) - } - return nil - } - - func validateSearchesAnnotationsInner(fields []*j5schema.ObjectProperty) (map[string]protoreflect.Name, error) { - // search annotations have a 'field_identifier' which specifies the database column name to use for the text-search-vector. - // This function validates that the field_identifier is unique for the given field-set - // In cases where the field is a message, it will recurse into the message to validate the field identifiers - - // When the same message is used in multiple places, any field of that - // message or a child message will, by definition, have the same field - // identifier, and is therefore invalid. - - // Search annotation cannot be used in repeated or map fields - // Recursion is already invalid. - - // Oneof fields, however, can have the same field identifier on different - // branches. - - ids := make(map[string]string) - - for _, field := range fields { - - err := validateSearchAnnotationsField(ids, field) - if err != nil { - return nil, err - } - } - - return ids, nil - } - -func validateSearchAnnotationsField(ids map[string]string, field *j5schema.ObjectProperty) error { - - switch bigType := field.Schema.(type) { - case *j5schema.ObjectField: - fields := bigType.ObjectSchema().Fields() - searchIdentifiers, err := validateSearchesAnnotationsInner(fields) - if err != nil { - return fmt.Errorf("object search validation: %w", err) - } - - for searchKey, usedIn := range searchIdentifiers { - if existing, ok := ids[searchKey]; ok { - return fmt.Errorf("field identifier '%s' is already used at %s", searchKey, existing) - } - ids[searchKey] = protoreflect.Name(fmt.Sprintf("%s.%s", field.JSONName, usedIn)) - } - - case *j5schema.OneofField: - fields := bigType.OneofSchema().Fields() - searchIdentifiers, err := validateSearchesAnnotationsInner(fields) - if err != nil { - return fmt.Errorf("oneof search validation: %w", err) - } - for searchKey, usedIn := range searchIdentifiers { - if existing, ok := ids[searchKey]; ok { - return fmt.Errorf("field identifier '%s' is already used at %s", searchKey, existing) - } - ids[searchKey] = protoreflect.Name(fmt.Sprintf("%s.%s", field.JSONName, usedIn)) - } - - case *j5schema.MapField: - items := bigType.MapSchema() - - case *j5schema.ArrayField: - - case *j5schema.ScalarField: - - schema := bigType - - } - } - - switch field.Kind() { - case protoreflect.StringKind: - fieldOpts, ok := proto.GetExtension(field.Options().(*descriptorpb.FieldOptions), list_j5pb.E_Field).(*list_j5pb.FieldConstraint) - if !ok { - return nil - } - - switch fieldOpts.GetString_().GetWellKnown().(type) { - case *list_j5pb.StringRules_OpenText: - searchOpts := fieldOpts.GetString_().GetOpenText().GetSearching() - if searchOpts == nil || !searchOpts.Searchable { - return nil - } - - if searchOpts.GetFieldIdentifier() == "" { - return fmt.Errorf("field '%s' is missing a field identifier", field.FullName()) - } - - if existing, ok := ids[searchOpts.GetFieldIdentifier()]; ok { - return fmt.Errorf("field identifier '%s' is already used at %s", searchOpts.GetFieldIdentifier(), existing) - } - - ids[searchOpts.GetFieldIdentifier()] = field.Name() - } - - return nil - case protoreflect.MessageKind: - msg := field.Message().Fields() - fields := make([]protoreflect.FieldDescriptor, msg.Len()) - for i := range msg.Len() { - fields[i] = msg.Get(i) - } - - searchIdentifiers, err := validateSearchesAnnotationsInner(fields) - if err != nil { - return fmt.Errorf("message search validation: %w", err) - } - for searchKey, usedIn := range searchIdentifiers { - if existing, ok := ids[searchKey]; ok { - return fmt.Errorf("field identifier '%s' is already used at %s", searchKey, existing) - } - - ids[searchKey] = protoreflect.Name(fmt.Sprintf("%s.%s", field.Name(), usedIn)) - } - } - - return nil - - func validateQueryRequestSearches(message *j5schema.ObjectSchema, searches []*list_j5pb.Search) error { - for _, search := range searches { - - spec, err := NewJSONPath(message, ParseJSONPathSpec(search.GetField())) - if err != nil { - return fmt.Errorf("field spec: %w", err) - } - - field := spec.LeafField() - if field == nil { - return fmt.Errorf("leaf '%s' is not a field", spec.JSONPathQuery()) - } - - // validate the fields are annotated correctly for the request query - searchOpts, ok := proto.GetExtension(field.Options().(*descriptorpb.FieldOptions), list_j5pb.E_Field).(*list_j5pb.FieldConstraint) - if !ok { - return fmt.Errorf("requested search field '%s' does not have any searchable constraints defined", search.Field) - } - - searchable := false - if searchOpts != nil { - switch field.Kind() { - case protoreflect.StringKind: - switch searchOpts.GetString_().WellKnown.(type) { - case *list_j5pb.StringRules_OpenText: - searchable = searchOpts.GetString_().GetOpenText().GetSearching().Searchable - } - } - } - - if !searchable { - return fmt.Errorf("requested search field '%s' is not searchable", search.Field) - } - } - - return nil - } -*/ -var rePgUnsafe = regexp.MustCompile(`[^a-zA-Z0-9_]`) - -func TSVColumns(message *j5schema.ObjectSchema) ([]*TSVColumn, error) { - return tsvColumns(message) -} - -func buildTsvColumnMap(message *j5schema.ObjectSchema) (map[string]string, error) { - out := make(map[string]string) - paths, err := tsvColumns(message) - if err != nil { - return nil, fmt.Errorf("build tsv column map: %w", err) - } - - for _, path := range paths { - out[path.IDPath()] = path.ColumnName - } - - return out, nil -} - -type TSVColumn struct { - Path - ColumnName string -} - -func tsvColumns(message *j5schema.ObjectSchema) ([]*TSVColumn, error) { - - usedColNames := make(map[string]struct{}) - out := []*TSVColumn{} - - err := WalkPathNodes(message, func(path Path) error { - field := path.LeafField() - switch bigSchema := field.Schema.(type) { - - case *j5schema.ScalarSchema: - innerSchema := bigSchema.ToJ5Field() - switch ist := innerSchema.Type.(type) { - case *schema_j5pb.Field_String_: - if ist.String_.ListRules == nil || ist.String_.ListRules.Searching == nil || !ist.String_.ListRules.Searching.Searchable { - return nil // not searchable - } - - idPath := path.IDPath() - columnName := rePgUnsafe.ReplaceAllString(idPath, "_") - columnName = strings.ToLower(columnName) - if _, exists := usedColNames[columnName]; exists { - // the character set isn't sufficient to guarantee - // uniqueness when dropping the casing, e.g., `foo_d` - // becomes `fooD`, which becomes `food`, and `food` itself - // could also be valid. - // It's unlikely to come up, but better throw here than - // behave unexpectedly later. - return fmt.Errorf("duplicate tsv column name %q for path %q", columnName, idPath) - } - usedColNames[columnName] = struct{}{} - - out = append(out, &TSVColumn{ - Path: path, - ColumnName: fmt.Sprintf("tsv_%s", columnName), - }) - } - } - - return nil - }) - if err != nil { - return nil, err - } - - return out, nil -} - -func (ll *Lister) buildDynamicSearches(tableAlias string, searches []*list_j5pb.Search) ([]sq.Sqlizer, error) { - out := []sq.Sqlizer{} - - for i := range searches { - col, ok := ll.tsvColumnMap[searches[i].GetField()] - if !ok { - return nil, fmt.Errorf("unknown search field %q", searches[i].GetField()) - } - - out = append(out, sq.And{sq.Expr(fmt.Sprintf("%s.%s @@ phraseto_tsquery(?)", tableAlias, col), searches[i].GetValue())}) - } - - return out, nil -} diff --git a/pquery/search_test.go b/pquery/search_test.go deleted file mode 100644 index a063dba..0000000 --- a/pquery/search_test.go +++ /dev/null @@ -1,264 +0,0 @@ -package pquery - -import ( - "testing" - - "github.com/pentops/flowtest/prototest" - "github.com/pentops/j5/lib/j5schema" - "github.com/stretchr/testify/assert" -) - -func TestBuildTsvColumnMap(t *testing.T) { - descFiles := prototest.DescriptorsFromSource(t, map[string]string{ - "test.proto": ` - syntax = "proto3"; - - import "j5/list/v1/annotations.proto"; - - package test; - - message Foo { - string unoptioned_field = 1; - string optioned_field = 2 [(j5.list.v1.field).string.open_text.searching = { - searchable: true, - }]; - Bar bar = 3; - } - - message Bar { - string unoptioned_field = 1; - string optioned_field = 2 [(j5.list.v1.field).string.open_text.searching = { - searchable: true, - }]; - } - `}) - - fooDesc := descFiles.MessageByName(t, "test.Foo") - schemaSet := j5schema.NewSchemaCache() - fooObj, err := schemaSet.ObjectSchema(fooDesc) - if err != nil { - t.Fatalf("failed to get schema for Foo: %v", err) - } - - columnMap, err := buildTsvColumnMap(fooObj) - if err != nil { - t.Fatalf("failed to build TSV column map: %v", err) - } - assert.Len(t, columnMap, 2) - - want := map[string]string{ - "optionedField": "tsv_optionedfield", - "bar.optionedField": "tsv_bar_optionedfield", - } - for f, c := range columnMap { - t.Log("field: ", f, "\tcolumn: ", c) - if want[f] != c { - t.Errorf("expected column %s to map to field %s, but got %s", f, want[f], c) - } - delete(want, f) - } - for k, v := range want { - t.Errorf("expected column %s to map to field %s, but it was not found", v, k) - } - -} - -/* -func TestValidateSearchAnnotations(t *testing.T) { - t.Run("happy path", func(t *testing.T) { - descFiles := prototest.DescriptorsFromSource(t, map[string]string{ - "test.proto": ` - syntax = "proto3"; - - import "j5/list/v1/annotations.proto"; - - package test; - - message Foo { - string unoptioned_field = 1; - string optioned_field = 2 [(j5.list.v1.field).string.open_text.searching = { - searchable: true, - field_identifier: "optioned_field" - }]; - Bar bar = 3; - } - - message Bar { - string unoptioned_field = 1; - string optioned_field = 2 [(j5.list.v1.field).string.open_text.searching = { - searchable: true, - field_identifier: "bar_optioned_field" - }]; - } - - `}) - - fooDesc := descFiles.MessageByName(t, "test.Foo") - fooObj, err := j5schema.Global.ObjectSchema(fooDesc) - if err != nil { - t.Fatalf("failed to get schema for Foo: %v", err) - } - - err = validateSearchesAnnotations(fooObj.Properties) - assert.NoError(t, err) - }) - - t.Run("multi path duplicates", func(t *testing.T) { - descFiles := prototest.DescriptorsFromSource(t, map[string]string{ - "test.proto": ` - syntax = "proto3"; - - import "j5/list/v1/annotations.proto"; - - package test; - - message Msg { - string unoptioned_field = 1; - string optioned_field = 2 [(j5.list.v1.field).string.open_text.searching = { - searchable: true, - field_identifier: "optioned_field" - }]; - } - - message Foo { - oneof type { - Type1 type1 = 1; - Type2 type2 = 2; - } - } - - message Bar { - Type1 type1 = 1; - Type2 type2 = 2; - } - - message Baz { - oneof set1 { - Type1 s1type1 = 1; - Type2 s1type2 = 2; - } - - oneof set2 { - Type3 type3 = 3; - Type4 type4 = 4; - } - - } - - message Type1 { - Msg msg = 1; - } - message Type2 { - Msg msg = 1; - } - message Type3 { - Msg msg = 1; - } - message Type4 { - Msg msg = 1; - } - `}) - - // Both Type1 and Type2 import the same Msg messaage, which means they - // have the same field identifier. - - // Bar is not mutually exclusive, so is not OK - - // This will be a common pattern for event messages. - - // The two instances of Msg within Foo are mutually exclusive, so is OK - fooDesc := descFiles.MessageByName(t, "test.Foo") - fooObj, err := j5schema.Global.ObjectSchema(fooDesc) - if err != nil { - t.Fatalf("failed to get schema for Foo: %v", err) - } - err = validateSearchesAnnotations(fooObj.Properties) - assert.NoError(t, err) - - // The two instances of Msg within Bar are NOT mutually exclusive, this - // is not OK. - barDesc := descFiles.MessageByName(t, "test.Bar") - barObj, err := j5schema.Global.ObjectSchema(barDesc) - if err != nil { - t.Fatalf("failed to get schema for Bar: %v", err) - } - err = validateSearchesAnnotations(barObj.Properties) - assert.Error(t, err) - - // Each oneof in Baz is OK by itself, but Type1 and Type3 can be set - // together, and so the search key can be duplicated. - bazDesc := descFiles.MessageByName(t, "test.Baz") - bazObj, err := j5schema.Global.ObjectSchema(bazDesc) - if err != nil { - t.Fatalf("failed to get schema for Baz: %v", err) - } - - err = validateSearchesAnnotations(bazObj.Properties) - assert.Error(t, err) - }) - - t.Run("duplicate field identifier", func(t *testing.T) { - descFiles := prototest.DescriptorsFromSource(t, map[string]string{ - "test.proto": ` - syntax = "proto3"; - - import "j5/list/v1/annotations.proto"; - - package test; - - message Foo { - string unoptioned_field = 1; - string optioned_field = 2 [(j5.list.v1.field).string.open_text.searching = { - searchable: true, - field_identifier: "optioned_field" - }]; - Bar bar = 3; - } - - message Bar { - string unoptioned_field = 1; - string optioned_field = 2 [(j5.list.v1.field).string.open_text.searching = { - searchable: true, - field_identifier: "optioned_field" - }]; - } - `}) - - fooDesc := descFiles.MessageByName(t, "test.Foo") - fooObj, err := j5schema.Global.ObjectSchema(fooDesc) - if err != nil { - t.Fatalf("failed to get schema for Foo: %v", err) - } - - err = validateSearchesAnnotations(fooObj.Properties) - assert.Error(t, err) - }) - - t.Run("missing field identifier", func(t *testing.T) { - descFiles := prototest.DescriptorsFromSource(t, map[string]string{ - "test.proto": ` - syntax = "proto3"; - - import "j5/list/v1/annotations.proto"; - - package test; - - message Foo { - string unoptioned_field = 1; - string optioned_field = 2 [(j5.list.v1.field).string.open_text.searching = { - searchable: true, - field_identifier: "" - }]; - } - `}) - - fooDesc := descFiles.MessageByName(t, "test.Foo") - fooObj, err := j5schema.Global.ObjectSchema(fooDesc) - if err != nil { - t.Fatalf("failed to get schema for Foo: %v", err) - } - - err = validateSearchesAnnotations(fooObj.Properties) - assert.Error(t, err) - }) -}*/ diff --git a/pquery/sort.go b/pquery/sort.go deleted file mode 100644 index 9a35f0e..0000000 --- a/pquery/sort.go +++ /dev/null @@ -1,189 +0,0 @@ -package pquery - -import ( - "fmt" - - "github.com/pentops/j5/gen/j5/list/v1/list_j5pb" - "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" - "github.com/pentops/j5/lib/j5schema" -) - -type sortSpec struct { - *NestedField - desc bool -} - -func (ss sortSpec) errorName() string { - return ss.Path.JSONPathQuery() -} - -func buildTieBreakerFields(dataColumn string, req *j5schema.ObjectSchema, arrayField *j5schema.ObjectSchema, fallback []ProtoField) ([]sortSpec, error) { - - if req.ListRequest != nil && len(req.ListRequest.SortTiebreaker) > 0 { - tieBreakerFields := make([]sortSpec, 0, len(req.ListRequest.SortTiebreaker)) - for _, tieBreaker := range req.ListRequest.SortTiebreaker { - spec, err := NewJSONPath(arrayField, tieBreaker) - if err != nil { - return nil, fmt.Errorf("field %s in annotated sort tiebreaker for %s: %w", tieBreaker, req.FullName(), err) - } - - tieBreakerFields = append(tieBreakerFields, sortSpec{ - NestedField: &NestedField{ - RootColumn: dataColumn, - Path: *spec, - }, - desc: false, - }) - } - - return tieBreakerFields, nil - } - - if len(fallback) == 0 { - return []sortSpec{}, nil - } - - tieBreakerFields := make([]sortSpec, 0, len(fallback)) - for _, tieBreaker := range fallback { - - path, err := NewJSONPath(arrayField, tieBreaker.pathInRoot) - if err != nil { - return nil, fmt.Errorf("field %s in fallback sort tiebreaker for %s: %w", tieBreaker.pathInRoot, req.FullName(), err) - } - - tieBreakerFields = append(tieBreakerFields, sortSpec{ - NestedField: &NestedField{ - Path: *path, - RootColumn: dataColumn, - ValueColumn: tieBreaker.valueColumn, - }, - desc: false, - }) - } - - return tieBreakerFields, nil -} - -func getFieldSorting(field *j5schema.ObjectProperty) *list_j5pb.SortingConstraint { - scalar, ok := field.Schema.(*j5schema.ScalarSchema) - if !ok { - return nil // only scalars are sortable - } - - schema := scalar.ToJ5Field() - - switch st := schema.Type.(type) { - case *schema_j5pb.Field_Float: - if st.Float.ListRules == nil { - return nil - } - return st.Float.ListRules.Sorting - case *schema_j5pb.Field_Integer: - if st.Integer.ListRules == nil { - return nil - } - return st.Integer.ListRules.Sorting - case *schema_j5pb.Field_Timestamp: - if st.Timestamp.ListRules == nil { - return nil - } - return st.Timestamp.ListRules.Sorting - case *schema_j5pb.Field_Decimal: - if st.Decimal.ListRules == nil { - return nil - } - return st.Decimal.ListRules.Sorting - default: - return nil - } -} - -func buildDefaultSorts(columnName string, message *j5schema.ObjectSchema) ([]sortSpec, error) { - var defaultSortFields []sortSpec - - err := WalkPathNodes(message, func(path Path) error { - field := path.LeafField() - if field == nil { - return nil // oneof or something - } - - sortConstraint := getFieldSorting(field) - if sortConstraint == nil { - return nil // not a sortable field - } - if sortConstraint.DefaultSort { - defaultSortFields = append(defaultSortFields, sortSpec{ - NestedField: &NestedField{ - RootColumn: columnName, - Path: path, - }, - desc: true, - }) - } - return nil - }) - if err != nil { - return nil, err - } - - return defaultSortFields, nil -} - -func (ll *Lister) buildDynamicSortSpec(sorts []*list_j5pb.Sort) ([]sortSpec, error) { - results := []sortSpec{} - direction := "" - for _, sort := range sorts { - pathSpec := ParseJSONPathSpec(sort.Field) - spec, err := NewJSONPath(ll.arrayObject, pathSpec) - if err != nil { - return nil, fmt.Errorf("dynamic filter: find field: %w", err) - } - - biggerSpec := &NestedField{ - Path: *spec, - RootColumn: ll.dataColumn, - } - - results = append(results, sortSpec{ - NestedField: biggerSpec, - desc: sort.Descending, - }) - - // TODO: Remove this constraint, we can sort by different directions once we have the reversal logic in place - // validate direction of all the fields is the same - if direction == "" { - direction = "ASC" - if sort.Descending { - direction = "DESC" - } - } else { - if (direction == "DESC" && !sort.Descending) || (direction == "ASC" && sort.Descending) { - return nil, fmt.Errorf("requested sorts have conflicting directions, they must all be the same") - } - } - } - - return results, nil -} - -func validateQueryRequestSorts(message *j5schema.ObjectSchema, sorts []*list_j5pb.Sort) error { - for _, sort := range sorts { - pathSpec := ParseJSONPathSpec(sort.Field) - spec, err := NewJSONPath(message, pathSpec) - if err != nil { - return fmt.Errorf("find field %s: %w", sort.Field, err) - } - - field := spec.LeafField() - if field == nil { - return fmt.Errorf("node %s is not a field", spec.DebugName()) - } - - sortAnnotation := getFieldSorting(field) - if sortAnnotation == nil || !sortAnnotation.Sortable { - return fmt.Errorf("requested sort field '%s' is not sortable", sort.Field) - } - } - - return nil -} diff --git a/pquery/types.go b/pquery/types.go deleted file mode 100644 index b839636..0000000 --- a/pquery/types.go +++ /dev/null @@ -1 +0,0 @@ -package pquery diff --git a/psm/state_query.go b/psm/state_query.go index cadfe2a..ad94ab2 100644 --- a/psm/state_query.go +++ b/psm/state_query.go @@ -6,7 +6,7 @@ import ( "github.com/pentops/j5/lib/j5reflect" "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/protostate/pquery" + "github.com/pentops/j5/lib/j5query" ) // QueryTableSpec the TableMap with descriptors for the messages, without using diff --git a/psm/table_map.go b/psm/table_map.go index ea7b9ab..8a452ad 100644 --- a/psm/table_map.go +++ b/psm/table_map.go @@ -8,7 +8,7 @@ import ( "github.com/iancoleman/strcase" "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/protostate/pquery" + "github.com/pentops/j5/lib/j5query" ) type TableMap struct { From 2a5bb568ff18a145a687353563ec61a26aadbd06 Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Tue, 29 Jul 2025 17:04:23 -0700 Subject: [PATCH 11/12] Strip Back --- go.mod | 4 +- go.sum | 2 + internal/dbconvert/dbconvert.go | 117 ------------------ internal/integration/universe_test.go | 4 +- psm/state_query.go | 2 +- psm/statemachine.go | 26 ++-- psm/table_map.go | 2 +- psmigrate/migrate.go | 10 -- {internal/pgstore/psmpg => psmigrate}/psm.go | 10 +- .../pgstore/psmpg => psmigrate}/psm_test.go | 2 +- 10 files changed, 30 insertions(+), 149 deletions(-) delete mode 100644 internal/dbconvert/dbconvert.go delete mode 100644 psmigrate/migrate.go rename {internal/pgstore/psmpg => psmigrate}/psm.go (95%) rename {internal/pgstore/psmpg => psmigrate}/psm_test.go (97%) diff --git a/go.mod b/go.mod index 22a45b3..31d4e2b 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/iancoleman/strcase v0.3.0 github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21 github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 - github.com/pentops/j5 v0.0.0-20250729201549-91d43d72f0bc + github.com/pentops/j5 v0.0.0-20250730000347-c9a449751d4e github.com/pentops/log.go v0.0.16 github.com/pentops/o5-messaging v0.0.0-20250619024104-7e07c29129f0 github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b @@ -46,5 +46,3 @@ require ( golang.org/x/text v0.25.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect ) - -replace github.com/pentops/j5 => /Users/daemonl/pentops/j5 diff --git a/go.sum b/go.sum index ff801f4..48ae166 100644 --- a/go.sum +++ b/go.sum @@ -77,6 +77,8 @@ github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21 h1:EIMCYtccNxV59a github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21/go.mod h1:vNp8crAKcH0f/sZU9frkmQLUeDsTIgMqV14kQtkAqC0= github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 h1:s5qtWT2/s79gy/wm3/bwvKYLK6u2AkW05JiLPqxraP0= github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63/go.mod h1:I58JIVvL1/nP4CEHGKGbBhvWIEA9mVkGeoviemaqanU= +github.com/pentops/j5 v0.0.0-20250730000347-c9a449751d4e h1:8JFNkPsm6R1O25PD785eaUdCFLHwa9LfrKC15iGvgaU= +github.com/pentops/j5 v0.0.0-20250730000347-c9a449751d4e/go.mod h1:i+eCdoCoDvULbEuPeoQm4q1YLw13oaJIlYMB+kaX6fM= github.com/pentops/log.go v0.0.16 h1:oxCuHSBOBPjfUVSXyOSEEdYUwytysj4T29/7T2FBp9Q= github.com/pentops/log.go v0.0.16/go.mod h1:yR34x8aMlvhdGvqgIU4+0MiLjJTKt0vpcgUnVN2nZV4= github.com/pentops/o5-messaging v0.0.0-20250619024104-7e07c29129f0 h1:aAt5rIhzFyF1RdT262LGHL0V4vBxcGCTWIeC1C0vZ20= diff --git a/internal/dbconvert/dbconvert.go b/internal/dbconvert/dbconvert.go deleted file mode 100644 index 6a210a1..0000000 --- a/internal/dbconvert/dbconvert.go +++ /dev/null @@ -1,117 +0,0 @@ -package dbconvert - -import ( - "database/sql/driver" - "fmt" - "reflect" - - sq "github.com/elgris/sqrl" - "github.com/pentops/j5/j5types/date_j5t" - "github.com/pentops/j5/lib/j5codec" - "github.com/pentops/j5/lib/j5reflect" - "google.golang.org/protobuf/types/known/timestamppb" -) - -func FieldsToDBValues(m map[string]any) (map[string]any, error) { - out := map[string]any{} - for k, v := range m { - converted, err := interfaceToDBValue(v) - if err != nil { - return nil, fmt.Errorf("field values: %w", err) - } - - out[k] = converted - } - return out, nil -} - -func FieldsToEqMap(ofTable string, m map[string]any) (sq.Eq, error) { - out := sq.Eq{} - for k, v := range m { - converted, err := interfaceToDBValue(v) - if err != nil { - return nil, fmt.Errorf("eq map: %w", err) - } - - fullKey := fmt.Sprintf("%s.%s", ofTable, k) - out[fullKey] = converted - } - return out, nil -} - -type j5message interface { - J5Reflect() j5reflect.Root -} - -func interfaceToDBValue(i any) (any, error) { - switch v := i.(type) { - case *timestamppb.Timestamp: - return v.AsTime(), nil - - case *date_j5t.Date: - return v.DateString(), nil - - case driver.Valuer: - return v, nil - - case j5message: - i, err := marshalJ5(v) - if err != nil { - return nil, fmt.Errorf("interface values: %w", err) - } - - return i, nil - case *string: - if v == nil { - return nil, nil - } - return v, nil - } - - switch reflect.TypeOf(i).Kind() { - case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Slice, reflect.Array: - if reflect.ValueOf(i).IsNil() { - return nil, nil - } - } - - return i, nil -} - -var codec *j5codec.Codec - -func init() { - codec = j5codec.NewCodec(j5codec.WithIncludeEmpty()) -} - -func MarshalJ5(msg j5reflect.Root) ([]byte, error) { - return codec.ReflectToJSON(msg) -} - -func UnmarshalJ5(b []byte, msg j5reflect.Root) error { - return codec.JSONToReflect(b, msg) -} - -func marshalJ5(msg j5message) (any, error) { - if msg == nil { - return nil, nil - } - - j5r := msg.J5Reflect() - if j5r == nil { - return nil, fmt.Errorf("j5reflect is nil for %T", msg) - } - - return MarshalJ5(j5r) -} - -/* -func MarshalProto(state protoreflect.ProtoMessage) ([]byte, error) { - // EmitDefaultValues behaves similarly to EmitUnpopulated, but does not emit "null"-value fields, - // i.e. presence-sensing fields that are omitted will remain omitted to preserve presence-sensing. - b, err := protojson.MarshalOptions{EmitDefaultValues: true}.Marshal(state) - if err != nil { - return b, fmt.Errorf("custom protomarshal: %w", err) - } - return b, nil -}*/ diff --git a/internal/integration/universe_test.go b/internal/integration/universe_test.go index f0eee6e..6c9555c 100644 --- a/internal/integration/universe_test.go +++ b/internal/integration/universe_test.go @@ -8,10 +8,10 @@ import ( "github.com/pentops/flowtest" "github.com/pentops/log.go/log" "github.com/pentops/pgtest.go/pgtest" - "github.com/pentops/protostate/internal/pgstore/psmpg" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_pb" "github.com/pentops/protostate/internal/testproto/gen/test/v1/test_spb" "github.com/pentops/protostate/psm" + "github.com/pentops/protostate/psmigrate" "github.com/pentops/sqrlx.go/sqrlx" ) @@ -72,7 +72,7 @@ func setupUniverse(t flowtest.Asserter, uu *Universe) { uu.FooQuery = NewMiniFooController(db, fooQuery) uu.BarQuery = NewMiniBarController(db, barQuery) - if err := psmpg.CreateStateMachines(context.Background(), conn, + if err := psmigrate.CreateStateMachines(context.Background(), conn, sm.Foo.StateTableSpec(), sm.Bar.StateTableSpec(), ); err != nil { diff --git a/psm/state_query.go b/psm/state_query.go index ad94ab2..ab4b0b7 100644 --- a/psm/state_query.go +++ b/psm/state_query.go @@ -4,9 +4,9 @@ import ( "context" "fmt" + pquery "github.com/pentops/j5/lib/j5query" "github.com/pentops/j5/lib/j5reflect" "github.com/pentops/j5/lib/j5schema" - "github.com/pentops/j5/lib/j5query" ) // QueryTableSpec the TableMap with descriptors for the messages, without using diff --git a/psm/statemachine.go b/psm/statemachine.go index c154814..80ea863 100644 --- a/psm/statemachine.go +++ b/psm/statemachine.go @@ -8,6 +8,7 @@ import ( "time" "github.com/pentops/j5/j5types/date_j5t" + "github.com/pentops/j5/lib/j5codec" "github.com/pentops/j5/lib/j5reflect" "github.com/pentops/j5/lib/j5schema" "github.com/pentops/j5/lib/j5validate" @@ -17,7 +18,6 @@ import ( "github.com/google/uuid" "github.com/pentops/j5/gen/j5/state/v1/psm_j5pb" "github.com/pentops/log.go/log" - "github.com/pentops/protostate/internal/dbconvert" "github.com/pentops/sqrlx.go/sqrlx" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -55,6 +55,8 @@ type StateMachine[ validator *j5validate.Validator protoValidator protovalidate.Validator + + codec *j5codec.Codec } func NewStateMachine[ @@ -90,6 +92,7 @@ func NewStateMachine[ return nil, fmt.Errorf("failed to initialize protovalidate: %w", err) } + codec := j5codec.NewCodec(j5codec.WithIncludeEmpty()) return &StateMachine[K, S, ST, SD, E, IE]{ keyValueFunc: cb.keyValues, initialStateFunc: cb.initialStateFunc, @@ -98,9 +101,18 @@ func NewStateMachine[ eventSchema: eventSchema, validator: j5validate.NewValidator(), protoValidator: pv, + codec: codec, }, nil } +func (sm StateMachine[K, S, ST, SD, E, IE]) marshalJ5(obj j5reflect.Root) ([]byte, error) { + return sm.codec.ReflectToJSON(obj) +} + +func (sm StateMachine[K, S, ST, SD, E, IE]) unmarshalJ5(data []byte, obj j5reflect.Root) error { + return sm.codec.JSONToReflect(data, obj) +} + func (sm StateMachine[K, S, ST, SD, E, IE]) StateTableSpec() QueryTableSpec { return QueryTableSpec{ TableMap: *sm.tableMap, @@ -254,7 +266,7 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) getCurrentState(ctx context.Context return state, fmt.Errorf("selecting current state (%s): %w", qq, err) } - if err := dbconvert.UnmarshalJ5(stateJSON, stateRefl); err != nil { + if err := sm.unmarshalJ5(stateJSON, stateRefl); err != nil { return state, err } @@ -332,12 +344,12 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) store( event E, ) error { - stateDBValue, err := dbconvert.MarshalJ5(state.J5Reflect()) + stateDBValue, err := sm.marshalJ5(state.J5Reflect()) if err != nil { return fmt.Errorf("state field: %w", err) } - eventDBValue, err := dbconvert.MarshalJ5(event.J5Reflect()) + eventDBValue, err := sm.marshalJ5(event.J5Reflect()) if err != nil { return fmt.Errorf("event field: %w", err) } @@ -439,7 +451,7 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) followEventDeduplicate(ctx context. } existing := newJ5Message[E]() - if err := dbconvert.UnmarshalJ5(eventData, existing.J5Reflect()); err != nil { + if err := sm.unmarshalJ5(eventData, existing.J5Reflect()); err != nil { return true, fmt.Errorf("unmarshalling event: %w", err) } @@ -636,7 +648,7 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) firstEventUniqueCheck(ctx context.C existing := newJ5Message[E]() - if err := dbconvert.UnmarshalJ5(eventData, existing.J5Reflect()); err != nil { + if err := sm.unmarshalJ5(eventData, existing.J5Reflect()); err != nil { return s, false, fmt.Errorf("unmarshalling event: %w", err) } @@ -648,7 +660,7 @@ func (sm *StateMachine[K, S, ST, SD, E, IE]) firstEventUniqueCheck(ctx context.C } state := newJ5Message[S]() - if err := dbconvert.UnmarshalJ5(stateData, state.J5Reflect()); err != nil { + if err := sm.unmarshalJ5(stateData, state.J5Reflect()); err != nil { return s, false, fmt.Errorf("unmarshalling state: %w", err) } diff --git a/psm/table_map.go b/psm/table_map.go index 8a452ad..fe649c2 100644 --- a/psm/table_map.go +++ b/psm/table_map.go @@ -7,8 +7,8 @@ import ( "github.com/iancoleman/strcase" "github.com/pentops/j5/gen/j5/schema/v1/schema_j5pb" - "github.com/pentops/j5/lib/j5schema" "github.com/pentops/j5/lib/j5query" + "github.com/pentops/j5/lib/j5schema" ) type TableMap struct { diff --git a/psmigrate/migrate.go b/psmigrate/migrate.go deleted file mode 100644 index f16cf9e..0000000 --- a/psmigrate/migrate.go +++ /dev/null @@ -1,10 +0,0 @@ -package psmigrate - -import ( - "github.com/pentops/protostate/internal/pgstore/psmpg" - "github.com/pentops/protostate/psm" -) - -func BuildStateMachineMigrations(specs ...psm.QueryTableSpec) ([]byte, error) { - return psmpg.BuildStateMachineMigrations(specs...) -} diff --git a/internal/pgstore/psmpg/psm.go b/psmigrate/psm.go similarity index 95% rename from internal/pgstore/psmpg/psm.go rename to psmigrate/psm.go index cf015c5..d6faa49 100644 --- a/internal/pgstore/psmpg/psm.go +++ b/psmigrate/psm.go @@ -1,4 +1,4 @@ -package psmpg +package psmigrate import ( "context" @@ -30,9 +30,7 @@ func BuildStateMachineMigrations(specs ...psm.QueryTableSpec) ([]byte, error) { return nil, err } - for _, index := range indexes { - allMigrations = append(allMigrations, index) - } + allMigrations = append(allMigrations, indexes...) eventList := spec.EventTable() indexes, err = pgmigrate.IndexMigrations(eventList) @@ -40,9 +38,7 @@ func BuildStateMachineMigrations(specs ...psm.QueryTableSpec) ([]byte, error) { return nil, err } - for _, index := range indexes { - allMigrations = append(allMigrations, index) - } + allMigrations = append(allMigrations, indexes...) } fileData, err := pgmigrate.PrintMigrations(allMigrations...) diff --git a/internal/pgstore/psmpg/psm_test.go b/psmigrate/psm_test.go similarity index 97% rename from internal/pgstore/psmpg/psm_test.go rename to psmigrate/psm_test.go index ee56853..b6e7db8 100644 --- a/internal/pgstore/psmpg/psm_test.go +++ b/psmigrate/psm_test.go @@ -1,4 +1,4 @@ -package psmpg +package psmigrate import ( "testing" From 997703f8775a39f92f2331dd27b07a0604ad8fe4 Mon Sep 17 00:00:00 2001 From: Damien Whitten Date: Wed, 30 Jul 2025 11:28:32 -0700 Subject: [PATCH 12/12] J5 Update --- go.mod | 2 +- go.sum | 4 ++-- psm/table_map.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 31d4e2b..0fc0a6f 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/iancoleman/strcase v0.3.0 github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21 github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 - github.com/pentops/j5 v0.0.0-20250730000347-c9a449751d4e + github.com/pentops/j5 v0.0.0-20250730174934-447885654c3d github.com/pentops/log.go v0.0.16 github.com/pentops/o5-messaging v0.0.0-20250619024104-7e07c29129f0 github.com/pentops/pgtest.go v0.0.0-20241223222214-7638cc50e15b diff --git a/go.sum b/go.sum index 48ae166..4035a83 100644 --- a/go.sum +++ b/go.sum @@ -77,8 +77,8 @@ github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21 h1:EIMCYtccNxV59a github.com/pentops/flowtest v0.0.0-20250716231535-9c97a48adf21/go.mod h1:vNp8crAKcH0f/sZU9frkmQLUeDsTIgMqV14kQtkAqC0= github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63 h1:s5qtWT2/s79gy/wm3/bwvKYLK6u2AkW05JiLPqxraP0= github.com/pentops/golib v0.0.0-20250326060930-8c83d58ddb63/go.mod h1:I58JIVvL1/nP4CEHGKGbBhvWIEA9mVkGeoviemaqanU= -github.com/pentops/j5 v0.0.0-20250730000347-c9a449751d4e h1:8JFNkPsm6R1O25PD785eaUdCFLHwa9LfrKC15iGvgaU= -github.com/pentops/j5 v0.0.0-20250730000347-c9a449751d4e/go.mod h1:i+eCdoCoDvULbEuPeoQm4q1YLw13oaJIlYMB+kaX6fM= +github.com/pentops/j5 v0.0.0-20250730174934-447885654c3d h1:lORHJXvXiel67tT7sm+XfW7Ke2JCMXMLFAMyzhQi+mE= +github.com/pentops/j5 v0.0.0-20250730174934-447885654c3d/go.mod h1:i+eCdoCoDvULbEuPeoQm4q1YLw13oaJIlYMB+kaX6fM= github.com/pentops/log.go v0.0.16 h1:oxCuHSBOBPjfUVSXyOSEEdYUwytysj4T29/7T2FBp9Q= github.com/pentops/log.go v0.0.16/go.mod h1:yR34x8aMlvhdGvqgIU4+0MiLjJTKt0vpcgUnVN2nZV4= github.com/pentops/o5-messaging v0.0.0-20250619024104-7e07c29129f0 h1:aAt5rIhzFyF1RdT262LGHL0V4vBxcGCTWIeC1C0vZ20= diff --git a/psm/table_map.go b/psm/table_map.go index fe649c2..64e2ec3 100644 --- a/psm/table_map.go +++ b/psm/table_map.go @@ -105,7 +105,7 @@ type KeyField struct { ColumnName *string // Optional, stores in the table as a column. Primary bool Unique bool - Path *pquery.Path + Path *j5query.Path } func safeTableName(name string) string {