diff --git a/gql.go b/gql.go index d93e04d..496d19c 100644 --- a/gql.go +++ b/gql.go @@ -10,6 +10,7 @@ import ( "errors" "fmt" "sync" + "time" ) func GraphiQLHandler() func(http.ResponseWriter, *http.Request) { @@ -93,6 +94,8 @@ func GraphiQLHandler() func(http.ResponseWriter, *http.Request) { type GQLQuery struct { Query string `json:"query"` + OperationName string `json:"operationName"` + Variables map[string]interface{} `json:"variables"` } func GQLHandler(schema graphql.Schema, ctx context.Context) func(http.ResponseWriter, *http.Request) { @@ -104,13 +107,24 @@ func GQLHandler(schema graphql.Schema, ctx context.Context) func(http.ResponseWr } res := GQLQuery{} json.Unmarshal(str, &res) - result := graphql.Do(graphql.Params{ + params := graphql.Params{ Schema: schema, Context: ctx, RequestString: res.Query, - }) + } + if res.OperationName != "" { + params.OperationName = res.OperationName + } + if len(res.Variables) > 0 { + log.Logf("gql", "VARIABLES: %+v", res.Variables) + params.VariableValues = res.Variables + } + result := graphql.Do(params) if len(result.Errors) > 0 { - log.Logf("gql", "wrong result, unexpected errors: %v", result.Errors) + extra_fields := map[string]interface{}{} + extra_fields["body"] = string(str) + extra_fields["headers"] = r.Header + log.Logm("gql", extra_fields, "wrong result, unexpected errors: %v", result.Errors) } json.NewEncoder(w).Encode(result) } @@ -395,6 +409,93 @@ func GQLTypeEventQueue() * graphql.Object { return gql_type_event_queue } +func GQLSignalFn(p graphql.ResolveParams, fn func(GraphSignal, graphql.ResolveParams)(interface{}, error))(interface{}, error) { + if signal, ok := p.Source.(GraphSignal); ok { + return fn(signal, p) + } + return nil, errors.New("Failed to cast source to event") +} + +func GQLSignalType(p graphql.ResolveParams) (interface{}, error) { + return GQLSignalFn(p, func(signal GraphSignal, p graphql.ResolveParams)(interface{}, error){ + return signal.Type(), nil + }) +} + +func GQLSignalDesc(p graphql.ResolveParams) (interface{}, error) { + return GQLSignalFn(p, func(signal GraphSignal, p graphql.ResolveParams)(interface{}, error){ + return signal.Description(), nil + }) +} + +func GQLSignalTime(p graphql.ResolveParams) (interface{}, error) { + return GQLSignalFn(p, func(signal GraphSignal, p graphql.ResolveParams)(interface{}, error){ + return signal.Time(), nil + }) +} + +func GQLSignalString(p graphql.ResolveParams) (interface{}, error) { + return GQLSignalFn(p, func(signal GraphSignal, p graphql.ResolveParams)(interface{}, error){ + return signal.String(), nil + }) +} + + +var gql_type_signal *graphql.Object = nil +func GQLTypeSignal() *graphql.Object { + if gql_type_signal == nil { + gql_type_signal = graphql.NewObject(graphql.ObjectConfig{ + Name: "SignalOut", + IsTypeOf: func(p graphql.IsTypeOfParams) bool { + _, ok := p.Value.(GraphSignal) + return ok + }, + Fields: graphql.Fields{}, + }) + + gql_type_signal.AddFieldConfig("Type", &graphql.Field{ + Type: graphql.String, + Resolve: GQLSignalType, + }) + gql_type_signal.AddFieldConfig("Description", &graphql.Field{ + Type: graphql.String, + Resolve: GQLSignalDesc, + }) + gql_type_signal.AddFieldConfig("Time", &graphql.Field{ + Type: graphql.DateTime, + Resolve: GQLSignalTime, + }) + gql_type_signal.AddFieldConfig("String", &graphql.Field{ + Type: graphql.String, + Resolve: GQLSignalString, + }) + } + return gql_type_signal +} + +var gql_type_signal_input *graphql.InputObject = nil +func GQLTypeSignalInput() *graphql.InputObject { + if gql_type_signal_input == nil { + gql_type_signal_input = graphql.NewInputObject(graphql.InputObjectConfig{ + Name: "SignalIn", + Fields: graphql.InputObjectConfigFieldMap{}, + }) + gql_type_signal_input.AddFieldConfig("Type", &graphql.InputObjectFieldConfig{ + Type: graphql.String, + }) + gql_type_signal_input.AddFieldConfig("Description", &graphql.InputObjectFieldConfig{ + Type: graphql.String, + DefaultValue: "", + }) + gql_type_signal_input.AddFieldConfig("Time", &graphql.InputObjectFieldConfig{ + Type: graphql.DateTime, + DefaultValue: time.Now(), + }) + } + return gql_type_signal_input +} + + type GQLServer struct { BaseResource abort chan error @@ -428,7 +529,7 @@ func (server * GQLServer) Handler() func(http.ResponseWriter, *http.Request) { valid_resources := map[reflect.Type]*graphql.Object{} valid_resources[reflect.TypeOf((*BaseResource)(nil))] = GQLTypeBaseResource() - gql_types := []graphql.Type{GQLTypeBaseEvent(), GQLTypeEventQueue()} + gql_types := []graphql.Type{GQLTypeBaseEvent(), GQLTypeEventQueue(), GQLTypeSignal(), GQLTypeSignalInput()} for go_t, gql_t := range(server.extended_types) { valid_events[go_t] = gql_t gql_types = append(gql_types, gql_t) @@ -436,6 +537,54 @@ func (server * GQLServer) Handler() func(http.ResponseWriter, *http.Request) { schemaConfig := graphql.SchemaConfig{ Types: gql_types, + Mutation: graphql.NewObject(graphql.ObjectConfig{ + Name: "Mutation", + Fields: graphql.Fields{ + "updateEvent": &graphql.Field{ + Type: GQLTypeSignal(), + Args: graphql.FieldConfigArgument{ + "id": &graphql.ArgumentConfig{ + Type: graphql.String, + }, + "signal": &graphql.ArgumentConfig{ + Type: GQLTypeSignalInput(), + }, + }, + Resolve: func(p graphql.ResolveParams) (interface{}, error) { + signal_map, ok := p.Args["signal"].(map[string]interface{}) + if ok == false { + return nil, errors.New(fmt.Sprintf("Failed to cast arg signal to GraphSignal: %+v", p.Args["signal"])) + } + signal := NewSignal(server, signal_map["Type"].(string)) + signal.description = signal_map["Description"].(string) + signal.time = signal_map["Time"].(time.Time) + + id , ok := p.Args["id"].(string) + if ok == false { + return nil, errors.New("Failed to cast arg id to string") + } + + owner := server.Owner() + if owner == nil { + return nil, errors.New("Cannot send update without owner") + } + + root_event, ok := owner.(Event) + if ok == false { + return nil, errors.New("Cannot send update to Event unless owned by an Event") + } + + node := FindChild(root_event, id) + if node == nil { + return nil, errors.New("Failed to find id in event tree from server") + } + + SendUpdate(node, signal) + return signal, nil + }, + }, + }, + }), Query: graphql.NewObject(graphql.ObjectConfig{ Name: "Query", Fields: graphql.Fields{ diff --git a/graph.go b/graph.go index 919648e..24d687f 100644 --- a/graph.go +++ b/graph.go @@ -13,6 +13,7 @@ import ( type Logger interface { Init() error Logf(component string, format string, items ... interface{}) + Logm(component string, fields map[string]interface{}, format string, items ... interface{}) } type DefaultLogger struct { @@ -25,6 +26,15 @@ var log DefaultLogger = DefaultLogger{loggers: map[string]zerolog.Logger{}} var all_components = []string{"update", "graph", "event", "resource", "manager", "test", "gql"} func (logger * DefaultLogger) Init(components []string) error { + logger.init_lock.Lock() + defer logger.init_lock.Unlock() + + if logger.init == true { + return nil + } + + logger.init = true + file, err := os.OpenFile("test.log", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0666) if err != nil { return err @@ -40,24 +50,28 @@ func (logger * DefaultLogger) Init(components []string) error { } writer := io.MultiWriter(file, os.Stdout) - for _, c := range([]string{"gql"}) { + for _, c := range(all_components) { if component_enabled(c) == true { logger.loggers[c] = zerolog.New(writer).With().Timestamp().Str("component", c).Logger() - } else { - panic(fmt.Sprintf("%s is not a component in DefaultLogger", c)) } } return nil } -func (logger * DefaultLogger) Logf(component string, format string, items ... interface{}) { - logger.init_lock.Lock() - if logger.init == false { - logger.Init(all_components) - logger.init = true +func (logger * DefaultLogger) Logm(component string, fields map[string]interface{}, format string, items ... interface{}) { + logger.Init([]string{"gql"}) + l, exists := logger.loggers[component] + if exists == true { + log := l.Log() + for key, value := range(fields) { + log = log.Str(key, fmt.Sprintf("%+v", value)) + } + log.Msg(fmt.Sprintf(format, items...)) } - logger.init_lock.Unlock() +} +func (logger * DefaultLogger) Logf(component string, format string, items ... interface{}) { + logger.Init([]string{"gql"}) l, exists := logger.loggers[component] if exists == true { l.Log().Msg(fmt.Sprintf(format, items...)) diff --git a/main.go b/main.go index cad4d9d..d94c846 100644 --- a/main.go +++ b/main.go @@ -241,7 +241,7 @@ func (client * FakeClient) process_update(update GraphSignal) { } func main() { - event_manager, arenas_div1, arenas_div2 := fake_data() + event_manager, _, _ := fake_data() sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) @@ -261,11 +261,12 @@ func main() { pprof.WriteHeapProfile(memfile) }() - // Fake arena clients + /*// Fake arena clients arena_1_client := NewFakeClient(arenas_div1[0]) arena_2_client := NewFakeClient(arenas_div1[1]) arena_3_client := NewFakeClient(arenas_div2[0]) arena_4_client := NewFakeClient(arenas_div2[1]) + */ go func() { for true { select { @@ -276,7 +277,7 @@ func main() { time.Sleep(time.Second * 5) pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) break - case update := <- arena_1_client.update: + /*case update := <- arena_1_client.update: arena_1_client.process_update(update) case update := <- arena_2_client.update: arena_2_client.process_update(update) @@ -291,7 +292,7 @@ func main() { arena_4_client.games_done == 12 { //signal := NewSignal(nil, "cancel") //signal.description = event_manager.root_event.ID() - //SendUpdate(event_manager.root_event, signal) + //SendUpdate(event_manager.root_event, signal)*/ } } }() @@ -302,10 +303,6 @@ func main() { log.Logf("test", "Error running event_manager: %s", err) } else { log.Logf("test", "Finished event_manager") - log.Logf("test", "Client 1 games: %d", arena_1_client.games_done) - log.Logf("test", "Client 2 games: %d", arena_2_client.games_done) - log.Logf("test", "Client 3 games: %d", arena_3_client.games_done) - log.Logf("test", "Client 4 games: %d", arena_4_client.games_done) } pprof.StopCPUProfile() }