Added more sane methods to register GQL fields, need to do the same for itnerfaces and their defaults

gql_cataclysm
noah metz 2023-07-29 17:23:25 -06:00
parent d6a35247b0
commit 891e69c775
6 changed files with 129 additions and 128 deletions

@ -2,6 +2,7 @@ package graphvent
import ( import (
badger "github.com/dgraph-io/badger/v3" badger "github.com/dgraph-io/badger/v3"
"github.com/graphql-go/graphql"
"fmt" "fmt"
"sync" "sync"
"errors" "errors"
@ -273,12 +274,14 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) {
return nil, err return nil, err
} }
err = gql_ctx.RegisterNodeType(GQLNodeType, TypeGQLNode.Type) err = RegisterField(gql_ctx, graphql.String, "Listen", GQLExtType, "listen", func(listen string) (interface{}, error) {
return listen, nil
})
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = gql_ctx.RegisterField("Listen", GQLExtType, "listen") err = gql_ctx.RegisterNodeType(GQLNodeType, "GQLServer", NodeInterfaces, []string{"Listen"})
if err != nil { if err != nil {
return nil, err return nil, err
} }

124
gql.go

@ -459,29 +459,10 @@ type Type struct {
List *graphql.List List *graphql.List
} }
func NewGQLNodeType(node_type NodeType, interfaces []*graphql.Interface, init func(*Type)) *Type {
var gql Type
gql.Type = graphql.NewObject(graphql.ObjectConfig{
Name: string(node_type),
Interfaces: interfaces,
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
node, ok := p.Value.(NodeResult)
if ok == false {
return false
}
return node.Result.NodeType == node_type
},
Fields: graphql.Fields{},
})
gql.List = graphql.NewList(gql.Type)
init(&gql)
return &gql
}
type Field struct { type Field struct {
Ext ExtType Ext ExtType
Name string Name string
Field *graphql.Field
} }
// GQL Specific Context information // GQL Specific Context information
@ -491,7 +472,7 @@ type GQLExtContext struct {
// Custom graphql types, mapped to NodeTypes // Custom graphql types, mapped to NodeTypes
NodeTypes map[NodeType]*graphql.Object NodeTypes map[NodeType]*graphql.Object
Interfaces []*Interface Interfaces map[string]*Interface
Fields map[string]Field Fields map[string]Field
// Schema parameters // Schema parameters
@ -536,17 +517,48 @@ func BuildSchema(ctx *GQLExtContext) (graphql.Schema, error) {
return graphql.NewSchema(schemaConfig) return graphql.NewSchema(schemaConfig)
} }
func (ctx *GQLExtContext) RegisterField(gql_name string, ext ExtType, acl_name string) error { func RegisterField[T any](ctx *GQLExtContext, gql_type graphql.Type, gql_name string, ext_type ExtType, acl_name string, resolve_fn func(T)(interface{}, error)) error {
if ctx == nil {
return fmt.Errorf("ctx is nil")
}
if resolve_fn == nil {
return fmt.Errorf("resolve_fn cannot be nil")
}
_, exists := ctx.Fields[gql_name] _, exists := ctx.Fields[gql_name]
if exists == true { if exists == true {
return fmt.Errorf("%s is already a field in the context, cannot add again", gql_name) return fmt.Errorf("%s is already a field in the context, cannot add again", gql_name)
} }
ctx.Fields[gql_name] = Field{ext, acl_name} ctx.Fields[gql_name] = Field{ext_type, acl_name, &graphql.Field{
Type: gql_type,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return ResolveNodeResult(p, func(result NodeResult) (interface{}, error) {
ext, exists := result.Result.Extensions[ext_type]
if exists == false {
return nil, fmt.Errorf("%s is not in the extensions of the result", ext_type)
}
val_if, exists := ext[acl_name]
if exists == false {
return nil, fmt.Errorf("%s is not in the fields of %s in the result", acl_name, ext_type)
}
var zero T
val, ok := val_if.(T)
if ok == false {
return nil, fmt.Errorf("%s.%s is not %s", ext_type, acl_name, reflect.TypeOf(zero))
}
return resolve_fn(val)
})
},
}}
return nil return nil
} }
func (ctx *GQLExtContext) AddInterface(i *Interface) error { func (ctx *GQLExtContext) RegisterInterface(i *Interface) error {
if i == nil { if i == nil {
return fmt.Errorf("interface is nil") return fmt.Errorf("interface is nil")
} }
@ -555,15 +567,21 @@ func (ctx *GQLExtContext) AddInterface(i *Interface) error {
return fmt.Errorf("invalid interface, contains nil") return fmt.Errorf("invalid interface, contains nil")
} }
ctx.Interfaces = append(ctx.Interfaces, i) name := i.Interface.PrivateName
_, exists := ctx.Interfaces[name]
if exists == true {
return fmt.Errorf("%s is already an interface in ctx", name)
}
ctx.Interfaces[name] = i
ctx.Types = append(ctx.Types, i.Default) ctx.Types = append(ctx.Types, i.Default)
return nil return nil
} }
func (ctx *GQLExtContext) RegisterNodeType(node_type NodeType, gql_type *graphql.Object) error { func (ctx *GQLExtContext) RegisterNodeType(node_type NodeType, name string, interfaces []*Interface, field_names []string) error {
if gql_type == nil { if field_names == nil {
return fmt.Errorf("gql_type is nil") return fmt.Errorf("fields is nil")
} }
_, exists := ctx.NodeTypes[node_type] _, exists := ctx.NodeTypes[node_type]
@ -571,6 +589,50 @@ func (ctx *GQLExtContext) RegisterNodeType(node_type NodeType, gql_type *graphql
return fmt.Errorf("%s already in GQLExtContext.NodeTypes", node_type) return fmt.Errorf("%s already in GQLExtContext.NodeTypes", node_type)
} }
node_interfaces := make([]*graphql.Interface, len(interfaces))
for i, in := range(interfaces) {
if_name := in.Interface.PrivateName
_, found := ctx.Interfaces[if_name]
if found == false {
return fmt.Errorf("%+v is not in GQLExtContext.Interfaces", in)
}
node_interfaces[i] = in.Interface
}
fields := graphql.Fields{
"ID": &graphql.Field{
Type: graphql.String,
Resolve: ResolveNodeID,
},
"TypeHash": &graphql.Field{
Type: graphql.String,
Resolve: ResolveNodeTypeHash,
},
}
for _, name := range(field_names) {
field, exists := ctx.Fields[name]
if exists == false {
return fmt.Errorf("%s is not in GQLExtContext.Fields", name)
}
fields[name] = field.Field
}
gql_type := graphql.NewObject(graphql.ObjectConfig{
Name: name,
Interfaces: node_interfaces,
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
node, ok := p.Value.(NodeResult)
if ok == false {
return false
}
return node.Result.NodeType == node_type
},
Fields: fields,
})
ctx.NodeTypes[node_type] = gql_type ctx.NodeTypes[node_type] = gql_type
ctx.Types = append(ctx.Types, gql_type) ctx.Types = append(ctx.Types, gql_type)
@ -608,16 +670,16 @@ func NewGQLExtContext() *GQLExtContext {
Mutation: mutation, Mutation: mutation,
Subscription: subscription, Subscription: subscription,
NodeTypes: map[NodeType]*graphql.Object{}, NodeTypes: map[NodeType]*graphql.Object{},
Interfaces: []*Interface{}, Interfaces: map[string]*Interface{},
Fields: map[string]Field{}, Fields: map[string]Field{},
} }
var err error var err error
err = context.AddInterface(InterfaceNode) err = context.RegisterInterface(InterfaceNode)
if err != nil { if err != nil {
panic(err) panic(err)
} }
err = context.AddInterface(InterfaceLockable) err = context.RegisterInterface(InterfaceLockable)
if err != nil { if err != nil {
panic(err) panic(err)
} }

