diff --git a/gql-gen/graph/collectFields.go b/gql-gen/graph/collectFields.go index cf0e63b..4b342cc 100644 --- a/gql-gen/graph/collectFields.go +++ b/gql-gen/graph/collectFields.go @@ -26,15 +26,17 @@ func refinePaths(fields []string) []string { } refinedField := strings.Join(segments, ".") refined = append(refined, refinedField) + } + return refined } func SortFieldPaths(fields []string, rootType string) []string { - refined := refinePaths(fields) - sort.Slice(refined, func(i, j int) bool { - countTypeI, nestingLevelI := countTypeOccurrencesAndLevels(refined[i]) - countTypeJ, nestingLevelJ := countTypeOccurrencesAndLevels(refined[j]) + //refined := refinePaths(fields) + sort.Slice(fields, func(i, j int) bool { + countTypeI, nestingLevelI := countTypeOccurrencesAndLevels(fields[i]) + countTypeJ, nestingLevelJ := countTypeOccurrencesAndLevels(fields[j]) // Prioritize fewer "Type" segments if countTypeI != countTypeJ { @@ -45,9 +47,9 @@ func SortFieldPaths(fields []string, rootType string) []string { if nestingLevelI != nestingLevelJ { return nestingLevelI < nestingLevelJ } - return refined[i] < refined[j] + return fields[i] < fields[j] }) - return refined + return fields } // Helper function to count "Type" segments and nesting levels diff --git a/gql-gen/graph/gripFetch.go b/gql-gen/graph/gripFetch.go index 74a64aa..366854a 100644 --- a/gql-gen/graph/gripFetch.go +++ b/gql-gen/graph/gripFetch.go @@ -1,7 +1,9 @@ package graph import ( + "context" "fmt" + "strings" "github.com/bmeg/grip/gripql" ) @@ -10,13 +12,46 @@ type Resolver struct { GripDb gripql.Client } -func gripQuery(fields []string, sourceType string) *gripql.Query { - query := gripql.V().HasLabel(sourceType).As("_" + sourceType) +func (r *queryResolver) gripQuery(fields []string, sourceType string) []any { + sourceTypeLen := len(sourceType) + fmt.Println("SOURCE TYPE: ", sourceType[:sourceTypeLen-4]) + query := gripql.V().HasLabel(sourceType[:sourceTypeLen-4]).As(sourceType) for i, field := range fields { fields[i] = sourceType + "." + field } - typeGraph, traversal := constructTypeTraversal(fields) - fmt.Println("TYPE GRAPH: ", typeGraph) - fmt.Println("Traversal Path", traversal) - return query + traversal, typeGraph := constructTypeTraversal(fields) + + // Construct query from template traversal string + for _, value := range traversal { + if strings.HasPrefix(value, "OUTNULL_") { + splitStr := strings.Split(value, "_") + //fmt.Println("SPLIT STR: ", splitStr) + query = query.OutNull(splitStr[1] + "_" + splitStr[2][:len(splitStr[2])-4]).As(splitStr[2]) + } else if strings.HasPrefix(value, "SELECT_") { + //fmt.Println("VALUE SELECT : ", value[7:]) + query = query.Select(value[7:]) + } + } + + render := map[string]any{} + for typeKey, typeVals := range typeGraph { + for _, val := range typeVals { + render[typeKey+"_"+val] = "$" + typeKey + "." + val + } + } + query = query.Render(render) + fmt.Println("RENDER: ", render) + + fmt.Println("STATEMENTS: ", query.Statements) + result, err := r.GripDb.Traversal(context.Background(), &gripql.GraphQuery{Graph: "CALIPER", Query: query.Statements}) + if err != nil { + return nil + } + + out := []any{} + for r := range result { + out = append(out, r.GetRender().GetStructValue().AsMap()) + } + + return out } diff --git a/gql-gen/graph/resolver.go b/gql-gen/graph/resolver.go index 1f750c0..3a3c43a 100644 --- a/gql-gen/graph/resolver.go +++ b/gql-gen/graph/resolver.go @@ -4,11 +4,11 @@ package graph import ( "context" + "fmt" "github.com/bmeg/grip-graphql/gql-gen/generated" "github.com/bmeg/grip-graphql/gql-gen/model" - //"github.com/vektah/gqlparser/v2/ast" ) // Organization is the resolver for the organization field. @@ -65,7 +65,8 @@ func (r *queryResolver) Specimen(ctx context.Context, offset *int, first *int, f func (r *queryResolver) Observation(ctx context.Context, offset *int, first *int, filter *string, sort *string, accessibility *model.Accessibility, format *model.Format) ([]*model.ObservationType, error) { sourceType := "ObservationType" fields := GetQueryFields(ctx, sourceType) - _ = gripQuery(fields, sourceType) + res := r.gripQuery(fields, sourceType) + fmt.Println("RES: ", res) /*for _, field := range fields { fmt.Println("PATH: ", field) diff --git a/gql-gen/graph/traversal.go b/gql-gen/graph/traversal.go index 62317e8..88db1d9 100644 --- a/gql-gen/graph/traversal.go +++ b/gql-gen/graph/traversal.go @@ -6,13 +6,15 @@ import ( ) type Node struct { - value string - children map[string]*Node - fields []string - visited bool + value string + children map[string]*Node + fields []string + referenceName string + visited bool } func createNodeGraph(paths []string) *Node { + fmt.Println("ENTRY PATHS: ", paths) /* Construct a Node graph from a list of '.' delimited traversal paths */ graph := &Node{ children: make(map[string]*Node), @@ -21,33 +23,38 @@ func createNodeGraph(paths []string) *Node { for _, path := range paths { parts := strings.Split(path, ".") current := graph - + var referenceName string + var lstTypeIndex int for i, part := range parts { if strings.HasSuffix(part, "Type") && !strings.HasSuffix(part, "resourceType") { + lstTypeIndex = i if _, ok := current.children[part]; !ok { current.children[part] = &Node{ - value: part, - children: make(map[string]*Node), + value: part, + children: make(map[string]*Node), + referenceName: referenceName, } } current = current.children[part] } else { - current.fields = append(current.fields, strings.Join(parts[i:], ".")) - break + if i+1 == len(parts) { + current.fields = append(current.fields, strings.Join(parts[lstTypeIndex+1:], ".")) + } + referenceName = parts[i] } } } return graph } -func constructTypeTraversal(paths []string) (string, map[string][]string) { +func constructTypeTraversal(paths []string) ([]string, map[string][]string) { //Build traversal using a modifed Depth First Search algorithm typeFields := make(map[string][]string) returnPath, traversalPath := []string{}, []string{} stack := []*Node{} graph := createNodeGraph(paths) - + printNode(graph, 0) for node := range graph.children { if strings.HasSuffix(node, "Type") { stack = append(stack, graph.children[node]) @@ -61,7 +68,6 @@ func constructTypeTraversal(paths []string) (string, map[string][]string) { continue } current.visited = true - // Append fields on each node to typeFields if len(current.fields) > 0 { typeFields[current.value] = append(typeFields[current.value], current.fields...) @@ -76,7 +82,7 @@ func constructTypeTraversal(paths []string) (string, map[string][]string) { traversalPath = append(traversalPath, current.value) if len(traversalPath) > 1 { - returnPath = append(returnPath, "OUTNULL_"+current.value) + returnPath = append(returnPath, "OUTNULL_"+current.referenceName+"_"+current.value) } for _, child := range current.children { @@ -84,7 +90,8 @@ func constructTypeTraversal(paths []string) (string, map[string][]string) { } } - return strings.Join(returnPath, "."), typeFields + fmt.Println("RETURN PATH: ", returnPath, typeFields) + return returnPath, typeFields } func findParentNode(graph *Node, node *Node) string { diff --git a/gql-gen/server.go b/gql-gen/server.go index 9776a44..16ce999 100644 --- a/gql-gen/server.go +++ b/gql-gen/server.go @@ -20,36 +20,46 @@ import ( ) type Handler struct { - router *gin.Engine - config map[string]string + router *gin.Engine + config map[string]string + handler *handler.Server + client gripql.Client } func (gh *Handler) graphqlHandler(client gripql.Client) gin.HandlerFunc { - // NewExecutableSchema and Config are in the generated.go file - // Resolver is in the resolver.go file srv := handler.New(generated.NewExecutableSchema(generated.Config{Resolvers: &graph.Resolver{ - //GripClient: client, + GripDb: client, }})) + gh.handler = srv - srv.AddTransport(transport.Options{}) - srv.AddTransport(transport.GET{}) - srv.AddTransport(transport.POST{}) - srv.SetQueryCache(lru.New[*ast.QueryDocument](1000)) + gh.handler.AddTransport(transport.Options{}) + gh.handler.AddTransport(transport.GET{}) + gh.handler.AddTransport(transport.POST{}) + gh.handler.SetQueryCache(lru.New[*ast.QueryDocument](1000)) - srv.Use(extension.Introspection{}) - srv.Use(extension.AutomaticPersistedQuery{ + gh.handler.Use(extension.Introspection{}) + gh.handler.Use(extension.AutomaticPersistedQuery{ Cache: lru.New[string](100), }) return func(c *gin.Context) { - srv.ServeHTTP(c.Writer, c.Request) + gh.handler.ServeHTTP(c.Writer, c.Request) + //c.Next() } } func (gh *Handler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { + log.Infoln("HELLO INSIDE SERVE HTTP ", request) gh.router.ServeHTTP(writer, request) } +func playgroundHandler() gin.HandlerFunc { + h := playground.Handler("GraphQL Playground", "/graphql/query") + return func(c *gin.Context) { + h.ServeHTTP(c.Writer, c.Request) + } +} + func NewHTTPHandler(client gripql.Client, config map[string]string) (http.Handler, error) { // Setting up Gin r := gin.New() @@ -59,26 +69,28 @@ func NewHTTPHandler(client gripql.Client, config map[string]string) (http.Handle } log.ConfigureLogger(logConfig) r.Use(gin.Logger()) - r.NoRoute(func(c *gin.Context) { - log.WithFields(log.Fields{ - "graph": nil, - "status": "404", - }).Info(c.Request.URL.Path + " Not Found") - c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ - "status": "404", - "message": c.Request.URL.Path + " Not Found", - "data": nil, - }) + /*r.NoRoute(func(c *gin.Context) { + log.WithFields(log.Fields{ + "graph": nil, + "status": "404", + }).Info(c.Request.URL.Path + " Not Found") + c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{ + "status": "404", + "message": c.Request.URL.Path + " Not Found", + "data": nil, }) + })*/ r.Use(gin.Recovery()) r.RemoveExtraSlash = true + h := &Handler{ router: r, config: config, + client: client, } r.POST("/query", h.graphqlHandler(client)) - r.GET("/", gin.WrapH(playground.Handler("GraphQL", "/query"))) //r.GET("/", playgroundHandler()) - r.Run() + r.GET("/", playgroundHandler()) return h, nil + }