diff --git a/context.go b/context.go index ce6e2c8..b590745 100644 --- a/context.go +++ b/context.go @@ -1186,6 +1186,36 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) { return nil, err } + err = ctx.RegisterType(reflect.TypeOf(time.Duration(0)), DurationType, + func(ctx *Context, ctx_type SerializedType, reflect_type reflect.Type, value *reflect.Value)(SerializedValue,error){ + var data []byte + type_stack := []SerializedType{ctx_type} + if value == nil { + data = nil + } else { + data := make([]byte, 8) + binary.BigEndian.PutUint64(data, uint64(value.Int())) + } + return SerializedValue{ + type_stack, + data, + }, nil + },func(ctx *Context, value SerializedValue)(reflect.Type,*reflect.Value,SerializedValue,error){ + if value.Data == nil { + return reflect.TypeOf(time.Duration(0)), nil, value, nil + } else { + var bytes []byte + var err error + bytes, value, err = value.PopData(8) + if err != nil { + return nil, nil, value, err + } + duration := time.Duration(int64(binary.BigEndian.Uint64(bytes))) + duration_value := reflect.ValueOf(duration) + return duration_value.Type(), &duration_value, value, nil + } + }) + err = ctx.RegisterType(reflect.TypeOf(time.Time{}), TimeType, func(ctx *Context, ctx_type SerializedType, reflect_type reflect.Type, value *reflect.Value)(SerializedValue,error) { var data []byte @@ -1321,6 +1351,11 @@ func NewContext(db * badger.DB, log Logger) (*Context, error) { return nil, err } + err = ctx.RegisterSignal(reflect.TypeOf(StoppedSignal{}), StoppedSignalType) + if err != nil { + return nil, err + } + err = ctx.RegisterSignal(reflect.TypeOf(AddSubGroupSignal{}), AddSubGroupSignalType) if err != nil { return nil, err diff --git a/gql.go b/gql.go index e26c05b..98d93f9 100644 --- a/gql.go +++ b/gql.go @@ -843,6 +843,9 @@ type GQLExtContext struct { Query *graphql.Object Mutation *graphql.Object Subscription *graphql.Object + + TypeMap map[reflect.Type]GQLTypeInfo + KindMap map[reflect.Kind]GQLTypeInfo } func (ctx *GQLExtContext) GetACLFields(obj_name string, names []string) (map[ExtType][]string, error) { @@ -1159,6 +1162,110 @@ func NewGQLExtContext() *GQLExtContext { Fields: graphql.Fields{}, }) + kind_map := map[reflect.Kind]GQLTypeInfo{ + reflect.String: { + func(ctx *GQLExtContext, reflect_type reflect.Type)(graphql.Type, error) { + return graphql.String, nil + }, + func(ctx *GQLExtContext, value interface{})(reflect.Value, error) { + return reflect.ValueOf(value), nil + }, + }, + reflect.Bool: { + func(ctx *GQLExtContext, reflect_type reflect.Type)(graphql.Type, error) { + return graphql.Boolean, nil + }, + func(ctx *GQLExtContext, value interface{})(reflect.Value, error) { + return reflect.ValueOf(value), nil + }, + }, + } + type_map := map[reflect.Type]GQLTypeInfo{ + reflect.TypeOf([2]NodeID{}): { + func(ctx *GQLExtContext, reflect_type reflect.Type)(graphql.Type, error) { + return graphql.NewList(graphql.String), nil + }, + func(ctx *GQLExtContext, value interface{})(reflect.Value, error) { + l, ok := value.([]interface{}) + if ok == false { + return reflect.Value{}, fmt.Errorf("not list: %s", reflect.TypeOf(value)) + } else if len(l) != 2 { + return reflect.Value{}, fmt.Errorf("wrong length: %d/2", len(l)) + } + + id1_str, ok := l[0].(string) + if ok == false { + return reflect.Value{}, fmt.Errorf("not strg: %s", reflect.TypeOf(l[0])) + } + id1, err := ParseID(id1_str) + if err != nil { + return reflect.Value{}, err + } + id2_str, ok := l[1].(string) + if ok == false { + return reflect.Value{}, fmt.Errorf("not strg: %s", reflect.TypeOf(l[1])) + } + id2, err := ParseID(id2_str) + if err != nil { + return reflect.Value{}, err + } + return_value := reflect.New(reflect.TypeOf([2]NodeID{})).Elem() + return_value.Index(0).Set(reflect.ValueOf(id1)) + return_value.Index(1).Set(reflect.ValueOf(id2)) + + return return_value, nil + }, + }, + reflect.TypeOf(time.Time{}): { + func(ctx *GQLExtContext, reflect_type reflect.Type) (graphql.Type, error) { + return graphql.DateTime, nil + }, + func(ctx *GQLExtContext, value interface{}) (reflect.Value, error) { + return reflect.ValueOf(value), nil + }, + }, + reflect.TypeOf(&NodeID{}): { + func(ctx *GQLExtContext, reflect_type reflect.Type) (graphql.Type, error) { + return graphql.String, nil + }, + func(ctx *GQLExtContext, value interface{}) (reflect.Value, error) { + str, ok := value.(string) + if ok == false { + return reflect.Value{}, fmt.Errorf("value is not string") + } + + if str == "" { + return reflect.New(reflect.TypeOf(&NodeID{})).Elem(), nil + } + + id_parsed, err := ParseID(str) + if err != nil { + return reflect.Value{}, err + } + + return reflect.ValueOf(&id_parsed), nil + }, + }, + reflect.TypeOf(NodeID{}): { + func(ctx *GQLExtContext, reflect_type reflect.Type)(graphql.Type, error) { + return graphql.String, nil + }, + func(ctx *GQLExtContext, value interface{})(reflect.Value, error) { + str, ok := value.(string) + if ok == false { + return reflect.Value{}, fmt.Errorf("value is not string") + } + + id_parsed, err := ParseID(str) + if err != nil { + return reflect.Value{}, err + } + + return reflect.ValueOf(id_parsed), nil + }, + }, + } + context := GQLExtContext{ Schema: graphql.Schema{}, Types: []graphql.Type{}, @@ -1168,6 +1275,8 @@ func NewGQLExtContext() *GQLExtContext { NodeTypes: map[NodeType]*graphql.Object{}, Interfaces: map[string]*Interface{}, Fields: map[string]Field{}, + KindMap: kind_map, + TypeMap: type_map, } var err error diff --git a/gql_signal.go b/gql_signal.go index a5607f2..8efa43b 100644 --- a/gql_signal.go +++ b/gql_signal.go @@ -8,63 +8,25 @@ import ( "time" ) +type GQLTypeConverter func(*GQLExtContext, reflect.Type)(graphql.Type, error) +type GQLValueConverter func(*GQLExtContext, interface{})(reflect.Value, error) type GQLTypeInfo struct { - graphql.Type - FromGQL func(interface{})(reflect.Value, error) + Type GQLTypeConverter + Value GQLValueConverter } -var kind_gql_map = map[reflect.Kind]GQLTypeInfo{ - reflect.String: { - graphql.String, - func(value interface{})(reflect.Value, error) { - return reflect.ValueOf(value), nil - }, - }, - reflect.Bool: { - graphql.Boolean, - func(value interface{})(reflect.Value, error) { - return reflect.ValueOf(value), nil - }, - }, -} - -var type_gql_map = map[reflect.Type]GQLTypeInfo{ - reflect.TypeOf(&NodeID{}): { - graphql.String, - func(value interface{}) (reflect.Value, error) { - str, ok := value.(string) - if ok == false { - return reflect.Value{}, fmt.Errorf("value is not string") - } - - if str == "" { - return reflect.New(reflect.TypeOf(&NodeID{})).Elem(), nil - } - - id_parsed, err := ParseID(str) - if err != nil { - return reflect.Value{}, err - } - - return reflect.ValueOf(&id_parsed), nil - }, - }, - reflect.TypeOf(NodeID{}): { - graphql.String, - func(value interface{})(reflect.Value, error) { - str, ok := value.(string) - if ok == false { - return reflect.Value{}, fmt.Errorf("value is not string") - } - - id_parsed, err := ParseID(str) - if err != nil { - return reflect.Value{}, err - } - - return reflect.ValueOf(id_parsed), nil - }, - }, +func GetGQLTypeInfo(ctx *GQLExtContext, reflect_type reflect.Type) (*GQLTypeInfo, error) { + type_info, type_mapped := ctx.TypeMap[reflect_type] + if type_mapped == false { + kind_info, kind_mapped := ctx.KindMap[reflect_type.Kind()] + if kind_mapped == false { + return nil, fmt.Errorf("Signal has unsupported type/kind: %s/%s", reflect_type, reflect_type.Kind()) + } else { + return &kind_info, nil + } + } else { + return &type_info, nil + } } type StructFieldInfo struct { @@ -74,7 +36,7 @@ type StructFieldInfo struct { Index []int } -func SignalFromArgs(signal_type reflect.Type, fields []StructFieldInfo, args map[string]interface{}, id_index, direction_index []int) (Signal, error) { +func SignalFromArgs(ctx *GQLExtContext, signal_type reflect.Type, fields []StructFieldInfo, args map[string]interface{}, id_index, direction_index []int) (Signal, error) { signal_value := reflect.New(signal_type) id_field := signal_value.Elem().FieldByIndex(id_index) @@ -92,7 +54,7 @@ func SignalFromArgs(signal_type reflect.Type, fields []StructFieldInfo, args map if field_value.CanConvert(field.Type) == false { return nil, fmt.Errorf("Arg %s wrong type %s/%s", field.Name, field_value.Type(), field.Type) } - value, err := field.GQL.FromGQL(arg) + value, err := field.GQL.Value(ctx, arg) if err != nil { return nil, err } @@ -101,6 +63,25 @@ func SignalFromArgs(signal_type reflect.Type, fields []StructFieldInfo, args map return signal_value.Interface().(Signal), nil } +func ArgumentInfo(ctx *GQLExtContext, field reflect.StructField, gv_tag string) (*graphql.ArgumentConfig, StructFieldInfo, error) { + gql_info, err := GetGQLTypeInfo(ctx, field.Type) + if err != nil { + return nil, StructFieldInfo{}, err + } + gql_type, err := gql_info.Type(ctx, field.Type) + if err != nil { + return nil, StructFieldInfo{}, err + } + return &graphql.ArgumentConfig{ + Type: gql_type, + }, StructFieldInfo{ + gv_tag, + field.Type, + gql_info, + field.Index, + }, nil +} + func (ext *GQLExtContext) AddSignalMutation(name string, send_id_key string, signal_type reflect.Type) error { args := graphql.FieldConfigArgument{} arg_info := []StructFieldInfo{} @@ -118,27 +99,14 @@ func (ext *GQLExtContext) AddSignalMutation(name string, send_id_key string, sig _, exists := args[gv_tag] if exists == true { return fmt.Errorf("Signal has repeated tag %s", gv_tag) - } - var gql_info GQLTypeInfo - var type_mapped bool - gql_info, type_mapped = type_gql_map[field.Type] - if type_mapped == false { - var kind_mapped bool - gql_info, kind_mapped = kind_gql_map[field.Type.Kind()] - if kind_mapped == false { - return fmt.Errorf("Signal has unsupported type/kind: %s/%s", field.Type, field.Type.Kind()) + } else { + config, info, err := ArgumentInfo(ext, field, gv_tag) + if err != nil { + return err } + args[gv_tag] = config + arg_info = append(arg_info, info) } - - args[gv_tag] = &graphql.ArgumentConfig{ - Type: gql_info.Type, - } - arg_info = append(arg_info, StructFieldInfo{ - gv_tag, - field.Type, - &gql_info, - field.Index, - }) } } } @@ -161,7 +129,7 @@ func (ext *GQLExtContext) AddSignalMutation(name string, send_id_key string, sig return nil, err } - signal, err := SignalFromArgs(signal_type, arg_info, p.Args, id_index, direction_index) + signal, err := SignalFromArgs(ctx.GQLContext, signal_type, arg_info, p.Args, id_index, direction_index) if err != nil { return nil, err } diff --git a/serialize.go b/serialize.go index f22002f..1a0f4b1 100644 --- a/serialize.go +++ b/serialize.go @@ -106,6 +106,7 @@ var ( ACLSignalType = NewSignalType("ACL") AddSubGroupSignalType = NewSignalType("ADD_SUBGROUP") RemoveSubGroupSignalType = NewSignalType("REMOVE_SUBGROUP") + StoppedSignalType = NewSignalType("STOPPED") MemberOfPolicyType = NewPolicyType("USER_OF") RequirementOfPolicyType = NewPolicyType("REQUIEMENT_OF") @@ -152,6 +153,7 @@ var ( PendingACLType = NewSerializedType("PENDING_ACL") PendingSignalType = NewSerializedType("PENDING_SIGNAL") TimeType = NewSerializedType("TIME") + DurationType = NewSerializedType("DURATION") ResponseType = NewSerializedType("RESPONSE") StatusType = NewSerializedType("STATUS") TreeType = NewSerializedType("TREE")