Did most of the work to move node interface creation to GQLExtContext

gql_cataclysm
noah metz 2023-07-29 18:27:52 -06:00
parent 891e69c775
commit fad8d8123c
6 changed files with 169 additions and 280 deletions

@ -281,7 +281,7 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) {
return nil, err return nil, err
} }
err = gql_ctx.RegisterNodeType(GQLNodeType, "GQLServer", NodeInterfaces, []string{"Listen"}) err = gql_ctx.RegisterNodeType(GQLNodeType, "GQLServer", []string{"Node"}, []string{"Listen"})
if err != nil { if err != nil {
return nil, err return nil, err
} }

193
gql.go

@ -558,28 +558,123 @@ func RegisterField[T any](ctx *GQLExtContext, gql_type graphql.Type, gql_name st
return nil return nil
} }
func (ctx *GQLExtContext) RegisterInterface(i *Interface) error { func GQLInterfaces(ctx *GQLExtContext, interface_names []string) ([]*graphql.Interface, error) {
if i == nil { ret := make([]*graphql.Interface, len(interface_names))
return fmt.Errorf("interface is nil") for i, in := range(interface_names) {
ctx_interface, exists := ctx.Interfaces[in]
if exists == false {
return nil, fmt.Errorf("%s is not in GQLExtContext.Interfaces", in)
}
ret[i] = ctx_interface.Interface
}
return ret, nil
}
func GQLFields(ctx *GQLExtContext, field_names []string) (graphql.Fields, []ExtType, error) {
fields := graphql.Fields{
"ID": &graphql.Field{
Type: graphql.String,
Resolve: ResolveNodeID,
},
"TypeHash": &graphql.Field{
Type: graphql.String,
Resolve: ResolveNodeTypeHash,
},
}
exts := map[ExtType]ExtType{}
ext_list := []ExtType{}
for _, name := range(field_names) {
field, exists := ctx.Fields[name]
if exists == false {
return nil, nil, fmt.Errorf("%s is not in GQLExtContext.Fields", name)
}
fields[name] = field.Field
_, exists = exts[field.Ext]
if exists == false {
ext_list = append(ext_list, field.Ext)
exts[field.Ext] = field.Ext
}
}
return fields, ext_list, nil
}
type NodeResult struct {
ID NodeID
Result *ReadResultSignal
}
type ListField struct {
ACLName string
Extension ExtType
ResolveFn func(interface{}) ([]NodeID, error)
}
type SelfField struct {
ACLName string
Extension ExtType
ResolveFn func(interface{}) (NodeID, error)
} }
if i.Interface == nil || i.Extensions == nil || i.Default == nil || i.List == nil { func (ctx *GQLExtContext) RegisterInterface(name string, default_name string, interfaces []string, fields []string, self_fields map[string]SelfField, list_fields map[string]ListField) error {
return fmt.Errorf("invalid interface, contains nil") if interfaces == nil {
return fmt.Errorf("interfaces is nil")
}
if fields == nil {
return fmt.Errorf("fields is nil")
} }
name := i.Interface.PrivateName
_, exists := ctx.Interfaces[name] _, exists := ctx.Interfaces[name]
if exists == true { if exists == true {
return fmt.Errorf("%s is already an interface in ctx", name) return fmt.Errorf("%s is already an interface in ctx", name)
} }
ctx.Interfaces[name] = i node_interfaces, err := GQLInterfaces(ctx, interfaces)
ctx.Types = append(ctx.Types, i.Default) if err != nil {
return err
}
node_fields, node_exts, err := GQLFields(ctx, fields)
if err != nil {
return err
}
ctx_interface := Interface{}
ctx_interface.Interface = graphql.NewInterface(graphql.InterfaceConfig{
Name: name,
ResolveType: NodeInterfaceResolveType(node_exts, &ctx_interface.Default),
Fields: node_fields,
})
ctx_interface.List = graphql.NewList(ctx_interface.Interface)
//TODO finish self_fields and do list_fields
for field_name, self_field := range(self_fields) {
err := RegisterField(ctx, ctx_interface.Interface, field_name, self_field.Extension, self_field.ACLName, func(id_str string) (interface{}, error) {
return nil, nil
})
if err != nil {
return err
}
}
ctx_interface.Default = graphql.NewObject(graphql.ObjectConfig{
Name: default_name,
Interfaces: append(node_interfaces, ctx_interface.Interface),
IsTypeOf: NodeInterfaceDefaultIsType(node_exts),
Fields: node_fields,
})
ctx.Interfaces[name] = &ctx_interface
ctx.Types = append(ctx.Types, ctx_interface.Default)
return nil return nil
} }
func (ctx *GQLExtContext) RegisterNodeType(node_type NodeType, name string, interfaces []*Interface, field_names []string) error { func (ctx *GQLExtContext) RegisterNodeType(node_type NodeType, name string, interface_names []string, field_names []string) error {
if field_names == nil { if field_names == nil {
return fmt.Errorf("fields is nil") return fmt.Errorf("fields is nil")
} }
@ -589,34 +684,14 @@ func (ctx *GQLExtContext) RegisterNodeType(node_type NodeType, name string, inte
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)) node_interfaces, err := GQLInterfaces(ctx, interface_names)
for i, in := range(interfaces) { if err != nil {
if_name := in.Interface.PrivateName return err
_, 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) { gql_fields, _, err := GQLFields(ctx, field_names)
field, exists := ctx.Fields[name] if err != nil {
if exists == false { return err
return fmt.Errorf("%s is not in GQLExtContext.Fields", name)
}
fields[name] = field.Field
} }
gql_type := graphql.NewObject(graphql.ObjectConfig{ gql_type := graphql.NewObject(graphql.ObjectConfig{
@ -630,7 +705,7 @@ func (ctx *GQLExtContext) RegisterNodeType(node_type NodeType, name string, inte
return node.Result.NodeType == node_type return node.Result.NodeType == node_type
}, },
Fields: fields, Fields: gql_fields,
}) })
ctx.NodeTypes[node_type] = gql_type ctx.NodeTypes[node_type] = gql_type
@ -645,45 +720,45 @@ func NewGQLExtContext() *GQLExtContext {
Fields: graphql.Fields{}, Fields: graphql.Fields{},
}) })
query.AddFieldConfig("Self", QuerySelf)
query.AddFieldConfig("Node", QueryNode)
mutation := graphql.NewObject(graphql.ObjectConfig{
Name: "Mutation",
Fields: graphql.Fields{},
})
mutation.AddFieldConfig("stop", MutationStop)
subscription := graphql.NewObject(graphql.ObjectConfig{
Name: "Subscription",
Fields: graphql.Fields{},
})
subscription.AddFieldConfig("Self", SubscriptionSelf)
subscription.AddFieldConfig("Self", SubscriptionNode)
context := GQLExtContext{ context := GQLExtContext{
Schema: graphql.Schema{}, Schema: graphql.Schema{},
Types: []graphql.Type{}, Types: []graphql.Type{},
Query: query, Query: query,
Mutation: mutation, Mutation: nil,
Subscription: subscription, Subscription: nil,
NodeTypes: map[NodeType]*graphql.Object{}, NodeTypes: map[NodeType]*graphql.Object{},
Interfaces: map[string]*Interface{}, Interfaces: map[string]*Interface{},
Fields: map[string]Field{}, Fields: map[string]Field{},
} }
var err error var err error
err = context.RegisterInterface(InterfaceNode) err = context.RegisterInterface("Node", "DefaultNode", []string{}, []string{}, map[string]SelfField{}, map[string]ListField{})
if err != nil { if err != nil {
panic(err) panic(err)
} }
err = context.RegisterInterface(InterfaceLockable)
context.Query.AddFieldConfig("Node", &graphql.Field{
Type: context.Interfaces["Node"].Interface,
Args: graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{
Type: graphql.String,
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
ctx, err := PrepResolve(p)
if err != nil { if err != nil {
panic(err) return nil, err
}
id_str, err := ExtractParam[string](p, "id")
if err != nil {
return nil, err
} }
return ResolveNode(ctx, p, id_str)
},
})
schema, err := BuildSchema(&context) schema, err := BuildSchema(&context)
if err != nil { if err != nil {
panic(err) panic(err)

@ -25,37 +25,7 @@ func NewSingleton[K graphql.Type](init func() K, post_init func(K, *graphql.List
} }
} }
func AddNodeInterfaceFields(gql *Interface) { func NodeInterfaceDefaultIsType(extensions []ExtType) func(graphql.IsTypeOfParams) bool {
gql.Interface.AddFieldConfig("ID", &graphql.Field{
Type: graphql.String,
})
gql.Interface.AddFieldConfig("TypeHash", &graphql.Field{
Type: graphql.String,
})
}
func AddLockableInterfaceFields(gql *Interface) {
addLockableInterfaceFields(gql, InterfaceLockable)
}
func addLockableInterfaceFields(gql *Interface, gql_lockable *Interface) {
AddNodeInterfaceFields(gql)
gql.Interface.AddFieldConfig("Requirements", &graphql.Field{
Type: gql_lockable.List,
})
gql.Interface.AddFieldConfig("Dependencies", &graphql.Field{
Type: gql_lockable.List,
})
gql.Interface.AddFieldConfig("Owner", &graphql.Field{
Type: gql_lockable.Interface,
})
}
func NodeIsTypeResolver(extensions []ExtType) func(graphql.IsTypeOfParams) bool {
return func(p graphql.IsTypeOfParams) bool { return func(p graphql.IsTypeOfParams) bool {
node, ok := p.Value.(NodeResult) node, ok := p.Value.(NodeResult)
if ok == false { if ok == false {
@ -73,7 +43,7 @@ func NodeIsTypeResolver(extensions []ExtType) func(graphql.IsTypeOfParams) bool
} }
} }
func NodeTypeResolver(required_extensions []ExtType, default_type **graphql.Object)func(graphql.ResolveTypeParams) *graphql.Object { func NodeInterfaceResolveType(required_extensions []ExtType, default_type **graphql.Object)func(graphql.ResolveTypeParams) *graphql.Object {
return func(p graphql.ResolveTypeParams) *graphql.Object { return func(p graphql.ResolveTypeParams) *graphql.Object {
ctx, ok := p.Context.Value("resolve").(*ResolveContext) ctx, ok := p.Context.Value("resolve").(*ResolveContext)
if ok == false { if ok == false {
@ -99,57 +69,3 @@ func NodeTypeResolver(required_extensions []ExtType, default_type **graphql.Obje
return gql_type return gql_type
} }
} }
type NodeResult struct {
ID NodeID
Result *ReadResultSignal
}
func NewInterface(if_name string, default_name string, interfaces []*graphql.Interface, extensions []ExtType, init_1 func(*Interface), init_2 func(*Interface)) *Interface {
var gql Interface
gql.Extensions = extensions
gql.Interface = graphql.NewInterface(graphql.InterfaceConfig{
Name: if_name,
ResolveType: NodeTypeResolver([]ExtType{}, &gql.Default),
Fields: graphql.Fields{},
})
gql.List = graphql.NewList(gql.Interface)
init_1(&gql)
gql.Default = graphql.NewObject(graphql.ObjectConfig{
Name: default_name,
Interfaces: append(interfaces, gql.Interface),
IsTypeOf: NodeIsTypeResolver([]ExtType{}),
Fields: graphql.Fields{},
})
init_2(&gql)
return &gql
}
var InterfaceNode = NewInterface("Node", "DefaultNode", []*graphql.Interface{}, []ExtType{}, func(gql *Interface) {
AddNodeInterfaceFields(gql)
}, func(gql *Interface) {
AddNodeFields(gql.Default)
})
var InterfaceLockable = NewInterface("Lockable", "DefaultLockable", []*graphql.Interface{InterfaceNode.Interface}, []ExtType{LockableExtType}, func(gql *Interface) {
addLockableInterfaceFields(gql, gql)
}, func(gql *Interface) {
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,
})
})

@ -35,32 +35,20 @@ func GetResolveFields(ctx *Context, p graphql.ResolveParams) []string {
return names return names
} }
var QueryNode = &graphql.Field{ func ResolveNode(ctx *ResolveContext, p graphql.ResolveParams, id_str string) (NodeResult, error) {
Type: InterfaceNode.Interface, var zero NodeResult
Args: graphql.FieldConfigArgument{ fields := GetResolveFields(ctx.Context, p)
"id": &graphql.ArgumentConfig{ ctx.Context.Log.Logf("gql", "RESOLVE_NODE(%s): %+v", id_str, fields)
Type: graphql.String,
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
ctx, err := PrepResolve(p)
if err != nil {
return nil, err
}
id, err := ExtractID(p, "id") id, err := ParseID(id_str)
if err != nil { if err != nil {
return nil, err return zero, err
} }
fields := GetResolveFields(ctx.Context, p)
ctx.Context.Log.Logf("gql", "RESOLVE_NODE(%s): %+v", id, fields)
// Get a list of fields that will be written // Get a list of fields that will be written
ext_fields, err := ctx.GQLContext.GetACLFields(p.Info.FieldName, fields) ext_fields, err := ctx.GQLContext.GetACLFields(p.Info.FieldName, fields)
if err != nil { if err != nil {
return nil, err return zero, err
} }
// Create a read signal, send it to the specified node, and add the wait to the response map if the send returns no error // Create a read signal, send it to the specified node, and add the wait to the response map if the send returns no error
read_signal := NewReadSignal(ext_fields) read_signal := NewReadSignal(ext_fields)
@ -74,30 +62,14 @@ var QueryNode = &graphql.Field{
ctx.Ext.resolver_reads_lock.Lock() ctx.Ext.resolver_reads_lock.Lock()
delete(ctx.Ext.resolver_reads, read_signal.UUID) delete(ctx.Ext.resolver_reads, read_signal.UUID)
ctx.Ext.resolver_reads_lock.Unlock() ctx.Ext.resolver_reads_lock.Unlock()
return nil, err return zero, err
} }
// Wait for the response, returning an error on timeout // Wait for the response, returning an error on timeout
response, err := WaitForReadResult(ctx.Chan, time.Millisecond*100, read_signal.UUID) response, err := WaitForReadResult(ctx.Chan, time.Millisecond*100, read_signal.UUID)
if err != nil { if err != nil {
return nil, err return zero, err
} }
return NodeResult{id, response}, nil return NodeResult{id, response}, nil
},
} }
var QuerySelf = &graphql.Field{
Type: InterfaceNode.Default,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
ctx, err := PrepResolve(p)
if err != nil {
return nil, err
}
ctx.Context.Log.Logf("gql", "FIELDS: %+v", GetResolveFields(ctx.Context, p))
return nil, nil
},
}

@ -1,75 +1,4 @@
package graphvent package graphvent
import ( import (
"github.com/graphql-go/graphql"
) )
func SubscribeNode(p graphql.ResolveParams) (interface{}, error) {
return SubscribeFn(p, false, func(ctx *Context, server *Node, ext *GQLExt, signal Signal, p graphql.ResolveParams)(interface{}, error) {
return nil, nil
})
}
func SubscribeSelf(p graphql.ResolveParams) (interface{}, error) {
return SubscribeFn(p, true, func(ctx *Context, server *Node, ext *GQLExt, signal Signal, p graphql.ResolveParams)(interface{}, error) {
return server, nil
})
}
func SubscribeFn(p graphql.ResolveParams, send_nil bool, fn func(*Context, *Node, *GQLExt, Signal, graphql.ResolveParams)(interface{}, error))(interface{}, error) {
ctx, err := PrepResolve(p)
if err != nil {
return nil, err
}
c := make(chan interface{})
go func(c chan interface{}, ext *GQLExt, server *Node) {
ctx.Context.Log.Logf("gqlws", "GQL_SUBSCRIBE_THREAD_START")
sig_c := make(chan Signal, 1)
if send_nil == true {
sig_c <- nil
}
for {
val, ok := <- sig_c
if ok == false {
return
}
ret, err := fn(ctx.Context, server, ext, val, p)
if err != nil {
ctx.Context.Log.Logf("gqlws", "type convertor error %s", err)
return
}
c <- ret
}
}(c, ctx.Ext, ctx.Server)
return c, nil
}
var SubscriptionSelf = NewField(func()*graphql.Field{
subscription_self := &graphql.Field{
Type: InterfaceNode.Default,
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return p.Source, nil
},
Subscribe: SubscribeSelf,
}
return subscription_self
})
var SubscriptionNode = NewField(func()*graphql.Field{
subscription_node := &graphql.Field{
Type: InterfaceNode.Default,
Args: graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{
Type: graphql.String,
},
},
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
return p.Source, nil
},
Subscribe: SubscribeNode,
}
return subscription_node
})

@ -16,9 +16,6 @@ func AddNodeFields(object *graphql.Object) {
}) })
} }
var NodeInterfaces = []*Interface{InterfaceNode}
var LockableInterfaces = append(NodeInterfaces, InterfaceLockable)
func NewGQLNodeType(node_type NodeType, interfaces []*graphql.Interface, init func(*Type)) *Type { func NewGQLNodeType(node_type NodeType, interfaces []*graphql.Interface, init func(*Type)) *Type {
var gql Type var gql Type
gql.Type = graphql.NewObject(graphql.ObjectConfig{ gql.Type = graphql.NewObject(graphql.ObjectConfig{