Cleaned up GQL context

graph-rework-2
noah metz 2023-07-10 21:15:01 -06:00
parent a9431ecf73
commit e12d02eb3f
3 changed files with 135 additions and 154 deletions

@ -19,12 +19,16 @@ type NodeLoadFunc func(*Context, NodeID, []byte, NodeMap)(Node, error)
type NodeDef struct { type NodeDef struct {
Load NodeLoadFunc Load NodeLoadFunc
Type NodeType Type NodeType
GQLType *graphql.Object
Reflect reflect.Type
} }
func NewNodeDef(type_name string, load_func NodeLoadFunc) NodeDef { func NewNodeDef(type_name string, reflect reflect.Type, load_func NodeLoadFunc, gql_type *graphql.Object) NodeDef {
return NodeDef{ return NodeDef{
Type: NodeType(type_name), Type: NodeType(type_name),
Load: load_func, Load: load_func,
GQLType: gql_type,
Reflect: reflect,
} }
} }
@ -32,181 +36,158 @@ type Context struct {
DB * badger.DB DB * badger.DB
Log Logger Log Logger
Types map[uint64]NodeDef Types map[uint64]NodeDef
GQL * GQLContext GQL GQLContext
} }
func (ctx * Context) RegisterNodeType(type_name string, load_func NodeLoadFunc) error { func (ctx * Context) RebuildSchema() error {
if load_func == nil { schemaConfig := graphql.SchemaConfig{
return fmt.Errorf("Cannot register a node without a load function") Types: ctx.GQL.TypeList,
} Query: ctx.GQL.Query,
Mutation: ctx.GQL.Mutation,
def := NodeDef{ Subscription: ctx.GQL.Subscription,
Type: NodeType(type_name),
Load: load_func,
} }
type_hash := def.Type.Hash() schema, err := graphql.NewSchema(schemaConfig)
_, exists := ctx.Types[type_hash] if err != nil {
if exists == true { return err
return fmt.Errorf("Cannot register node of type %s, type already exists in context", type_name)
} }
ctx.Types[type_hash] = def ctx.GQL.Schema = schema
return nil return nil
} }
type TypeList []graphql.Type func (ctx * Context) AddGQLType(gql_type graphql.Type) {
type ObjTypeMap map[reflect.Type]*graphql.Object ctx.GQL.TypeList = append(ctx.GQL.TypeList, gql_type)
type FieldMap map[string]*graphql.Field
type GQLContext struct {
Schema graphql.Schema
ValidNodes ObjTypeMap
NodeType reflect.Type
ValidLockables ObjTypeMap
LockableType reflect.Type
ValidThreads ObjTypeMap
ThreadType reflect.Type
} }
func NewGQLContext(additional_types TypeList, extended_types ObjTypeMap, extended_queries FieldMap, extended_subscriptions FieldMap, extended_mutations FieldMap) (*GQLContext, error) { func (ctx * Context) RegisterNodeType(def NodeDef) error {
type_list := TypeList{ if def.Load == nil {
GQLTypeSignalInput(), return fmt.Errorf("Cannot register a node without a load function: %s", def.Type)
} }
for _, gql_type := range(additional_types) { if def.Reflect == nil {
type_list = append(type_list, gql_type) return fmt.Errorf("Cannot register a node without a reflect type: %s", def.Type)
} }
type_map := ObjTypeMap{} if def.GQLType == nil {
type_map[reflect.TypeOf((*GraphNode)(nil))] = GQLTypeBaseNode() return fmt.Errorf("Cannot register a node without a gql type: %s", def.Type)
type_map[reflect.TypeOf((*SimpleLockable)(nil))] = GQLTypeBaseLockable()
type_map[reflect.TypeOf((*SimpleThread)(nil))] = GQLTypeBaseThread()
type_map[reflect.TypeOf((*GQLThread)(nil))] = GQLTypeGQLThread()
type_map[reflect.TypeOf((*BaseSignal)(nil))] = GQLTypeSignal()
for go_t, gql_t := range(extended_types) {
type_map[go_t] = gql_t
} }
valid_nodes := ObjTypeMap{} type_hash := def.Type.Hash()
valid_lockables := ObjTypeMap{} _, exists := ctx.Types[type_hash]
valid_threads := ObjTypeMap{} if exists == true {
return fmt.Errorf("Cannot register node of type %s, type already exists in context", def.Type)
}
node_type := reflect.TypeOf((*Node)(nil)).Elem() ctx.Types[type_hash] = def
lockable_type := reflect.TypeOf((*Lockable)(nil)).Elem()
thread_type := reflect.TypeOf((*Thread)(nil)).Elem()
for go_t, gql_t := range(type_map) { if def.Reflect.Implements(ctx.GQL.NodeType) {
if go_t.Implements(node_type) { ctx.GQL.ValidNodes[def.Reflect] = def.GQLType
valid_nodes[go_t] = gql_t
}
if go_t.Implements(lockable_type) {
valid_lockables[go_t] = gql_t
} }
if go_t.Implements(thread_type) { if def.Reflect.Implements(ctx.GQL.LockableType) {
valid_threads[go_t] = gql_t ctx.GQL.ValidLockables[def.Reflect] = def.GQLType
} }
type_list = append(type_list, gql_t) if def.Reflect.Implements(ctx.GQL.ThreadType) {
ctx.GQL.ValidThreads[def.Reflect] = def.GQLType
} }
ctx.GQL.TypeList = append(ctx.GQL.TypeList, def.GQLType)
queries := graphql.Fields{ return nil
"Self": GQLQuerySelf(),
} }
for key, val := range(extended_queries) { type TypeList []graphql.Type
queries[key] = val type ObjTypeMap map[reflect.Type]*graphql.Object
} type FieldMap map[string]*graphql.Field
subscriptions := graphql.Fields{ type GQLContext struct {
"Update": GQLSubscriptionUpdate(), Schema graphql.Schema
"Self": GQLSubscriptionSelf(),
}
for key, val := range(extended_subscriptions) { NodeType reflect.Type
subscriptions[key] = val LockableType reflect.Type
} ThreadType reflect.Type
mutations := graphql.Fields{ TypeList TypeList
"SendUpdate": GQLMutationSendUpdate(),
}
for key, val := range(extended_mutations) { ValidNodes ObjTypeMap
mutations[key] = val ValidLockables ObjTypeMap
ValidThreads ObjTypeMap
Query *graphql.Object
Mutation *graphql.Object
Subscription *graphql.Object
} }
schemaConfig := graphql.SchemaConfig{ func NewGQLContext() GQLContext {
Types: type_list, query := graphql.NewObject(graphql.ObjectConfig{
Query: graphql.NewObject(graphql.ObjectConfig{
Name: "Query", Name: "Query",
Fields: queries, Fields: graphql.Fields{},
}), })
Mutation: graphql.NewObject(graphql.ObjectConfig{
mutation := graphql.NewObject(graphql.ObjectConfig{
Name: "Mutation", Name: "Mutation",
Fields: mutations, Fields: graphql.Fields{},
}), })
Subscription: graphql.NewObject(graphql.ObjectConfig{
Name: "Subscription",
Fields: subscriptions,
}),
}
schema, err := graphql.NewSchema(schemaConfig) subscription := graphql.NewObject(graphql.ObjectConfig{
if err != nil{ Name: "Subscription",
return nil, err Fields: graphql.Fields{},
} })
ctx := GQLContext{ ctx := GQLContext{
Schema: schema, Schema: graphql.Schema{},
ValidNodes: valid_nodes, TypeList: TypeList{},
NodeType: node_type, ValidNodes: ObjTypeMap{},
ValidThreads: valid_threads, NodeType: reflect.TypeOf((*Node)(nil)).Elem(),
ThreadType: thread_type, ValidThreads: ObjTypeMap{},
ValidLockables: valid_lockables, ThreadType: reflect.TypeOf((*Thread)(nil)).Elem(),
LockableType: lockable_type, ValidLockables: ObjTypeMap{},
LockableType: reflect.TypeOf((*Lockable)(nil)).Elem(),
Query: query,
Mutation: mutation,
Subscription: subscription,
} }
return &ctx, nil return ctx
}
func NewContext(db * badger.DB, log Logger, extra_nodes map[string]NodeLoadFunc, types TypeList, type_map ObjTypeMap, queries FieldMap, subscriptions FieldMap, mutations FieldMap) * Context {
gql, err := NewGQLContext(types, type_map, queries, subscriptions, mutations)
if err != nil {
panic(err)
} }
func NewContext(db * badger.DB, log Logger) * Context {
ctx := &Context{ ctx := &Context{
GQL: gql, GQL: NewGQLContext(),
DB: db, DB: db,
Log: log, Log: log,
Types: map[uint64]NodeDef{}, Types: map[uint64]NodeDef{},
} }
err := ctx.RegisterNodeType(NewNodeDef("graph_node", reflect.TypeOf((*GraphNode)(nil)), LoadGraphNode, GQLTypeGraphNode()))
err = ctx.RegisterNodeType("graph_node", LoadGraphNode)
if err != nil { if err != nil {
panic(err) panic(err)
} }
err = ctx.RegisterNodeType("simple_lockable", LoadSimpleLockable) err = ctx.RegisterNodeType(NewNodeDef("simple_lockable", reflect.TypeOf((*SimpleLockable)(nil)), LoadSimpleLockable, GQLTypeSimpleLockable()))
if err != nil { if err != nil {
panic(err) panic(err)
} }
err = ctx.RegisterNodeType("simple_thread", LoadSimpleThread) err = ctx.RegisterNodeType(NewNodeDef("simple_thread", reflect.TypeOf((*SimpleThread)(nil)), LoadSimpleThread, GQLTypeSimpleThread()))
if err != nil { if err != nil {
panic(err) panic(err)
} }
err = ctx.RegisterNodeType("gql_thread", LoadGQLThread) err = ctx.RegisterNodeType(NewNodeDef("gql_thread", reflect.TypeOf((*GQLThread)(nil)), LoadGQLThread, GQLTypeGQLThread()))
if err != nil { if err != nil {
panic(err) panic(err)
} }
for name, load_fn := range(extra_nodes) { ctx.AddGQLType(GQLTypeSignal())
err := ctx.RegisterNodeType(name, load_fn)
ctx.GQL.Query.AddFieldConfig("Self", GQLQuerySelf())
ctx.GQL.Subscription.AddFieldConfig("Update", GQLSubscriptionUpdate())
ctx.GQL.Subscription.AddFieldConfig("Self", GQLSubscriptionSelf())
ctx.GQL.Mutation.AddFieldConfig("SendUpdate", GQLMutationSendUpdate())
err = ctx.RebuildSchema()
if err != nil { if err != nil {
panic(err) panic(err)
} }
}
return ctx return ctx
} }

