Added graphiql command, fixed map GQL naming, and made serialization interface use generics

gql_cataclysm
noah metz 2024-03-08 14:35:23 -07:00
parent 7314c74087
commit 7e143c9d93
5 changed files with 144 additions and 122 deletions

@ -0,0 +1,35 @@
package main
import (
"fmt"
badger "github.com/dgraph-io/badger/v3"
gv "github.com/mekkanized/graphvent"
)
func check(err error) {
if err != nil {
panic(err)
}
}
func main() {
db, err := badger.Open(badger.DefaultOptions("").WithInMemory(true))
check(err)
ctx, err := gv.NewContext(db, gv.NewConsoleLogger([]string{"test", "gql"}))
check(err)
gql_ext, err := gv.NewGQLExt(ctx, ":8080", nil, nil)
check(err)
listener_ext := gv.NewListenerExt(1000)
_, err = gv.NewNode(ctx, nil, "Base", 1000, gql_ext, listener_ext)
check(err)
select {
case message := <- listener_ext.Chan:
fmt.Printf("Listener Message: %+v\n", message)
}
}

@ -92,56 +92,63 @@ type Context struct {
nodeMap map[NodeID]*Node
}
func (ctx *Context) GQLType(t reflect.Type) graphql.Type {
func (ctx *Context) GQLType(t reflect.Type) (graphql.Type, error) {
info, mapped := ctx.TypeTypes[t]
if mapped {
return info.Type
return info.Type, nil
} else {
switch t.Kind() {
case reflect.Array:
info, mapped := ctx.TypeTypes[t.Elem()]
if mapped {
return graphql.NewList(info.Type)
return graphql.NewList(info.Type), nil
}
case reflect.Slice:
info, mapped := ctx.TypeTypes[t.Elem()]
if mapped {
return graphql.NewList(info.Type)
return graphql.NewList(info.Type), nil
}
case reflect.Map:
info, exists := ctx.TypeTypes[t]
if exists {
return info.Type
return info.Type, nil
} else {
err := RegisterMap(ctx, t)
if err != nil {
return nil
return nil, err
}
return ctx.TypeTypes[t].Type
map_type := ctx.TypeTypes[t].Type
ctx.Log.Logf("gql", "Getting type for %s: %s", t, map_type)
return map_type, nil
}
case reflect.Pointer:
info, mapped := ctx.TypeTypes[t.Elem()]
if mapped {
return info.Type
return info.Type, nil
}
}
return nil
return nil, fmt.Errorf("Can't convert %s to GQL type", t)
}
}
func RegisterMap(ctx *Context, reflect_type reflect.Type) error {
key_type := ctx.GQLType(reflect_type.Key())
if key_type == nil {
return nil
key_type, err := ctx.GQLType(reflect_type.Key())
if err != nil {
return err
}
val_type := ctx.GQLType(reflect_type.Elem())
if val_type == nil {
return nil
val_type, err := ctx.GQLType(reflect_type.Elem())
if err != nil {
return err
}
gql_name := strings.ReplaceAll(reflect_type.String(), ".", "_")
gql_name = strings.ReplaceAll(gql_name, "[", "_")
gql_name = strings.ReplaceAll(gql_name, "]", "_")
ctx.Log.Logf("gql", "Registering %s with gql name %s", reflect_type, gql_name)
gql_pair := graphql.NewObject(graphql.ObjectConfig{
Name: strings.ReplaceAll(reflect_type.String(), ".", "_"),
Name: gql_name,
Fields: graphql.Fields{
"Key": &graphql.Field{
Type: key_type,
@ -158,6 +165,7 @@ func RegisterMap(ctx *Context, reflect_type reflect.Type) error {
},
})
ctx.Log.Logf("gql", "Registering new map with pair type %+v", gql_pair)
gql_map := graphql.NewList(gql_pair)
serialized_type := SerializeType(reflect_type)
@ -227,8 +235,10 @@ func RegisterExtension[E any, T interface { *E; Extension}](ctx *Context, data i
return fmt.Errorf("Cannot register extension %+v of type %+v, type already exists in context", reflect_type, ext_type)
}
gql_name := "interface_" + strings.ReplaceAll(reflect_type.String(), ".", "_")
ctx.Log.Logf("gql", "Registering %s with gql name %s", reflect_type, gql_name)
gql_interface := graphql.NewInterface(graphql.InterfaceConfig{
Name: "interface_" + strings.ReplaceAll(reflect_type.String(), ".", "_"),
Name: gql_name,
ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
ctx, ok := p.Context.Value("resolve").(*ResolveContext)
if ok == false {
@ -260,9 +270,9 @@ func RegisterExtension[E any, T interface { *E; Extension}](ctx *Context, data i
if tagged_gv {
fields[gv_tag] = field.Index
gql_type := ctx.GQLType(field.Type)
if gql_type == nil {
return fmt.Errorf("Extension %s has field %s of unregistered type %s", reflect_type, gv_tag, field.Type)
gql_type, err := ctx.GQLType(field.Type)
if err != nil {
return err
}
gql_interface.AddFieldConfig(gv_tag, &graphql.Field{
@ -347,8 +357,10 @@ func RegisterObject[T any](ctx *Context) error {
return fmt.Errorf("%+v already registered in TypeMap", reflect_type)
}
gql_name := strings.ReplaceAll(reflect_type.String(), ".", "_")
ctx.Log.Logf("gql", "Registering %s with gql name %s", reflect_type, gql_name)
gql := graphql.NewObject(graphql.ObjectConfig{
Name: strings.ReplaceAll(reflect_type.String(), ".", "_"),
Name: gql_name,
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
return reflect_type == reflect.TypeOf(p.Value)
},
@ -372,9 +384,9 @@ func RegisterObject[T any](ctx *Context) error {
Index: field.Index,
}
gql_type := ctx.GQLType(field.Type)
if gql_type == nil {
return fmt.Errorf("Object %+v has field %s of unknown type %+v", reflect_type, gv_tag, field.Type)
gql_type, err := ctx.GQLType(field.Type)
if err != nil {
return err
}
gql.AddFieldConfig(gv_tag, &graphql.Field{
Type: gql_type,
@ -532,8 +544,10 @@ func RegisterScalar[S any](ctx *Context, to_json func(interface{})interface{}, f
return fmt.Errorf("%+v already registered in TypeMap", reflect_type)
}
gql_name := strings.ReplaceAll(reflect_type.String(), ".", "_")
ctx.Log.Logf("gql", "Registering %s with gql name %s", reflect_type, gql_name)
gql := graphql.NewScalar(graphql.ScalarConfig{
Name: strings.ReplaceAll(reflect_type.String(), ".", "_"),
Name: gql_name,
Serialize: to_json,
ParseValue: from_json,
ParseLiteral: from_ast,
@ -726,22 +740,17 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) {
return nil, err
}
err = RegisterMap(ctx, reflect.TypeFor[WaitMap]())
if err != nil {
return nil, err
}
err = RegisterExtension[ListenerExt](ctx, nil)
err = RegisterExtension[LockableExt](ctx, nil)
if err != nil {
return nil, err
}
err = RegisterExtension[LockableExt](ctx, nil)
err = RegisterExtension[EventExt](ctx, nil)
if err != nil {
return nil, err
}
err = RegisterExtension[EventExt](ctx, nil)
err = RegisterExtension[ListenerExt](ctx, nil)
if err != nil {
return nil, err
}
@ -756,7 +765,7 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) {
return nil, err
}
_, err = BuildSchema(ctx, graphql.NewObject(graphql.ObjectConfig{
schema, err := BuildSchema(ctx, graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"Test": &graphql.Field{
@ -781,5 +790,7 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) {
return nil, err
}
ctx.ExtensionTypes[reflect.TypeFor[GQLExt]()].Data = schema
return ctx, nil
}

15
db.go

@ -5,7 +5,12 @@ import (
)
func WriteNodeInit(ctx *Context, node *Node) error {
return ctx.DB.Update(func(db *badger.Txn) error {
return ctx.DB.Update(func(tx *badger.Txn) error {
_, err := node.ID.MarshalBinary()
if err != nil {
return err
}
// Write node private key
// Write node type
// Write Node buffer size
@ -17,8 +22,12 @@ func WriteNodeInit(ctx *Context, node *Node) error {
}
func WriteNodeChanges(ctx *Context, node *Node, changes map[ExtType]Changes) error {
return ctx.DB.Update(func(db *badger.Txn) error {
return ctx.DB.Update(func(tx *badger.Txn) error {
// Write the signal queue if it needs to be written
if node.writeSignalQueue {
node.writeSignalQueue = false
}
// For each ext in changes
// Write each change
return nil
@ -26,7 +35,7 @@ func WriteNodeChanges(ctx *Context, node *Node, changes map[ExtType]Changes) err
}
func LoadNode(ctx *Context, id NodeID) (*Node, error) {
err := ctx.DB.Update(func(db *badger.Txn) error {
err := ctx.DB.Update(func(tx *badger.Txn) error {
return nil
})

@ -190,7 +190,26 @@ func UnwrapStack(ctx *Context, stack []SerializedType) (reflect.Type, []Serializ
}
}
func SerializeValue(ctx *Context, value reflect.Value) ([]byte, error) {
func Serialize[T any](ctx *Context, value T) ([]byte, error) {
return serializeValue(ctx, reflect.ValueOf(value))
}
func Deserialize[T any](ctx *Context, data []byte) (T, error) {
reflect_type := reflect.TypeFor[T]()
var zero T
value, left, err := deserializeValue(ctx, data, reflect_type)
if err != nil {
return zero, err
} else if len(left) != 0 {
return zero, fmt.Errorf("%d bytes left after deserializing %+v", len(left), value)
} else if value.Type() != reflect_type {
return zero, fmt.Errorf("Deserialized type %s does not match %s", value.Type(), reflect_type)
}
return value.Interface().(T), nil
}
func serializeValue(ctx *Context, value reflect.Value) ([]byte, error) {
var serialize SerializeFn = nil
info, registered := ctx.TypeTypes[value.Type()]
@ -243,7 +262,7 @@ func SerializeValue(ctx *Context, value reflect.Value) ([]byte, error) {
if value.IsNil() {
return []byte{0x00}, nil
} else {
elem, err := SerializeValue(ctx, value.Elem())
elem, err := serializeValue(ctx, value.Elem())
if err != nil {
return nil, err
}
@ -260,7 +279,7 @@ func SerializeValue(ctx *Context, value reflect.Value) ([]byte, error) {
data := []byte{}
for i := 0; i < value.Len(); i++ {
elem, err := SerializeValue(ctx, value.Index(i))
elem, err := serializeValue(ctx, value.Index(i))
if err != nil {
return nil, err
}
@ -274,7 +293,7 @@ func SerializeValue(ctx *Context, value reflect.Value) ([]byte, error) {
case reflect.Array:
data := []byte{}
for i := 0; i < value.Len(); i++ {
elem, err := SerializeValue(ctx, value.Index(i))
elem, err := serializeValue(ctx, value.Index(i))
if err != nil {
return nil, err
}
@ -290,14 +309,14 @@ func SerializeValue(ctx *Context, value reflect.Value) ([]byte, error) {
data := []byte{}
iter := value.MapRange()
for iter.Next() {
k, err := SerializeValue(ctx, iter.Key())
k, err := serializeValue(ctx, iter.Key())
if err != nil {
return nil, err
}
data = append(data, k...)
v, err := SerializeValue(ctx, iter.Value())
v, err := serializeValue(ctx, iter.Value())
if err != nil {
return nil, err
}
@ -314,7 +333,7 @@ func SerializeValue(ctx *Context, value reflect.Value) ([]byte, error) {
for field_tag, field_info := range(info.Fields) {
data = append(data, binary.BigEndian.AppendUint64(nil, uint64(field_tag))...)
field_bytes, err := SerializeValue(ctx, value.FieldByIndex(field_info.Index))
field_bytes, err := serializeValue(ctx, value.FieldByIndex(field_info.Index))
if err != nil {
return nil, err
}
@ -336,7 +355,7 @@ func split(data []byte, n int) ([]byte, []byte) {
return data[:n], data[n:]
}
func DeserializeValue(ctx *Context, data []byte, t reflect.Type) (reflect.Value, []byte, error) {
func deserializeValue(ctx *Context, data []byte, t reflect.Type) (reflect.Value, []byte, error) {
var deserialize DeserializeFn = nil
info, registered := ctx.TypeTypes[t]
@ -423,7 +442,7 @@ func DeserializeValue(ctx *Context, data []byte, t reflect.Type) (reflect.Value,
value.SetZero()
return value, after_flags, nil
} else {
elem_value, after_elem, err := DeserializeValue(ctx, after_flags, t.Elem())
elem_value, after_elem, err := deserializeValue(ctx, after_flags, t.Elem())
if err != nil {
return reflect.Value{}, nil, err
}
@ -438,7 +457,7 @@ func DeserializeValue(ctx *Context, data []byte, t reflect.Type) (reflect.Value,
for i := 0; i < length; i++ {
var elem_value reflect.Value
var err error
elem_value, left, err = DeserializeValue(ctx, left, t.Elem())
elem_value, left, err = deserializeValue(ctx, left, t.Elem())
if err != nil {
return reflect.Value{}, nil, err
}
@ -452,7 +471,7 @@ func DeserializeValue(ctx *Context, data []byte, t reflect.Type) (reflect.Value,
for i := 0; i < t.Len(); i++ {
var elem_value reflect.Value
var err error
elem_value, left, err = DeserializeValue(ctx, left, t.Elem())
elem_value, left, err = deserializeValue(ctx, left, t.Elem())
if err != nil {
return reflect.Value{}, nil, err
}
@ -471,12 +490,12 @@ func DeserializeValue(ctx *Context, data []byte, t reflect.Type) (reflect.Value,
var val_value reflect.Value
var err error
key_value, left, err = DeserializeValue(ctx, left, t.Key())
key_value, left, err = deserializeValue(ctx, left, t.Key())
if err != nil {
return reflect.Value{}, nil, err
}
val_value, left, err = DeserializeValue(ctx, left, t.Elem())
val_value, left, err = deserializeValue(ctx, left, t.Elem())
if err != nil {
return reflect.Value{}, nil, err
}
@ -504,7 +523,7 @@ func DeserializeValue(ctx *Context, data []byte, t reflect.Type) (reflect.Value,
if mapped {
var field_val reflect.Value
var err error
field_val, left, err = DeserializeValue(ctx, left, field_info.Type)
field_val, left, err = deserializeValue(ctx, left, field_info.Type)
if err != nil {
return reflect.Value{}, nil, err
}

@ -41,123 +41,71 @@ func TestSerializeTypes(t *testing.T) {
}
func testSerializeCompare[T comparable](t *testing.T, ctx *Context, value T) {
serialized, err := SerializeValue(ctx, reflect.ValueOf(value))
serialized, err := Serialize(ctx, value)
fatalErr(t, err)
ctx.Log.Logf("test", "Serialized Value[%s : %+v]: %+v", reflect.TypeFor[T](), value, serialized)
deserialized, left, err := DeserializeValue(ctx, serialized, reflect.TypeFor[T]())
deserialized, err := Deserialize[T](ctx, serialized)
fatalErr(t, err)
if len(left) != 0 {
t.Fatalf("Data left after deserialize[%+v]: %+v", deserialized, left)
}
if reflect.TypeFor[T]() != deserialized.Type() {
t.Fatalf("Type mismatch after deserialize %s != %s", reflect.TypeFor[T](), deserialized.Type())
}
val, ok := deserialized.Interface().(T)
if ok == false {
t.Fatalf("Deserialized type[%s] can't cast to type %s", deserialized.Type(), reflect.TypeFor[T]())
}
if value != val {
if value != deserialized {
t.Fatalf("Deserialized value[%+v] doesn't match original[%+v]", value, deserialized)
}
ctx.Log.Logf("test", "Deserialized Value[%+v]: %+v", value, val)
ctx.Log.Logf("test", "Deserialized Value[%+v]: %+v", value, deserialized)
}
func testSerializeList[L []T, T comparable](t *testing.T, ctx *Context, value L) {
serialized, err := SerializeValue(ctx, reflect.ValueOf(value))
serialized, err := Serialize(ctx, value)
fatalErr(t, err)
ctx.Log.Logf("test", "Serialized Value[%s : %+v]: %+v", reflect.TypeFor[L](), value, serialized)
deserialized, left, err := DeserializeValue(ctx, serialized, reflect.TypeFor[L]())
deserialized, err := Deserialize[L](ctx, serialized)
fatalErr(t, err)
if len(left) != 0 {
t.Fatalf("Data left after deserialize[%+v]: %+v", deserialized, left)
}
if reflect.TypeFor[L]() != deserialized.Type() {
t.Fatalf("Type mismatch after deserialize %s != %s", reflect.TypeFor[L](), deserialized.Type())
}
val, ok := deserialized.Interface().(L)
if ok == false {
t.Fatalf("Deserialized type[%s] can't cast to type %s", deserialized.Type(), reflect.TypeFor[L]())
}
for i, item := range(value) {
if item != val[i] {
t.Fatalf("Deserialized list %+v does not match original %+v", value, val)
if item != deserialized[i] {
t.Fatalf("Deserialized list %+v does not match original %+v", value, deserialized)
}
}
ctx.Log.Logf("test", "Deserialized Value[%+v]: %+v", value, val)
ctx.Log.Logf("test", "Deserialized Value[%+v]: %+v", value, deserialized)
}
func testSerializePointer[P interface {*T}, T comparable](t *testing.T, ctx *Context, value P) {
serialized, err := SerializeValue(ctx, reflect.ValueOf(value))
serialized, err := Serialize(ctx, value)
fatalErr(t, err)
ctx.Log.Logf("test", "Serialized Value[%s : %+v]: %+v", reflect.TypeFor[P](), value, serialized)
deserialized, left, err := DeserializeValue(ctx, serialized, reflect.TypeFor[P]())
deserialized, err := Deserialize[P](ctx, serialized)
fatalErr(t, err)
if len(left) != 0 {
t.Fatalf("Data left after deserialize[%+v]: %+v", deserialized, left)
}
if reflect.TypeFor[P]() != deserialized.Type() {
t.Fatalf("Type mismatch after deserialize %s != %s", reflect.TypeFor[P](), deserialized.Type())
}
val, ok := deserialized.Interface().(P)
if ok == false {
t.Fatalf("Deserialized type[%s] can't cast to type %s", deserialized.Type(), reflect.TypeFor[P]())
}
if value == nil && val == nil {
if value == nil && deserialized == nil {
ctx.Log.Logf("test", "Deserialized nil")
} else if value == nil {
t.Fatalf("Non-nil value[%+v] returned for nil value", val)
} else if val == nil {
t.Fatalf("Non-nil value[%+v] returned for nil value", deserialized)
} else if deserialized == nil {
t.Fatalf("Nil value returned for non-nil value[%+v]", value)
} else if *val != *value {
} else if *deserialized != *value {
t.Fatalf("Deserialized value[%+v] doesn't match original[%+v]", value, deserialized)
} else {
ctx.Log.Logf("test", "Deserialized Value[%+v]: %+v", *value, *val)
ctx.Log.Logf("test", "Deserialized Value[%+v]: %+v", *value, *deserialized)
}
}
func testSerialize[T any](t *testing.T, ctx *Context, value T) {
serialized, err := SerializeValue(ctx, reflect.ValueOf(value))
serialized, err := Serialize(ctx, value)
fatalErr(t, err)
ctx.Log.Logf("test", "Serialized Value[%s : %+v]: %+v", reflect.TypeFor[T](), value, serialized)
deserialized, left, err := DeserializeValue(ctx, serialized, reflect.TypeFor[T]())
deserialized, err := Deserialize[T](ctx, serialized)
fatalErr(t, err)
if len(left) != 0 {
t.Fatalf("Data left after deserialize[%+v]: %+v", deserialized, left)
}
if reflect.TypeFor[T]() != deserialized.Type() {
t.Fatalf("Type mismatch after deserialize %s != %s", reflect.TypeFor[T](), deserialized.Type())
}
val, ok := deserialized.Interface().(T)
if ok == false {
t.Fatalf("Deserialized type[%s] can't cast to type %s", deserialized.Type(), reflect.TypeFor[T]())
}
ctx.Log.Logf("test", "Deserialized Value[%+v]: %+v", value, val)
ctx.Log.Logf("test", "Deserialized Value[%+v]: %+v", value, deserialized)
}
func TestSerializeValues(t *testing.T) {