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
}
err = gql_ctx.RegisterNodeType(GQLNodeType, "GQLServer", NodeInterfaces, []string{"Listen"})
err = gql_ctx.RegisterNodeType(GQLNodeType, "GQLServer", []string{"Node"}, []string{"Listen"})
if err != nil {
return nil, err
}

197
gql.go

@ -558,28 +558,123 @@ func RegisterField[T any](ctx *GQLExtContext, gql_type graphql.Type, gql_name st
return nil
}
func (ctx *GQLExtContext) RegisterInterface(i *Interface) error {
if i == nil {
return fmt.Errorf("interface is nil")
func GQLInterfaces(ctx *GQLExtContext, interface_names []string) ([]*graphql.Interface, error) {
ret := make([]*graphql.Interface, len(interface_names))
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
}
if i.Interface == nil || i.Extensions == nil || i.Default == nil || i.List == nil {
return fmt.Errorf("invalid interface, contains nil")
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)
}
func (ctx *GQLExtContext) RegisterInterface(name string, default_name string, interfaces []string, fields []string, self_fields map[string]SelfField, list_fields map[string]ListField) error {
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]
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)
node_interfaces, err := GQLInterfaces(ctx, interfaces)
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
}
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 {
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)
}
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,
},
node_interfaces, err := GQLInterfaces(ctx, interface_names)
if err != nil {
return err
}
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_fields, _, err := GQLFields(ctx, field_names)
if err != nil {
return err
}
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
},
Fields: fields,
Fields: gql_fields,
})
ctx.NodeTypes[node_type] = gql_type
@ -645,45 +720,45 @@ func NewGQLExtContext() *GQLExtContext {
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{
Schema: graphql.Schema{},
Types: []graphql.Type{},
Query: query,
Mutation: mutation,
Subscription: subscription,
Mutation: nil,
Subscription: nil,
NodeTypes: map[NodeType]*graphql.Object{},
Interfaces: map[string]*Interface{},
Fields: map[string]Field{},
}
var err error
err = context.RegisterInterface(InterfaceNode)
if err != nil {
panic(err)
}
err = context.RegisterInterface(InterfaceLockable)
err = context.RegisterInterface("Node", "DefaultNode", []string{}, []string{}, map[string]SelfField{}, map[string]ListField{})
if err != nil {
panic(err)
}
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 {
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)
if err != nil {
panic(err)

@ -25,37 +25,7 @@ func NewSingleton[K graphql.Type](init func() K, post_init func(K, *graphql.List
}
}
func AddNodeInterfaceFields(gql *Interface) {
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 {
func NodeInterfaceDefaultIsType(extensions []ExtType) func(graphql.IsTypeOfParams) bool {
return func(p graphql.IsTypeOfParams) bool {
node, ok := p.Value.(NodeResult)
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 {
ctx, ok := p.Context.Value("resolve").(*ResolveContext)
if ok == false {
@ -99,57 +69,3 @@ func NodeTypeResolver(required_extensions []ExtType, default_type **graphql.Obje
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,69 +35,41 @@ func GetResolveFields(ctx *Context, p graphql.ResolveParams) []string {
return names
}
var QueryNode = &graphql.Field{
Type: InterfaceNode.Interface,
Args: graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{
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")
if err != nil {
return nil, err
}
func ResolveNode(ctx *ResolveContext, p graphql.ResolveParams, id_str string) (NodeResult, error) {
var zero NodeResult
fields := GetResolveFields(ctx.Context, p)
ctx.Context.Log.Logf("gql", "RESOLVE_NODE(%s): %+v", id_str, fields)
id, err := ParseID(id_str)
if err != nil {
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
ext_fields, err := ctx.GQLContext.GetACLFields(p.Info.FieldName, fields)
if err != nil {
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
read_signal := NewReadSignal(ext_fields)
// Get a list of fields that will be written
ext_fields, err := ctx.GQLContext.GetACLFields(p.Info.FieldName, fields)
if err != nil {
return nil, 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
read_signal := NewReadSignal(ext_fields)
ctx.Ext.resolver_reads_lock.Lock()
ctx.Ext.resolver_reads[read_signal.UUID] = ctx.ID
ctx.Ext.resolver_reads_lock.Unlock()
err = ctx.Context.Send(ctx.Server.ID, id, read_signal)
if err != nil {
ctx.Ext.resolver_reads_lock.Lock()
ctx.Ext.resolver_reads[read_signal.UUID] = ctx.ID
delete(ctx.Ext.resolver_reads, read_signal.UUID)
ctx.Ext.resolver_reads_lock.Unlock()
return zero, err
}
err = ctx.Context.Send(ctx.Server.ID, id, read_signal)
if err != nil {
ctx.Ext.resolver_reads_lock.Lock()
delete(ctx.Ext.resolver_reads, read_signal.UUID)
ctx.Ext.resolver_reads_lock.Unlock()
return nil, err
}
// Wait for the response, returning an error on timeout
response, err := WaitForReadResult(ctx.Chan, time.Millisecond*100, read_signal.UUID)
if err != nil {
return nil, err
}
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))
// Wait for the response, returning an error on timeout
response, err := WaitForReadResult(ctx.Chan, time.Millisecond*100, read_signal.UUID)
if err != nil {
return zero, err
}
return nil, nil
},
return NodeResult{id, response}, nil
}

@ -1,75 +1,4 @@
package graphvent
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 {
var gql Type
gql.Type = graphql.NewObject(graphql.ObjectConfig{