graphvent/context.go

237 lines
6.5 KiB
Go

2023-07-09 14:30:30 -06:00
package graphvent
import (
"github.com/graphql-go/graphql"
badger "github.com/dgraph-io/badger/v3"
"reflect"
"fmt"
)
2023-07-10 22:31:43 -06:00
// NodeLoadFunc is the footprint of the function used to create a new node in memory from persisted bytes
2023-07-09 14:30:30 -06:00
type NodeLoadFunc func(*Context, NodeID, []byte, NodeMap)(Node, error)
2023-07-10 22:31:43 -06:00
// A NodeDef is a description of a node that can be added to a Context
2023-07-09 14:30:30 -06:00
type NodeDef struct {
Load NodeLoadFunc
Type NodeType
2023-07-10 21:15:01 -06:00
GQLType *graphql.Object
Reflect reflect.Type
2023-07-09 14:30:30 -06:00
}
2023-07-10 22:31:43 -06:00
// Create a new Node def, extracting the Type and Reflect from example
func NewNodeDef(example Node, load_func NodeLoadFunc, gql_type *graphql.Object) NodeDef {
2023-07-09 14:30:30 -06:00
return NodeDef{
Type: example.Type(),
2023-07-09 14:30:30 -06:00
Load: load_func,
2023-07-10 21:15:01 -06:00
GQLType: gql_type,
Reflect: reflect.TypeOf(example),
2023-07-09 14:30:30 -06:00
}
}
2023-07-10 22:31:43 -06:00
// A Context is all the data needed to run a graphvent
2023-07-09 14:30:30 -06:00
type Context struct {
2023-07-10 22:31:43 -06:00
// DB is the database connection used to load and write nodes
2023-07-09 14:30:30 -06:00
DB * badger.DB
2023-07-10 22:31:43 -06:00
// Log is an interface used to record events happening
2023-07-09 14:30:30 -06:00
Log Logger
2023-07-10 22:31:43 -06:00
// A mapping between type hashes and their corresponding node definitions
2023-07-09 14:30:30 -06:00
Types map[uint64]NodeDef
2023-07-10 22:31:43 -06:00
// GQL substructure
2023-07-10 21:15:01 -06:00
GQL GQLContext
2023-07-09 14:30:30 -06:00
}
2023-07-10 22:31:43 -06:00
// Recreate the GQL schema after making changes
2023-07-10 21:15:01 -06:00
func (ctx * Context) RebuildSchema() error {
schemaConfig := graphql.SchemaConfig{
Types: ctx.GQL.TypeList,
Query: ctx.GQL.Query,
Mutation: ctx.GQL.Mutation,
Subscription: ctx.GQL.Subscription,
2023-07-09 14:30:30 -06:00
}
2023-07-10 21:15:01 -06:00
schema, err := graphql.NewSchema(schemaConfig)
if err != nil {
return err
}
ctx.GQL.Schema = schema
return nil
}
2023-07-10 22:31:43 -06:00
// Add a non-node type to the gql context
2023-07-10 21:15:01 -06:00
func (ctx * Context) AddGQLType(gql_type graphql.Type) {
ctx.GQL.TypeList = append(ctx.GQL.TypeList, gql_type)
}
2023-07-10 22:31:43 -06:00
// Add a node to a context, returns an error if the def is invalid or already exists in the context
2023-07-10 21:15:01 -06:00
func (ctx * Context) RegisterNodeType(def NodeDef) error {
if def.Load == nil {
return fmt.Errorf("Cannot register a node without a load function: %s", def.Type)
}
if def.Reflect == nil {
return fmt.Errorf("Cannot register a node without a reflect type: %s", def.Type)
}
if def.GQLType == nil {
return fmt.Errorf("Cannot register a node without a gql type: %s", def.Type)
2023-07-09 14:30:30 -06:00
}
type_hash := def.Type.Hash()
_, exists := ctx.Types[type_hash]
if exists == true {
2023-07-10 21:15:01 -06:00
return fmt.Errorf("Cannot register node of type %s, type already exists in context", def.Type)
2023-07-09 14:30:30 -06:00
}
ctx.Types[type_hash] = def
2023-07-10 21:15:01 -06:00
2023-07-21 18:18:26 -06:00
node_type := reflect.TypeOf((*Node)(nil)).Elem()
2023-07-24 16:04:56 -06:00
lockable_type := reflect.TypeOf((*LockableNode)(nil)).Elem()
thread_type := reflect.TypeOf((*ThreadNode)(nil)).Elem()
2023-07-21 18:18:26 -06:00
if def.Reflect.Implements(node_type) {
2023-07-10 21:15:01 -06:00
ctx.GQL.ValidNodes[def.Reflect] = def.GQLType
}
2023-07-21 18:18:26 -06:00
if def.Reflect.Implements(lockable_type) {
2023-07-10 21:15:01 -06:00
ctx.GQL.ValidLockables[def.Reflect] = def.GQLType
}
2023-07-21 18:18:26 -06:00
if def.Reflect.Implements(thread_type) {
2023-07-10 21:15:01 -06:00
ctx.GQL.ValidThreads[def.Reflect] = def.GQLType
}
ctx.GQL.TypeList = append(ctx.GQL.TypeList, def.GQLType)
2023-07-09 14:30:30 -06:00
return nil
}
2023-07-10 22:31:43 -06:00
// Map of go types to graphql types
2023-07-09 14:30:30 -06:00
type ObjTypeMap map[reflect.Type]*graphql.Object
2023-07-10 22:31:43 -06:00
// GQL Specific Context information
2023-07-09 14:30:30 -06:00
type GQLContext struct {
2023-07-10 22:31:43 -06:00
// Generated GQL schema
2023-07-09 14:30:30 -06:00
Schema graphql.Schema
2023-07-10 21:15:01 -06:00
2023-07-10 22:31:43 -06:00
// List of GQL types
TypeList []graphql.Type
2023-07-09 14:30:30 -06:00
2023-07-10 22:31:43 -06:00
// Interface type maps to map go types of specific interfaces to gql types
2023-07-10 21:15:01 -06:00
ValidNodes ObjTypeMap
ValidLockables ObjTypeMap
ValidThreads ObjTypeMap
2023-07-09 14:30:30 -06:00
BaseNodeType *graphql.Object
BaseLockableType *graphql.Object
BaseThreadType *graphql.Object
2023-07-10 21:15:01 -06:00
Query *graphql.Object
Mutation *graphql.Object
Subscription *graphql.Object
}
2023-07-09 14:30:30 -06:00
2023-07-10 22:31:43 -06:00
// Create a new GQL context without any content
2023-07-10 21:15:01 -06:00
func NewGQLContext() GQLContext {
query := graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{},
})
2023-07-09 14:30:30 -06:00
2023-07-10 21:15:01 -06:00
mutation := graphql.NewObject(graphql.ObjectConfig{
Name: "Mutation",
Fields: graphql.Fields{},
})
2023-07-09 14:30:30 -06:00
2023-07-10 21:15:01 -06:00
subscription := graphql.NewObject(graphql.ObjectConfig{
Name: "Subscription",
Fields: graphql.Fields{},
})
2023-07-09 14:30:30 -06:00
ctx := GQLContext{
2023-07-10 21:15:01 -06:00
Schema: graphql.Schema{},
2023-07-10 22:31:43 -06:00
TypeList: []graphql.Type{},
2023-07-10 21:15:01 -06:00
ValidNodes: ObjTypeMap{},
ValidThreads: ObjTypeMap{},
ValidLockables: ObjTypeMap{},
Query: query,
Mutation: mutation,
Subscription: subscription,
2023-07-24 16:04:56 -06:00
BaseNodeType: GQLTypeSimpleNode.Type,
2023-07-21 18:18:26 -06:00
BaseLockableType: GQLTypeSimpleLockable.Type,
BaseThreadType: GQLTypeSimpleThread.Type,
2023-07-09 14:30:30 -06:00
}
2023-07-10 21:15:01 -06:00
return ctx
}
2023-07-09 14:30:30 -06:00
2023-07-10 22:31:43 -06:00
// Create a new Context with all the library content added
2023-07-10 21:15:01 -06:00
func NewContext(db * badger.DB, log Logger) * Context {
2023-07-09 14:30:30 -06:00
ctx := &Context{
2023-07-10 21:15:01 -06:00
GQL: NewGQLContext(),
2023-07-09 14:30:30 -06:00
DB: db,
Log: log,
Types: map[uint64]NodeDef{},
}
2023-07-24 16:04:56 -06:00
err := ctx.RegisterNodeType(NewNodeDef((*SimpleNode)(nil), LoadSimpleNode, GQLTypeSimpleNode.Type))
2023-07-09 14:30:30 -06:00
if err != nil {
panic(err)
}
2023-07-24 16:04:56 -06:00
err = ctx.RegisterNodeType(NewNodeDef((*Lockable)(nil), LoadLockable, GQLTypeSimpleLockable.Type))
2023-07-09 14:30:30 -06:00
if err != nil {
panic(err)
}
2023-07-24 16:04:56 -06:00
err = ctx.RegisterNodeType(NewNodeDef((*Listener)(nil), LoadListener, GQLTypeSimpleLockable.Type))
if err != nil {
panic(err)
}
err = ctx.RegisterNodeType(NewNodeDef((*Thread)(nil), LoadThread, GQLTypeSimpleThread.Type))
2023-07-09 14:30:30 -06:00
if err != nil {
panic(err)
}
err = ctx.RegisterNodeType(NewNodeDef((*GQLThread)(nil), LoadGQLThread, GQLTypeGQLThread.Type))
2023-07-09 14:30:30 -06:00
if err != nil {
panic(err)
2023-07-09 20:30:19 -06:00
}
err = ctx.RegisterNodeType(NewNodeDef((*User)(nil), LoadUser, GQLTypeUser.Type))
2023-07-20 00:24:22 -06:00
if err != nil {
panic(err)
}
2023-07-24 16:04:56 -06:00
err = ctx.RegisterNodeType(NewNodeDef((*PerNodePolicy)(nil), LoadPerNodePolicy, GQLTypeSimpleNode.Type))
2023-07-20 23:19:10 -06:00
if err != nil {
panic(err)
}
2023-07-24 16:04:56 -06:00
err = ctx.RegisterNodeType(NewNodeDef((*SimplePolicy)(nil), LoadSimplePolicy, GQLTypeSimpleNode.Type))
2023-07-21 13:33:04 -06:00
if err != nil {
panic(err)
}
2023-07-24 22:52:15 -06:00
err = ctx.RegisterNodeType(NewNodeDef((*DependencyPolicy)(nil), LoadSimplePolicy, GQLTypeSimpleNode.Type))
2023-07-21 13:55:27 -06:00
if err != nil {
panic(err)
}
2023-07-24 22:52:15 -06:00
err = ctx.RegisterNodeType(NewNodeDef((*ParentPolicy)(nil), LoadSimplePolicy, GQLTypeSimpleNode.Type))
if err != nil {
panic(err)
}
err = ctx.RegisterNodeType(NewNodeDef((*ChildrenPolicy)(nil), LoadSimplePolicy, GQLTypeSimpleNode.Type))
2023-07-24 01:12:30 -06:00
if err != nil {
panic(err)
}
2023-07-20 00:24:22 -06:00
ctx.AddGQLType(GQLTypeSignal.Type)
2023-07-10 21:15:01 -06:00
ctx.GQL.Query.AddFieldConfig("Self", GQLQuerySelf)
ctx.GQL.Query.AddFieldConfig("User", GQLQueryUser)
2023-07-10 21:15:01 -06:00
ctx.GQL.Subscription.AddFieldConfig("Update", GQLSubscriptionUpdate)
ctx.GQL.Subscription.AddFieldConfig("Self", GQLSubscriptionSelf)
2023-07-10 21:15:01 -06:00
ctx.GQL.Mutation.AddFieldConfig("abort", GQLMutationAbort)
ctx.GQL.Mutation.AddFieldConfig("startChild", GQLMutationStartChild)
2023-07-10 21:15:01 -06:00
err = ctx.RebuildSchema()
if err != nil {
panic(err)
2023-07-09 14:30:30 -06:00
}
return ctx
}