|
|
|
@ -7,7 +7,6 @@ import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"reflect"
|
|
|
|
|
"runtime"
|
|
|
|
|
"slices"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"sync"
|
|
|
|
@ -30,10 +29,14 @@ var (
|
|
|
|
|
type SerializeFn func(ctx *Context, value reflect.Value) ([]byte, error)
|
|
|
|
|
type DeserializeFn func(ctx *Context, data []byte) (reflect.Value, []byte, error)
|
|
|
|
|
|
|
|
|
|
type FieldInfo struct {
|
|
|
|
|
type NodeFieldInfo struct {
|
|
|
|
|
Extension ExtType
|
|
|
|
|
Index []int
|
|
|
|
|
Type graphql.Type
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type StructFieldInfo struct {
|
|
|
|
|
Index []int
|
|
|
|
|
Tag string
|
|
|
|
|
NodeTag string
|
|
|
|
|
Type reflect.Type
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -42,30 +45,37 @@ type TypeInfo struct {
|
|
|
|
|
Reflect reflect.Type
|
|
|
|
|
Type graphql.Type
|
|
|
|
|
|
|
|
|
|
Fields map[FieldTag]FieldInfo
|
|
|
|
|
Fields map[FieldTag]StructFieldInfo
|
|
|
|
|
PostDeserializeIndex int
|
|
|
|
|
|
|
|
|
|
Serialize SerializeFn
|
|
|
|
|
Deserialize DeserializeFn
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ExtensionFieldInfo struct {
|
|
|
|
|
Index []int
|
|
|
|
|
Type reflect.Type
|
|
|
|
|
NodeTag string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type ExtensionInfo struct {
|
|
|
|
|
ExtType
|
|
|
|
|
Fields map[string]FieldInfo
|
|
|
|
|
Type reflect.Type
|
|
|
|
|
Fields map[string]ExtensionFieldInfo
|
|
|
|
|
Data interface{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type NodeInfo struct {
|
|
|
|
|
NodeType
|
|
|
|
|
Type *graphql.Object
|
|
|
|
|
Interface *graphql.Interface
|
|
|
|
|
Extensions []ExtType
|
|
|
|
|
Fields map[string]ExtType
|
|
|
|
|
RequiredExtensions []ExtType
|
|
|
|
|
Fields map[string]NodeFieldInfo
|
|
|
|
|
ReverseFields map[ExtType]map[Tag]string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type InterfaceInfo struct {
|
|
|
|
|
Serialized SerializedType
|
|
|
|
|
Reflect reflect.Type
|
|
|
|
|
Type *graphql.Interface
|
|
|
|
|
Fields map[string]graphql.Type
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// A Context stores all the data to run a graphvent process
|
|
|
|
@ -77,23 +87,30 @@ type Context struct {
|
|
|
|
|
Log Logger
|
|
|
|
|
|
|
|
|
|
// Mapped types
|
|
|
|
|
TypeMap map[SerializedType]*TypeInfo
|
|
|
|
|
TypeTypes map[reflect.Type]*TypeInfo
|
|
|
|
|
Types map[reflect.Type]*TypeInfo
|
|
|
|
|
TypesReverse map[SerializedType]*TypeInfo
|
|
|
|
|
|
|
|
|
|
// Map between database extension hashes and the registered info
|
|
|
|
|
Extensions map[ExtType]*ExtensionInfo
|
|
|
|
|
ExtensionTypes map[reflect.Type]*ExtensionInfo
|
|
|
|
|
Extensions map[ExtType]ExtensionInfo
|
|
|
|
|
|
|
|
|
|
Interfaces map[SerializedType]*InterfaceInfo
|
|
|
|
|
InterfaceTypes map[reflect.Type]*InterfaceInfo
|
|
|
|
|
// Map between GQL interface name and the registered info
|
|
|
|
|
Interfaces map[string]InterfaceInfo
|
|
|
|
|
|
|
|
|
|
// Map between database type hashes and the registered info
|
|
|
|
|
Nodes map[NodeType]*NodeInfo
|
|
|
|
|
NodeTypes map[string]*NodeInfo
|
|
|
|
|
// Map between database node type hashes and the registered info
|
|
|
|
|
NodeTypes map[NodeType]NodeInfo
|
|
|
|
|
|
|
|
|
|
// Routing map to all the nodes local to this context
|
|
|
|
|
nodeMapLock sync.RWMutex
|
|
|
|
|
nodeMap map[NodeID]*Node
|
|
|
|
|
nodesLock sync.RWMutex
|
|
|
|
|
nodes map[NodeID]*Node
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func gqltype(ctx *Context, t reflect.Type, node_type string) graphql.Type {
|
|
|
|
|
gql, err := ctx.GQLType(t, node_type)
|
|
|
|
|
if err != nil {
|
|
|
|
|
panic(err)
|
|
|
|
|
} else {
|
|
|
|
|
return gql
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ctx *Context) GQLType(t reflect.Type, node_type string) (graphql.Type, error) {
|
|
|
|
@ -101,30 +118,37 @@ func (ctx *Context) GQLType(t reflect.Type, node_type string) (graphql.Type, err
|
|
|
|
|
if node_type == "" {
|
|
|
|
|
node_type = "Base"
|
|
|
|
|
}
|
|
|
|
|
node_info, mapped := ctx.NodeTypes[node_type]
|
|
|
|
|
|
|
|
|
|
interface_info, mapped := ctx.Interfaces[node_type]
|
|
|
|
|
if mapped == false {
|
|
|
|
|
type_info, mapped := ctx.NodeTypes[NodeTypeFor(node_type)]
|
|
|
|
|
if mapped {
|
|
|
|
|
return type_info.Type, nil
|
|
|
|
|
} else {
|
|
|
|
|
return nil, fmt.Errorf("Cannot get GQL type for unregistered Node Type \"%s\"", node_type)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
return node_info.Interface, nil
|
|
|
|
|
return interface_info.Type, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
info, mapped := ctx.TypeTypes[t]
|
|
|
|
|
info, mapped := ctx.Types[t]
|
|
|
|
|
if mapped {
|
|
|
|
|
return info.Type, nil
|
|
|
|
|
} else {
|
|
|
|
|
switch t.Kind() {
|
|
|
|
|
case reflect.Array:
|
|
|
|
|
info, mapped := ctx.TypeTypes[t.Elem()]
|
|
|
|
|
info, mapped := ctx.Types[t.Elem()]
|
|
|
|
|
if mapped {
|
|
|
|
|
return graphql.NewList(info.Type), nil
|
|
|
|
|
}
|
|
|
|
|
case reflect.Slice:
|
|
|
|
|
info, mapped := ctx.TypeTypes[t.Elem()]
|
|
|
|
|
info, mapped := ctx.Types[t.Elem()]
|
|
|
|
|
if mapped {
|
|
|
|
|
return graphql.NewList(info.Type), nil
|
|
|
|
|
}
|
|
|
|
|
case reflect.Map:
|
|
|
|
|
info, exists := ctx.TypeTypes[t]
|
|
|
|
|
info, exists := ctx.Types[t]
|
|
|
|
|
if exists {
|
|
|
|
|
return info.Type, nil
|
|
|
|
|
} else {
|
|
|
|
@ -132,7 +156,7 @@ func (ctx *Context) GQLType(t reflect.Type, node_type string) (graphql.Type, err
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
map_type := ctx.TypeTypes[t].Type
|
|
|
|
|
map_type := ctx.Types[t].Type
|
|
|
|
|
return map_type, nil
|
|
|
|
|
}
|
|
|
|
|
case reflect.Pointer:
|
|
|
|
@ -217,12 +241,12 @@ func RegisterMap(ctx *Context, reflect_type reflect.Type, node_type string) erro
|
|
|
|
|
gql_map := graphql.NewList(gql_pair)
|
|
|
|
|
|
|
|
|
|
serialized_type := SerializeType(reflect_type)
|
|
|
|
|
ctx.TypeMap[serialized_type] = &TypeInfo{
|
|
|
|
|
ctx.Types[reflect_type] = &TypeInfo{
|
|
|
|
|
Serialized: serialized_type,
|
|
|
|
|
Reflect: reflect_type,
|
|
|
|
|
Type: gql_map,
|
|
|
|
|
}
|
|
|
|
|
ctx.TypeTypes[reflect_type] = ctx.TypeMap[serialized_type]
|
|
|
|
|
ctx.TypesReverse[serialized_type] = ctx.Types[reflect_type]
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
@ -231,15 +255,18 @@ func BuildSchema(ctx *Context, query, mutation *graphql.Object) (graphql.Schema,
|
|
|
|
|
types := []graphql.Type{}
|
|
|
|
|
ctx.Log.Logf("gql", "Building Schema")
|
|
|
|
|
|
|
|
|
|
for _, info := range(ctx.TypeMap) {
|
|
|
|
|
for _, info := range(ctx.Types) {
|
|
|
|
|
if info.Type != nil {
|
|
|
|
|
types = append(types, info.Type)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, info := range(ctx.Nodes) {
|
|
|
|
|
for _, info := range(ctx.NodeTypes) {
|
|
|
|
|
types = append(types, info.Type)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, info := range(ctx.Interfaces) {
|
|
|
|
|
types = append(types, info.Type)
|
|
|
|
|
types = append(types, info.Interface)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
subscription := graphql.NewObject(graphql.ObjectConfig{
|
|
|
|
@ -287,78 +314,69 @@ func BuildSchema(ctx *Context, query, mutation *graphql.Object) (graphql.Schema,
|
|
|
|
|
|
|
|
|
|
func RegisterExtension[E any, T interface { *E; Extension}](ctx *Context, data interface{}) error {
|
|
|
|
|
reflect_type := reflect.TypeFor[E]()
|
|
|
|
|
ext_type := ExtType(SerializedTypeFor[E]())
|
|
|
|
|
ext_type := ExtTypeFor[E, T]()
|
|
|
|
|
_, exists := ctx.Extensions[ext_type]
|
|
|
|
|
if exists == true {
|
|
|
|
|
return fmt.Errorf("Cannot register extension %+v of type %+v, type already exists in context", reflect_type, ext_type)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fields := map[string]FieldInfo{}
|
|
|
|
|
fields := map[string]ExtensionFieldInfo{}
|
|
|
|
|
|
|
|
|
|
for _, field := range(reflect.VisibleFields(reflect_type)) {
|
|
|
|
|
gv_tag, tagged_gv := field.Tag.Lookup("gv")
|
|
|
|
|
node_tag := field.Tag.Get("node")
|
|
|
|
|
if tagged_gv {
|
|
|
|
|
fields[gv_tag] = FieldInfo{
|
|
|
|
|
fields[gv_tag] = ExtensionFieldInfo{
|
|
|
|
|
Index: field.Index,
|
|
|
|
|
Tag: gv_tag,
|
|
|
|
|
NodeTag: field.Tag.Get("node"),
|
|
|
|
|
Type: field.Type,
|
|
|
|
|
NodeTag: node_tag,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.Extensions[ext_type] = &ExtensionInfo{
|
|
|
|
|
ctx.Extensions[ext_type] = ExtensionInfo{
|
|
|
|
|
ExtType: ext_type,
|
|
|
|
|
Type: reflect_type,
|
|
|
|
|
Data: data,
|
|
|
|
|
Fields: fields,
|
|
|
|
|
}
|
|
|
|
|
ctx.ExtensionTypes[reflect_type] = ctx.Extensions[ext_type]
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func RegisterNodeType(ctx *Context, name string, extensions []ExtType) error {
|
|
|
|
|
node_type := NodeTypeFor(extensions)
|
|
|
|
|
_, exists := ctx.Nodes[node_type]
|
|
|
|
|
if exists == true {
|
|
|
|
|
return fmt.Errorf("Cannot register node type %+v, type already exists in context", node_type)
|
|
|
|
|
type FieldMapping struct {
|
|
|
|
|
Extension ExtType
|
|
|
|
|
Tag string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fields := map[string]ExtType{}
|
|
|
|
|
|
|
|
|
|
ext_found := map[ExtType]bool{}
|
|
|
|
|
for _, extension := range(extensions) {
|
|
|
|
|
ext_info, in_ctx := ctx.Extensions[extension]
|
|
|
|
|
if in_ctx == false {
|
|
|
|
|
return fmt.Errorf("Cannot register node type %+v, required extension %+v not in context", name, extension)
|
|
|
|
|
func RegisterNodeInterface(ctx *Context, name string, fields map[string]graphql.Type) error {
|
|
|
|
|
_, exists := ctx.Interfaces[name]
|
|
|
|
|
if exists {
|
|
|
|
|
return fmt.Errorf("Cannot register Node Interface %s, already registered", name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, duplicate := ext_found[extension]
|
|
|
|
|
if duplicate == true {
|
|
|
|
|
return fmt.Errorf("Duplicate extension %+v found in extension list", extension)
|
|
|
|
|
gql_fields := graphql.Fields{
|
|
|
|
|
"ID": &graphql.Field{
|
|
|
|
|
Type: ctx.Types[reflect.TypeFor[NodeID]()].Type,
|
|
|
|
|
},
|
|
|
|
|
"Type": &graphql.Field{
|
|
|
|
|
Type: ctx.Types[reflect.TypeFor[NodeType]()].Type,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ext_found[extension] = true
|
|
|
|
|
|
|
|
|
|
for field_name := range(ext_info.Fields) {
|
|
|
|
|
_, exists := fields[field_name]
|
|
|
|
|
for field_name, field_type := range(fields) {
|
|
|
|
|
_, exists := gql_fields[field_name]
|
|
|
|
|
if exists {
|
|
|
|
|
return fmt.Errorf("Cannot register NodeType %s with duplicate field name %s", name, field_name)
|
|
|
|
|
return fmt.Errorf("Cannot register interface %s with duplicate field %s", name, field_name)
|
|
|
|
|
}
|
|
|
|
|
fields[field_name] = extension
|
|
|
|
|
gql_fields[field_name] = &graphql.Field{
|
|
|
|
|
Type: field_type,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gql_interface := graphql.NewInterface(graphql.InterfaceConfig{
|
|
|
|
|
gql := graphql.NewInterface(graphql.InterfaceConfig{
|
|
|
|
|
Name: name,
|
|
|
|
|
Fields: graphql.Fields{
|
|
|
|
|
"ID": &graphql.Field{
|
|
|
|
|
Type: ctx.TypeTypes[reflect.TypeFor[NodeID]()].Type,
|
|
|
|
|
},
|
|
|
|
|
"Type": &graphql.Field{
|
|
|
|
|
Type: ctx.TypeTypes[reflect.TypeFor[NodeType]()].Type,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Fields: gql_fields,
|
|
|
|
|
ResolveType: func(p graphql.ResolveTypeParams) *graphql.Object {
|
|
|
|
|
ctx_val := p.Context.Value("resolve")
|
|
|
|
|
ctx, ok := ctx_val.(*ResolveContext)
|
|
|
|
@ -372,73 +390,78 @@ func RegisterNodeType(ctx *Context, name string, extensions []ExtType) error {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node_info, exists := ctx.Context.Nodes[val.NodeType]
|
|
|
|
|
node_info, exists := ctx.Context.NodeTypes[val.NodeType]
|
|
|
|
|
if exists == false {
|
|
|
|
|
ctx.Context.Log.Logf("gql", "Interface ResolveType got bad NodeType", val.NodeType)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, ext_type := range(extensions) {
|
|
|
|
|
if slices.Contains(node_info.Extensions, ext_type) == false {
|
|
|
|
|
ctx.Context.Log.Logf("gql", "Interface ResolveType for %s missing extensions %s: %+v", name, ext_type, val)
|
|
|
|
|
return node_info.Type
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ctx.Interfaces[name] = InterfaceInfo{
|
|
|
|
|
Type: gql,
|
|
|
|
|
Fields: fields,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func RegisterNodeType(ctx *Context, name string, mappings map[string]FieldMapping) error {
|
|
|
|
|
node_type := NodeTypeFor(name)
|
|
|
|
|
_, exists := ctx.NodeTypes[node_type]
|
|
|
|
|
if exists {
|
|
|
|
|
return fmt.Errorf("Cannot register node type %s, already registered", name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return node_info.Type
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
fields := map[string]NodeFieldInfo{}
|
|
|
|
|
reverse_fields := map[ExtType]map[Tag]string{}
|
|
|
|
|
|
|
|
|
|
gql := graphql.NewObject(graphql.ObjectConfig{
|
|
|
|
|
Name: name + "Node",
|
|
|
|
|
Interfaces: ctx.GQLInterfaces(node_type, extensions),
|
|
|
|
|
Fields: graphql.Fields{
|
|
|
|
|
gql_fields := graphql.Fields{
|
|
|
|
|
"ID": &graphql.Field{
|
|
|
|
|
Type: ctx.TypeTypes[reflect.TypeFor[NodeID]()].Type,
|
|
|
|
|
Type: ctx.Types[reflect.TypeFor[NodeID]()].Type,
|
|
|
|
|
Resolve: ResolveNodeID,
|
|
|
|
|
},
|
|
|
|
|
"Type": &graphql.Field{
|
|
|
|
|
Type: ctx.TypeTypes[reflect.TypeFor[NodeType]()].Type,
|
|
|
|
|
Type: ctx.Types[reflect.TypeFor[NodeType]()].Type,
|
|
|
|
|
Resolve: ResolveNodeType,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
|
|
|
|
|
source, ok := p.Value.(NodeResult)
|
|
|
|
|
if ok == false {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return source.NodeType == node_type
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ctx.Nodes[node_type] = &NodeInfo{
|
|
|
|
|
NodeType: node_type,
|
|
|
|
|
Interface: gql_interface,
|
|
|
|
|
Type: gql,
|
|
|
|
|
Extensions: extensions,
|
|
|
|
|
Fields: fields,
|
|
|
|
|
ext_map := map[ExtType]bool{}
|
|
|
|
|
for field_name, mapping := range(mappings) {
|
|
|
|
|
_, duplicate := fields[field_name]
|
|
|
|
|
if duplicate {
|
|
|
|
|
return fmt.Errorf("Cannot register node type %s, contains duplicate field %s", name, field_name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ext_info, exists := ctx.Extensions[mapping.Extension]
|
|
|
|
|
if exists == false {
|
|
|
|
|
return fmt.Errorf("Cannot register node type %s, unknown extension %s", name, mapping.Extension)
|
|
|
|
|
}
|
|
|
|
|
ctx.NodeTypes[name] = ctx.Nodes[node_type]
|
|
|
|
|
|
|
|
|
|
for _, ext_type := range(extensions) {
|
|
|
|
|
ext_info, ext_found := ctx.Extensions[ext_type]
|
|
|
|
|
if ext_found == false {
|
|
|
|
|
return fmt.Errorf("Extension %s not found", ext_type)
|
|
|
|
|
ext_map[mapping.Extension] = true
|
|
|
|
|
|
|
|
|
|
ext_field, exists := ext_info.Fields[mapping.Tag]
|
|
|
|
|
if exists == false {
|
|
|
|
|
return fmt.Errorf("Cannot register node type %s, extension %s has no field %s", name, mapping.Extension, mapping.Tag)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for field_name, field_info := range(ext_info.Fields) {
|
|
|
|
|
gql_type, err := ctx.GQLType(field_info.Type, field_info.NodeTag)
|
|
|
|
|
gql_type, err := ctx.GQLType(ext_field.Type, ext_field.NodeTag)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
return fmt.Errorf("Cannot register node type %s, GQLType error: %w", name, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gql_resolve := ctx.GQLResolve(field_info.Type, field_info.NodeTag)
|
|
|
|
|
gql_resolve := ctx.GQLResolve(ext_field.Type, ext_field.NodeTag)
|
|
|
|
|
|
|
|
|
|
gql_interface.AddFieldConfig(field_name, &graphql.Field{
|
|
|
|
|
fields[field_name] = NodeFieldInfo{
|
|
|
|
|
Extension: mapping.Extension,
|
|
|
|
|
Index: ext_field.Index,
|
|
|
|
|
Type: gql_type,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gql.AddFieldConfig(field_name, &graphql.Field{
|
|
|
|
|
gql_fields[field_name] = &graphql.Field{
|
|
|
|
|
Type: gql_type,
|
|
|
|
|
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
|
|
|
|
node, ok := p.Source.(NodeResult)
|
|
|
|
@ -446,38 +469,59 @@ func RegisterNodeType(ctx *Context, name string, extensions []ExtType) error {
|
|
|
|
|
return nil, fmt.Errorf("Can't resolve Node field on non-Node %s", reflect.TypeOf(p.Source))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node_info, mapped := ctx.Nodes[node.NodeType]
|
|
|
|
|
if mapped == false {
|
|
|
|
|
return nil, fmt.Errorf("Can't resolve unknown NodeType %s", node.NodeType)
|
|
|
|
|
return gql_resolve(node.Data[field_name], p)
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return gql_resolve(node.Data[node_info.Fields[field_name]][field_name], p)
|
|
|
|
|
gql := graphql.NewObject(graphql.ObjectConfig{
|
|
|
|
|
Name: name,
|
|
|
|
|
Interfaces: ctx.GQLInterfaces(fields),
|
|
|
|
|
Fields: gql_fields,
|
|
|
|
|
IsTypeOf: func(p graphql.IsTypeOfParams) bool {
|
|
|
|
|
source, ok := p.Value.(NodeResult)
|
|
|
|
|
if ok == false {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
return source.NodeType == node_type
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
extensions := []ExtType{}
|
|
|
|
|
for ext_type := range(ext_map) {
|
|
|
|
|
extensions = append(extensions, ext_type)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.NodeTypes[node_type] = NodeInfo{
|
|
|
|
|
NodeType: node_type,
|
|
|
|
|
Type: gql,
|
|
|
|
|
Fields: fields,
|
|
|
|
|
ReverseFields: reverse_fields,
|
|
|
|
|
RequiredExtensions: extensions,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ctx *Context) GQLInterfaces(known_type NodeType, extensions []ExtType) graphql.InterfacesThunk {
|
|
|
|
|
// Returns a function which returns a list of interfaces from the context whose fields are a subset of fields
|
|
|
|
|
func (ctx *Context) GQLInterfaces(fields map[string]NodeFieldInfo) graphql.InterfacesThunk {
|
|
|
|
|
return func() []*graphql.Interface {
|
|
|
|
|
interfaces := []*graphql.Interface{}
|
|
|
|
|
for node_type, node_info := range(ctx.Nodes) {
|
|
|
|
|
if node_type != known_type {
|
|
|
|
|
has_ext := true
|
|
|
|
|
for _, ext := range(node_info.Extensions) {
|
|
|
|
|
if slices.Contains(extensions, ext) == false {
|
|
|
|
|
has_ext = false
|
|
|
|
|
for _, interface_info := range(ctx.Interfaces) {
|
|
|
|
|
match := true
|
|
|
|
|
for field_name, field_type := range(interface_info.Fields) {
|
|
|
|
|
field, exists := fields[field_name]
|
|
|
|
|
if exists == false {
|
|
|
|
|
match = false
|
|
|
|
|
break
|
|
|
|
|
} else if field.Type != field_type {
|
|
|
|
|
match = false
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if has_ext == false {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if match {
|
|
|
|
|
interfaces = append(interfaces, interface_info.Type)
|
|
|
|
|
}
|
|
|
|
|
interfaces = append(interfaces, node_info.Interface)
|
|
|
|
|
}
|
|
|
|
|
return interfaces
|
|
|
|
|
}
|
|
|
|
@ -491,7 +535,7 @@ func RegisterObject[T any](ctx *Context) error {
|
|
|
|
|
reflect_type := reflect.TypeFor[T]()
|
|
|
|
|
serialized_type := SerializedTypeFor[T]()
|
|
|
|
|
|
|
|
|
|
_, exists := ctx.TypeTypes[reflect_type]
|
|
|
|
|
_, exists := ctx.Types[reflect_type]
|
|
|
|
|
if exists {
|
|
|
|
|
return fmt.Errorf("%+v already registered in TypeMap", reflect_type)
|
|
|
|
|
}
|
|
|
|
@ -505,7 +549,7 @@ func RegisterObject[T any](ctx *Context) error {
|
|
|
|
|
Fields: graphql.Fields{},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
field_infos := map[FieldTag]FieldInfo{}
|
|
|
|
|
field_infos := map[FieldTag]StructFieldInfo{}
|
|
|
|
|
|
|
|
|
|
post_deserialize, post_deserialize_exists := reflect.PointerTo(reflect_type).MethodByName("PostDeserialize")
|
|
|
|
|
post_deserialize_index := -1
|
|
|
|
@ -517,12 +561,11 @@ func RegisterObject[T any](ctx *Context) error {
|
|
|
|
|
gv_tag, tagged_gv := field.Tag.Lookup("gv")
|
|
|
|
|
if tagged_gv {
|
|
|
|
|
node_tag := field.Tag.Get("node")
|
|
|
|
|
field_infos[GetFieldTag(gv_tag)] = FieldInfo{
|
|
|
|
|
field_infos[GetFieldTag(gv_tag)] = StructFieldInfo{
|
|
|
|
|
Type: field.Type,
|
|
|
|
|
Index: field.Index,
|
|
|
|
|
NodeTag: node_tag,
|
|
|
|
|
Tag: gv_tag,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gql_type, err := ctx.GQLType(field.Type, node_tag)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
@ -552,14 +595,14 @@ func RegisterObject[T any](ctx *Context) error {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.TypeMap[serialized_type] = &TypeInfo{
|
|
|
|
|
ctx.Types[reflect_type] = &TypeInfo{
|
|
|
|
|
PostDeserializeIndex: post_deserialize_index,
|
|
|
|
|
Serialized: serialized_type,
|
|
|
|
|
Reflect: reflect_type,
|
|
|
|
|
Fields: field_infos,
|
|
|
|
|
Type: gql,
|
|
|
|
|
}
|
|
|
|
|
ctx.TypeTypes[reflect_type] = ctx.TypeMap[serialized_type]
|
|
|
|
|
ctx.TypesReverse[serialized_type] = ctx.Types[reflect_type]
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
@ -568,12 +611,12 @@ func RegisterObjectNoGQL[T any](ctx *Context) error {
|
|
|
|
|
reflect_type := reflect.TypeFor[T]()
|
|
|
|
|
serialized_type := SerializedTypeFor[T]()
|
|
|
|
|
|
|
|
|
|
_, exists := ctx.TypeTypes[reflect_type]
|
|
|
|
|
_, exists := ctx.Types[reflect_type]
|
|
|
|
|
if exists {
|
|
|
|
|
return fmt.Errorf("%+v already registered in TypeMap", reflect_type)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
field_infos := map[FieldTag]FieldInfo{}
|
|
|
|
|
field_infos := map[FieldTag]StructFieldInfo{}
|
|
|
|
|
|
|
|
|
|
post_deserialize, post_deserialize_exists := reflect.PointerTo(reflect_type).MethodByName("PostDeserialize")
|
|
|
|
|
post_deserialize_index := -1
|
|
|
|
@ -584,24 +627,21 @@ func RegisterObjectNoGQL[T any](ctx *Context) error {
|
|
|
|
|
for _, field := range(reflect.VisibleFields(reflect_type)) {
|
|
|
|
|
gv_tag, tagged_gv := field.Tag.Lookup("gv")
|
|
|
|
|
if tagged_gv {
|
|
|
|
|
node_tag := field.Tag.Get("node")
|
|
|
|
|
field_infos[GetFieldTag(gv_tag)] = FieldInfo{
|
|
|
|
|
field_infos[GetFieldTag(gv_tag)] = StructFieldInfo{
|
|
|
|
|
Type: field.Type,
|
|
|
|
|
Index: field.Index,
|
|
|
|
|
NodeTag: node_tag,
|
|
|
|
|
Tag: gv_tag,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.TypeMap[serialized_type] = &TypeInfo{
|
|
|
|
|
ctx.Types[reflect_type] = &TypeInfo{
|
|
|
|
|
PostDeserializeIndex: post_deserialize_index,
|
|
|
|
|
Serialized: serialized_type,
|
|
|
|
|
Reflect: reflect_type,
|
|
|
|
|
Fields: field_infos,
|
|
|
|
|
Type: nil,
|
|
|
|
|
}
|
|
|
|
|
ctx.TypeTypes[reflect_type] = ctx.TypeMap[serialized_type]
|
|
|
|
|
ctx.TypesReverse[serialized_type] = ctx.Types[reflect_type]
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
@ -726,7 +766,7 @@ func RegisterEnum[E comparable](ctx *Context, str_map map[E]string) error {
|
|
|
|
|
reflect_type := reflect.TypeFor[E]()
|
|
|
|
|
serialized_type := SerializedTypeFor[E]()
|
|
|
|
|
|
|
|
|
|
_, exists := ctx.TypeTypes[reflect_type]
|
|
|
|
|
_, exists := ctx.Types[reflect_type]
|
|
|
|
|
if exists {
|
|
|
|
|
return fmt.Errorf("%+v already registered in TypeMap", reflect_type)
|
|
|
|
|
}
|
|
|
|
@ -745,21 +785,21 @@ func RegisterEnum[E comparable](ctx *Context, str_map map[E]string) error {
|
|
|
|
|
Values: value_config,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ctx.TypeMap[serialized_type] = &TypeInfo{
|
|
|
|
|
ctx.Types[reflect_type] = &TypeInfo{
|
|
|
|
|
Serialized: serialized_type,
|
|
|
|
|
Reflect: reflect_type,
|
|
|
|
|
Type: gql,
|
|
|
|
|
}
|
|
|
|
|
ctx.TypeTypes[reflect_type] = ctx.TypeMap[serialized_type]
|
|
|
|
|
ctx.TypesReverse[serialized_type] = ctx.Types[reflect_type]
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func RegisterScalar[S any](ctx *Context, to_json func(interface{})interface{}, from_json func(interface{})interface{}, from_ast func(ast.Value)interface{}) error {
|
|
|
|
|
func RegisterScalar[S any](ctx *Context, to_json func(interface{})interface{}, from_json func(interface{})interface{}, from_ast func(ast.Value)interface{}, serialize SerializeFn, deserialize DeserializeFn) error {
|
|
|
|
|
reflect_type := reflect.TypeFor[S]()
|
|
|
|
|
serialized_type := SerializedTypeFor[S]()
|
|
|
|
|
|
|
|
|
|
_, exists := ctx.TypeTypes[reflect_type]
|
|
|
|
|
_, exists := ctx.Types[reflect_type]
|
|
|
|
|
if exists {
|
|
|
|
|
return fmt.Errorf("%+v already registered in TypeMap", reflect_type)
|
|
|
|
|
}
|
|
|
|
@ -772,26 +812,29 @@ func RegisterScalar[S any](ctx *Context, to_json func(interface{})interface{}, f
|
|
|
|
|
ParseLiteral: from_ast,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
ctx.TypeMap[serialized_type] = &TypeInfo{
|
|
|
|
|
ctx.Types[reflect_type] = &TypeInfo{
|
|
|
|
|
Serialized: serialized_type,
|
|
|
|
|
Reflect: reflect_type,
|
|
|
|
|
Type: gql,
|
|
|
|
|
|
|
|
|
|
Serialize: serialize,
|
|
|
|
|
Deserialize: deserialize,
|
|
|
|
|
}
|
|
|
|
|
ctx.TypeTypes[reflect_type] = ctx.TypeMap[serialized_type]
|
|
|
|
|
ctx.TypesReverse[serialized_type] = ctx.Types[reflect_type]
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ctx *Context) AddNode(id NodeID, node *Node) {
|
|
|
|
|
ctx.nodeMapLock.Lock()
|
|
|
|
|
ctx.nodeMap[id] = node
|
|
|
|
|
ctx.nodeMapLock.Unlock()
|
|
|
|
|
ctx.nodesLock.Lock()
|
|
|
|
|
ctx.nodes[id] = node
|
|
|
|
|
ctx.nodesLock.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ctx *Context) Node(id NodeID) (*Node, bool) {
|
|
|
|
|
ctx.nodeMapLock.RLock()
|
|
|
|
|
node, exists := ctx.nodeMap[id]
|
|
|
|
|
ctx.nodeMapLock.RUnlock()
|
|
|
|
|
ctx.nodesLock.RLock()
|
|
|
|
|
node, exists := ctx.nodes[id]
|
|
|
|
|
ctx.nodesLock.RUnlock()
|
|
|
|
|
return node, exists
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -805,25 +848,25 @@ func (ctx *Context) Delete(id NodeID) error {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ctx *Context) Unload(id NodeID) error {
|
|
|
|
|
ctx.nodeMapLock.Lock()
|
|
|
|
|
defer ctx.nodeMapLock.Unlock()
|
|
|
|
|
node, exists := ctx.nodeMap[id]
|
|
|
|
|
ctx.nodesLock.Lock()
|
|
|
|
|
defer ctx.nodesLock.Unlock()
|
|
|
|
|
node, exists := ctx.nodes[id]
|
|
|
|
|
if exists == false {
|
|
|
|
|
return fmt.Errorf("%s is not a node in ctx", id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err := node.Unload(ctx)
|
|
|
|
|
delete(ctx.nodeMap, id)
|
|
|
|
|
delete(ctx.nodes, id)
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ctx *Context) Stop() {
|
|
|
|
|
ctx.nodeMapLock.Lock()
|
|
|
|
|
for id, node := range(ctx.nodeMap) {
|
|
|
|
|
ctx.nodesLock.Lock()
|
|
|
|
|
for id, node := range(ctx.nodes) {
|
|
|
|
|
node.Unload(ctx)
|
|
|
|
|
delete(ctx.nodeMap, id)
|
|
|
|
|
delete(ctx.nodes, id)
|
|
|
|
|
}
|
|
|
|
|
ctx.nodeMapLock.Unlock()
|
|
|
|
|
ctx.nodesLock.Unlock()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (ctx *Context) Load(id NodeID) (*Node, error) {
|
|
|
|
@ -835,7 +878,6 @@ func (ctx *Context) Load(id NodeID) (*Node, error) {
|
|
|
|
|
ctx.AddNode(id, node)
|
|
|
|
|
started := make(chan error, 1)
|
|
|
|
|
go runNode(ctx, node, started)
|
|
|
|
|
|
|
|
|
|
err = <- started
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
@ -852,7 +894,7 @@ func (ctx *Context) getNode(id NodeID) (*Node, error) {
|
|
|
|
|
var err error
|
|
|
|
|
target, err = ctx.Load(id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to load node %s: %w", id, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return target, nil
|
|
|
|
@ -930,183 +972,203 @@ func (ctx *Context)GQLResolve(t reflect.Type, node_type string) (func(interface{
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func RegisterInterface[T any](ctx *Context) error {
|
|
|
|
|
serialized_type := SerializeType(reflect.TypeFor[T]())
|
|
|
|
|
reflect_type := reflect.TypeFor[T]()
|
|
|
|
|
|
|
|
|
|
_, exists := ctx.Interfaces[serialized_type]
|
|
|
|
|
if exists == true {
|
|
|
|
|
return fmt.Errorf("Interface %+v already exists in context", reflect_type)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.Interfaces[serialized_type] = &InterfaceInfo{
|
|
|
|
|
Serialized: serialized_type,
|
|
|
|
|
Reflect: reflect_type,
|
|
|
|
|
}
|
|
|
|
|
ctx.InterfaceTypes[reflect_type] = ctx.Interfaces[serialized_type]
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create a new Context with the base library content added
|
|
|
|
|
func NewContext(db * badger.DB, log Logger) (*Context, error) {
|
|
|
|
|
uuid.EnableRandPool()
|
|
|
|
|
|
|
|
|
|
ctx := &Context{
|
|
|
|
|
DB: db,
|
|
|
|
|
Log: log,
|
|
|
|
|
|
|
|
|
|
TypeMap: map[SerializedType]*TypeInfo{},
|
|
|
|
|
TypeTypes: map[reflect.Type]*TypeInfo{},
|
|
|
|
|
|
|
|
|
|
Extensions: map[ExtType]*ExtensionInfo{},
|
|
|
|
|
ExtensionTypes: map[reflect.Type]*ExtensionInfo{},
|
|
|
|
|
Types: map[reflect.Type]*TypeInfo{},
|
|
|
|
|
TypesReverse: map[SerializedType]*TypeInfo{},
|
|
|
|
|
Extensions: map[ExtType]ExtensionInfo{},
|
|
|
|
|
Interfaces: map[string]InterfaceInfo{},
|
|
|
|
|
NodeTypes: map[NodeType]NodeInfo{},
|
|
|
|
|
|
|
|
|
|
Interfaces: map[SerializedType]*InterfaceInfo{},
|
|
|
|
|
InterfaceTypes: map[reflect.Type]*InterfaceInfo{},
|
|
|
|
|
nodes: map[NodeID]*Node{},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Nodes: map[NodeType]*NodeInfo{},
|
|
|
|
|
NodeTypes: map[string]*NodeInfo{},
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
nodeMap: map[NodeID]*Node{},
|
|
|
|
|
err = RegisterScalar[NodeID](ctx, stringify, unstringify[NodeID], unstringifyAST[NodeID],
|
|
|
|
|
func(ctx *Context, value reflect.Value) ([]byte, error) {
|
|
|
|
|
return value.Bytes(), nil
|
|
|
|
|
}, func(ctx *Context, data []byte) (reflect.Value, []byte, error) {
|
|
|
|
|
if len(data) < 16 {
|
|
|
|
|
return reflect.Value{}, nil, fmt.Errorf("Not enough bytes to decode NodeID(got %d, want 16)", len(data))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
id := new(NodeID)
|
|
|
|
|
err := id.UnmarshalBinary(data[0:16])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return reflect.Value{}, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterScalar[NodeID](ctx, stringify, unstringify[NodeID], unstringifyAST[NodeID])
|
|
|
|
|
return reflect.ValueOf(id).Elem(), data[16:], nil
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register NodeID: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterInterface[Extension](ctx)
|
|
|
|
|
err = RegisterScalar[uuid.UUID](ctx, stringify, unstringify[uuid.UUID], unstringifyAST[uuid.UUID],
|
|
|
|
|
func(ctx *Context, value reflect.Value) ([]byte, error) {
|
|
|
|
|
return value.Bytes(), nil
|
|
|
|
|
}, func(ctx *Context, data []byte) (reflect.Value, []byte, error) {
|
|
|
|
|
if len(data) < 16 {
|
|
|
|
|
return reflect.Value{}, nil, fmt.Errorf("Not enough bytes to decode uuid.UUID(got %d, want 16)", len(data))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
id := new(uuid.UUID)
|
|
|
|
|
err := id.UnmarshalBinary(data[0:16])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return reflect.Value{}, nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterInterface[Signal](ctx)
|
|
|
|
|
return reflect.ValueOf(id).Elem(), data[16:], nil
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register uuid.UUID: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterScalar[NodeType](ctx, identity, coerce[NodeType], astInt[NodeType])
|
|
|
|
|
err = RegisterScalar[NodeType](ctx, identity, coerce[NodeType], astInt[NodeType], nil, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register NodeType: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterScalar[ExtType](ctx, identity, coerce[ExtType], astInt[ExtType])
|
|
|
|
|
err = RegisterScalar[ExtType](ctx, identity, coerce[ExtType], astInt[ExtType], nil, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register ExtType: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterNodeType(ctx, "Base", []ExtType{})
|
|
|
|
|
err = RegisterNodeInterface(ctx, "Base", map[string]graphql.Type{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register NodeInterface Base: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterScalar[bool](ctx, identity, coerce[bool], astBool[bool])
|
|
|
|
|
err = RegisterNodeType(ctx, "Node", map[string]FieldMapping{})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register NodeType Node: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterScalar[int](ctx, identity, coerce[int], astInt[int])
|
|
|
|
|
err = RegisterScalar[bool](ctx, identity, coerce[bool], astBool[bool], nil, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register bool: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterScalar[uint32](ctx, identity, coerce[uint32], astInt[uint32])
|
|
|
|
|
err = RegisterScalar[int](ctx, identity, coerce[int], astInt[int], nil, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register int: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterScalar[uint8](ctx, identity, coerce[uint8], astInt[uint8])
|
|
|
|
|
err = RegisterScalar[uint32](ctx, identity, coerce[uint32], astInt[uint32], nil, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register uint32: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterScalar[time.Time](ctx, stringify, unstringify[time.Time], unstringifyAST[time.Time])
|
|
|
|
|
err = RegisterScalar[uint8](ctx, identity, coerce[uint8], astInt[uint8], nil, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register uint8: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterScalar[string](ctx, identity, coerce[string], astString[string])
|
|
|
|
|
err = RegisterScalar[time.Time](ctx, stringify, unstringify[time.Time], unstringifyAST[time.Time], nil, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register time.Time: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterEnum[ReqState](ctx, ReqStateStrings)
|
|
|
|
|
err = RegisterScalar[string](ctx, identity, coerce[string], astString[string], nil, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register string: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterScalar[uuid.UUID](ctx, stringify, unstringify[uuid.UUID], unstringifyAST[uuid.UUID])
|
|
|
|
|
err = RegisterEnum[ReqState](ctx, ReqStateStrings)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register ReqState: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterScalar[Change](ctx, identity, coerce[Change], astString[Change])
|
|
|
|
|
err = RegisterScalar[Tag](ctx, identity, coerce[Tag], astString[Tag], nil, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register Tag: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: Register as a GQL type with Signal as an interface
|
|
|
|
|
err = RegisterObjectNoGQL[QueuedSignal](ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register QueuedSignal: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterSignal[TimeoutSignal](ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register TimeoutSignal: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterSignal[StatusSignal](ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register StatusSignal: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterObject[Node](ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register Node: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterExtension[LockableExt](ctx, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register LockableExt extension: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterExtension[ListenerExt](ctx, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register ListenerExt extension: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterExtension[GQLExt](ctx, nil)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register GQLExt extension: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterNodeType(ctx, "Lockable", []ExtType{ExtTypeFor[LockableExt]()})
|
|
|
|
|
err = RegisterNodeInterface(ctx, "Lockable", map[string]graphql.Type{
|
|
|
|
|
"LockableState": gqltype(ctx, reflect.TypeFor[ReqState](), ""),
|
|
|
|
|
"Requirements": gqltype(ctx, reflect.TypeFor[map[NodeID]ReqState](), ":Lockable"),
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register NodeInterface Lockable: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterNodeType(ctx, "LockableNode", map[string]FieldMapping{
|
|
|
|
|
"LockableState": {
|
|
|
|
|
Extension: ExtTypeFor[LockableExt](),
|
|
|
|
|
Tag: "state",
|
|
|
|
|
},
|
|
|
|
|
"Requirements": {
|
|
|
|
|
Extension: ExtTypeFor[LockableExt](),
|
|
|
|
|
Tag: "requirements",
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("Failed to register NodeType LockableNode: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterObject[LockableExt](ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register LockableExt object: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterObject[ListenerExt](ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register ListenerExt object: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = RegisterObject[GQLExt](ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to register GQLExt object: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
schema, err := BuildSchema(ctx, graphql.NewObject(graphql.ObjectConfig{
|
|
|
|
|
Name: "Query",
|
|
|
|
|
Fields: graphql.Fields{
|
|
|
|
|
"Self": &graphql.Field{
|
|
|
|
|
Type: ctx.NodeTypes["Base"].Interface,
|
|
|
|
|
Type: ctx.Interfaces["Base"].Type,
|
|
|
|
|
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
|
|
|
|
ctx, err := PrepResolve(p)
|
|
|
|
|
if err != nil {
|
|
|
|
@ -1117,10 +1179,10 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) {
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
"Node": &graphql.Field{
|
|
|
|
|
Type: ctx.NodeTypes["Base"].Interface,
|
|
|
|
|
Type: ctx.Interfaces["Base"].Type,
|
|
|
|
|
Args: graphql.FieldConfigArgument{
|
|
|
|
|
"id": &graphql.ArgumentConfig{
|
|
|
|
|
Type: ctx.TypeTypes[reflect.TypeFor[NodeID]()].Type,
|
|
|
|
|
Type: ctx.Types[reflect.TypeFor[NodeID]()].Type,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
|
|
|
|
@ -1145,10 +1207,12 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) {
|
|
|
|
|
},
|
|
|
|
|
}))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
return nil, fmt.Errorf("Failed to build schema: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ctx.ExtensionTypes[reflect.TypeFor[GQLExt]()].Data = schema
|
|
|
|
|
ext_info := ctx.Extensions[ExtTypeFor[GQLExt]()]
|
|
|
|
|
ext_info.Data = schema
|
|
|
|
|
ctx.Extensions[ExtTypeFor[GQLExt]()] = ext_info
|
|
|
|
|
|
|
|
|
|
return ctx, nil
|
|
|
|
|
}
|
|
|
|
|