@ -28,7 +28,7 @@ func GQLInterfaceNode() *graphql.Interface {
} }
if p_type.Implements(node_type) { if p_type.Implements(node_type) {
return GQLTypeBaseNode() return GQLTypeGraphNode()
} }
return nil return nil
@ -74,7 +74,7 @@ func GQLInterfaceThread() *graphql.Interface {
} }
if p_type.Implements(thread_type) { if p_type.Implements(thread_type) {
return GQLTypeBaseThread() return GQLTypeSimpleThread()
} }
ctx.Log.Logf("gql", "Found no type that matches %+v: %+v", p_type, p_type.Implements(thread_type)) ctx.Log.Logf("gql", "Found no type that matches %+v: %+v", p_type, p_type.Implements(thread_type))
@ -145,7 +145,7 @@ func GQLInterfaceLockable() *graphql.Interface {
} }
if p_type.Implements(lockable_type) { if p_type.Implements(lockable_type) {
return GQLTypeBaseLockable() return GQLTypeSimpleLockable()
} }
return nil return nil
}, },
@ -418,10 +418,10 @@ func GQLTypeGQLThread() * graphql.Object {
return gql_type_gql_thread return gql_type_gql_thread
} }
var gql_type_base_thread *graphql.Object = nil var gql_type_simple_thread *graphql.Object = nil
func GQLTypeBaseThread() * graphql.Object { func GQLTypeSimpleThread() * graphql.Object {
if gql_type_base_thread == nil { if gql_type_simple_thread == nil {
gql_type_base_thread = graphql.NewObject(graphql.ObjectConfig{ gql_type_simple_thread = graphql.NewObject(graphql.ObjectConfig{
Name: "BaseThread", Name: "BaseThread",
Interfaces: []*graphql.Interface{ Interfaces: []*graphql.Interface{
GQLInterfaceNode(), GQLInterfaceNode(),
@ -446,48 +446,48 @@ func GQLTypeBaseThread() * graphql.Object {
}, },
Fields: graphql.Fields{}, Fields: graphql.Fields{},
}) })
gql_type_base_thread.AddFieldConfig("ID", &graphql.Field{ gql_type_simple_thread.AddFieldConfig("ID", &graphql.Field{
Type: graphql.String, Type: graphql.String,
Resolve: GQLNodeID, Resolve: GQLNodeID,
}) })
gql_type_base_thread.AddFieldConfig("Name", &graphql.Field{ gql_type_simple_thread.AddFieldConfig("Name", &graphql.Field{
Type: graphql.String, Type: graphql.String,
Resolve: GQLLockableName, Resolve: GQLLockableName,
}) })
gql_type_base_thread.AddFieldConfig("Children", &graphql.Field{ gql_type_simple_thread.AddFieldConfig("Children", &graphql.Field{
Type: GQLListThread(), Type: GQLListThread(),
Resolve: GQLThreadChildren, Resolve: GQLThreadChildren,
}) })
gql_type_base_thread.AddFieldConfig("Parent", &graphql.Field{ gql_type_simple_thread.AddFieldConfig("Parent", &graphql.Field{
Type: GQLInterfaceThread(), Type: GQLInterfaceThread(),
Resolve: GQLThreadParent, Resolve: GQLThreadParent,
}) })
gql_type_base_thread.AddFieldConfig("Requirements", &graphql.Field{ gql_type_simple_thread.AddFieldConfig("Requirements", &graphql.Field{
Type: GQLListLockable(), Type: GQLListLockable(),
Resolve: GQLLockableRequirements, Resolve: GQLLockableRequirements,
}) })
gql_type_base_thread.AddFieldConfig("Owner", &graphql.Field{ gql_type_simple_thread.AddFieldConfig("Owner", &graphql.Field{
Type: GQLInterfaceLockable(), Type: GQLInterfaceLockable(),
Resolve: GQLLockableOwner, Resolve: GQLLockableOwner,
}) })
gql_type_base_thread.AddFieldConfig("Dependencies", &graphql.Field{ gql_type_simple_thread.AddFieldConfig("Dependencies", &graphql.Field{
Type: GQLListLockable(), Type: GQLListLockable(),
Resolve: GQLLockableDependencies, Resolve: GQLLockableDependencies,
}) })
} }
return gql_type_base_thread return gql_type_simple_thread
} }
var gql_type_base_lockable *graphql.Object = nil var gql_type_simple_lockable *graphql.Object = nil
func GQLTypeBaseLockable() * graphql.Object { func GQLTypeSimpleLockable() * graphql.Object {
if gql_type_base_lockable == nil { if gql_type_simple_lockable == nil {
gql_type_base_lockable = graphql.NewObject(graphql.ObjectConfig{ gql_type_simple_lockable = graphql.NewObject(graphql.ObjectConfig{
Name: "BaseLockable", Name: "BaseLockable",
Interfaces: []*graphql.Interface{ Interfaces: []*graphql.Interface{
GQLInterfaceNode(), GQLInterfaceNode(),
@ -511,38 +511,38 @@ func GQLTypeBaseLockable() * graphql.Object {
Fields: graphql.Fields{}, Fields: graphql.Fields{},
}) })
gql_type_base_lockable.AddFieldConfig("ID", &graphql.Field{ gql_type_simple_lockable.AddFieldConfig("ID", &graphql.Field{
Type: graphql.String, Type: graphql.String,
Resolve: GQLNodeID, Resolve: GQLNodeID,
}) })
gql_type_base_lockable.AddFieldConfig("Name", &graphql.Field{ gql_type_simple_lockable.AddFieldConfig("Name", &graphql.Field{
Type: graphql.String, Type: graphql.String,
Resolve: GQLLockableName, Resolve: GQLLockableName,
}) })
gql_type_base_lockable.AddFieldConfig("Requirements", &graphql.Field{ gql_type_simple_lockable.AddFieldConfig("Requirements", &graphql.Field{
Type: GQLListLockable(), Type: GQLListLockable(),
Resolve: GQLLockableRequirements, Resolve: GQLLockableRequirements,
}) })
gql_type_base_lockable.AddFieldConfig("Owner", &graphql.Field{ gql_type_simple_lockable.AddFieldConfig("Owner", &graphql.Field{
Type: GQLInterfaceLockable(), Type: GQLInterfaceLockable(),
Resolve: GQLLockableOwner, Resolve: GQLLockableOwner,
}) })
gql_type_base_lockable.AddFieldConfig("Dependencies", &graphql.Field{ gql_type_simple_lockable.AddFieldConfig("Dependencies", &graphql.Field{
Type: GQLListLockable(), Type: GQLListLockable(),
Resolve: GQLLockableDependencies, Resolve: GQLLockableDependencies,
}) })
} }
return gql_type_base_lockable return gql_type_simple_lockable
} }
var gql_type_base_node *graphql.Object = nil var gql_type_simple_node *graphql.Object = nil
func GQLTypeBaseNode() * graphql.Object { func GQLTypeGraphNode() * graphql.Object {
if gql_type_base_node == nil { if gql_type_simple_node == nil {
gql_type_base_node = graphql.NewObject(graphql.ObjectConfig{ gql_type_simple_node = graphql.NewObject(graphql.ObjectConfig{
Name: "BaseNode", Name: "BaseNode",
Interfaces: []*graphql.Interface{ Interfaces: []*graphql.Interface{
GQLInterfaceNode(), GQLInterfaceNode(),
@ -565,18 +565,18 @@ func GQLTypeBaseNode() * graphql.Object {
Fields: graphql.Fields{}, Fields: graphql.Fields{},
}) })
gql_type_base_node.AddFieldConfig("ID", &graphql.Field{ gql_type_simple_node.AddFieldConfig("ID", &graphql.Field{
Type: graphql.String, Type: graphql.String,
Resolve: GQLNodeID, Resolve: GQLNodeID,
}) })
gql_type_base_node.AddFieldConfig("Name", &graphql.Field{ gql_type_simple_node.AddFieldConfig("Name", &graphql.Field{
Type: graphql.String, Type: graphql.String,
Resolve: GQLLockableName, Resolve: GQLLockableName,
}) })
} }
return gql_type_base_node return gql_type_simple_node
} }
func GQLSignalFn(p graphql.ResolveParams, fn func(GraphSignal, graphql.ResolveParams)(interface{}, error))(interface{}, error) { func GQLSignalFn(p graphql.ResolveParams, fn func(GraphSignal, graphql.ResolveParams)(interface{}, error))(interface{}, error) {

@ -58,7 +58,7 @@ func logTestContext(t * testing.T, components []string) * Context {
t.Fatal(err) t.Fatal(err)
} }
return NewContext(db, NewConsoleLogger(components), map[string]NodeLoadFunc{}, TypeList{}, ObjTypeMap{}, FieldMap{}, FieldMap{}, FieldMap{}) return NewContext(db, NewConsoleLogger(components))
} }
func testContext(t * testing.T) * Context { func testContext(t * testing.T) * Context {
@ -67,7 +67,7 @@ func testContext(t * testing.T) * Context {
t.Fatal(err) t.Fatal(err)
} }
return NewContext(db, NewConsoleLogger([]string{}), map[string]NodeLoadFunc{}, TypeList{}, ObjTypeMap{}, FieldMap{}, FieldMap{}, FieldMap{}) return NewContext(db, NewConsoleLogger([]string{}))
} }
func fatalErr(t * testing.T, err error) { func fatalErr(t * testing.T, err error) {