@ -139,6 +139,17 @@ var InterfaceNode = NewInterface("Node", "DefaultNode", []*graphql.Interface{},
var InterfaceLockable = NewInterface("Lockable", "DefaultLockable", []*graphql.Interface{InterfaceNode.Interface}, []ExtType{LockableExtType}, func(gql *Interface) { var InterfaceLockable = NewInterface("Lockable", "DefaultLockable", []*graphql.Interface{InterfaceNode.Interface}, []ExtType{LockableExtType}, func(gql *Interface) {
addLockableInterfaceFields(gql, gql) addLockableInterfaceFields(gql, gql)
}, func(gql *Interface) { }, func(gql *Interface) {
addLockableFields(gql.Default, gql.Interface, gql.List) AddNodeFields(gql.Default)
gql.Default.AddFieldConfig("Requirements", &graphql.Field{
Type: gql.List,
})
gql.Default.AddFieldConfig("Owner", &graphql.Field{
Type: gql.Interface,
})
gql.Default.AddFieldConfig("Dependencies", &graphql.Field{
Type: gql.List,
})
}) })

@ -85,70 +85,6 @@ func ResolveNodeTypeHash(p graphql.ResolveParams) (interface{}, error) {
}) })
} }
func ResolveNodeResultExt[T any](p graphql.ResolveParams, ext_type ExtType, field string, resolve_fn func(T)(interface{}, error)) (interface{}, error) {
return ResolveNodeResult(p, func(result NodeResult) (interface{}, error) {
ext, exists := result.Result.Extensions[ext_type]
if exists == false {
return nil, fmt.Errorf("%s is not in the extensions of the result", ext_type)
}
val_if, exists := ext[field]
if exists == false {
return nil, fmt.Errorf("%s is not in the fields of %s in the result", field, ext_type)
}
var zero T
val, ok := val_if.(T)
if ok == false {
return nil, fmt.Errorf("%s.%s is not %s", ext_type, field, reflect.TypeOf(zero))
}
return resolve_fn(val)
})
}
func ResolveListen(p graphql.ResolveParams) (interface{}, error) {
return ResolveNodeResultExt(p, GQLExtType, "listen", func(listen string) (interface{}, error) {
return listen, nil
})
}
func ResolveRequirements(p graphql.ResolveParams) (interface{}, error) {
return ResolveNodeResultExt(p, LockableExtType, "requirements", func(requirements []NodeID) (interface{}, error) {
res := make([]string, len(requirements))
for i, id := range(requirements) {
res[i] = id.String()
}
return res, nil
})
}
func ResolveDependencies(p graphql.ResolveParams) (interface{}, error) {
return ResolveNodeResultExt(p, LockableExtType, "dependencies", func(dependencies []NodeID) (interface{}, error) {
res := make([]string, len(dependencies))
for i, id := range(dependencies) {
res[i] = id.String()
}
return res, nil
})
}
func ResolveOwner(p graphql.ResolveParams) (interface{}, error) {
return ResolveNodeResultExt(p, LockableExtType, "owner", func(owner NodeID) (interface{}, error) {
return owner.String(), nil
})
}
func ResolveMembers(p graphql.ResolveParams) (interface{}, error) {
return ResolveNodeResultExt(p, GroupExtType, "members", func(members []NodeID) (interface{}, error) {
res := make([]string, len(members))
for i, id := range(members) {
res[i] = id.String()
}
return res, nil
})
}
func GQLSignalFn(p graphql.ResolveParams, fn func(Signal, graphql.ResolveParams)(interface{}, error))(interface{}, error) { func GQLSignalFn(p graphql.ResolveParams, fn func(Signal, graphql.ResolveParams)(interface{}, error))(interface{}, error) {
if signal, ok := p.Source.(Signal); ok { if signal, ok := p.Source.(Signal); ok {
return fn(signal, p) return fn(signal, p)

@ -47,7 +47,7 @@ func TestGQL(t *testing.T) {
} }
req_2 := GQLPayload{ req_2 := GQLPayload{
Query: "query Node($id:String) { Node(id:$id) { ID, TypeHash, ... on GQL { Listen } } }", Query: "query Node($id:String) { Node(id:$id) { ID, TypeHash, ... on GQLServer { Listen } } }",
Variables: map[string]interface{}{ Variables: map[string]interface{}{
"id": gql.ID.String(), "id": gql.ID.String(),
}, },

@ -16,40 +16,29 @@ func AddNodeFields(object *graphql.Object) {
}) })
} }
func AddLockableFields(object *graphql.Object) { var NodeInterfaces = []*Interface{InterfaceNode}
addLockableFields(object, InterfaceLockable.Interface, InterfaceLockable.List) var LockableInterfaces = append(NodeInterfaces, InterfaceLockable)
}
func addLockableFields(object *graphql.Object, lockable_interface *graphql.Interface, lockable_list *graphql.List) {
AddNodeFields(object)
object.AddFieldConfig("Requirements", &graphql.Field{
Type: lockable_list,
Resolve: ResolveRequirements,
})
object.AddFieldConfig("Owner", &graphql.Field{ func NewGQLNodeType(node_type NodeType, interfaces []*graphql.Interface, init func(*Type)) *Type {
Type: lockable_interface, var gql Type
Resolve: ResolveOwner, gql.Type = graphql.NewObject(graphql.ObjectConfig{
Name: string(node_type),
Interfaces: interfaces,
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
node, ok := p.Value.(NodeResult)
if ok == false {
return false
}
return node.Result.NodeType == node_type
},
Fields: graphql.Fields{},
}) })
gql.List = graphql.NewList(gql.Type)
object.AddFieldConfig("Dependencies", &graphql.Field{ init(&gql)
Type: lockable_list, return &gql
Resolve: ResolveDependencies,
})
} }
var GQLNodeInterfaces = []*graphql.Interface{InterfaceNode.Interface}
var GQLLockableInterfaces = append(GQLNodeInterfaces, InterfaceLockable.Interface)
var TypeGQLNode = NewGQLNodeType(GQLNodeType, GQLNodeInterfaces, func(gql *Type) {
AddNodeFields(gql.Type)
gql.Type.AddFieldConfig("Listen", &graphql.Field{
Type: graphql.String,
Resolve: ResolveListen,
})
})
var TypeSignal = NewSingleton(func() *graphql.Object { var TypeSignal = NewSingleton(func() *graphql.Object {
gql_type_signal := graphql.NewObject(graphql.ObjectConfig{ gql_type_signal := graphql.NewObject(graphql.ObjectConfig{
Name: "Signal", Name: "Signal",