diff --git a/context.go b/context.go index c9c8e0b..2fe2a1b 100644 --- a/context.go +++ b/context.go @@ -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 } diff --git a/gql.go b/gql.go index b018be7..93e7ce6 100644 --- a/gql.go +++ b/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) diff --git a/gql_interfaces.go b/gql_interfaces.go index f0539e2..45228c9 100644 --- a/gql_interfaces.go +++ b/gql_interfaces.go @@ -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, - }) -}) - diff --git a/gql_query.go b/gql_query.go index 5a37e6b..57defba 100644 --- a/gql_query.go +++ b/gql_query.go @@ -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 } - diff --git a/gql_subscribe.go b/gql_subscribe.go index da09b8a..948021c 100644 --- a/gql_subscribe.go +++ b/gql_subscribe.go @@ -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 -}) - diff --git a/gql_types.go b/gql_types.go index a540700..f38e9ab 100644 --- a/gql_types.go +++ b/gql_types.go @@ -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{