Skip to content
This repository has been archived by the owner on Sep 15, 2024. It is now read-only.

Commit

Permalink
a little bit of reorg/cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
bketelsen authored and rawkode committed Sep 7, 2021
1 parent 46f8dbe commit 83eb7eb
Show file tree
Hide file tree
Showing 5 changed files with 423 additions and 391 deletions.
224 changes: 224 additions & 0 deletions content/graphql.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
package content

import (
"encoding/json"
"fmt"
"net/http"
"sort"

"github.com/cueblox/blox/internal/cuedb"
"github.com/graphql-go/graphql"
"github.com/graphql-go/handler"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
)

func (s *Service) prepGraphQL() error {
if !s.built {
err := s.build()
if err != nil {
return err
}

}
dag := s.engine.GetDataSetsDAG()
nodes, _ := dag.GetDescendants("root")

// GraphQL API
graphqlObjects := map[string]cuedb.GraphQlObjectGlue{}
graphqlFields := graphql.Fields{}
keys := make([]string, 0, len(nodes))
for k := range nodes {
keys = append(keys, k)
}
sort.Strings(keys)
vertexComplete := map[string]bool{}

// iterate through all the root nodes
for _, k := range keys {
node := nodes[k]

chNode, _, err := dag.DescendantsWalker(k)
cobra.CheckErr(err)

// first iterate through all the children
// so dependencies are registered
for nd := range chNode {
n, err := dag.GetVertex(nd)
cobra.CheckErr(err)
if dg, ok := n.(*cuedb.DagNode); ok {
_, ok := vertexComplete[dg.Name]
if !ok {
err := s.translateNode(n, graphqlObjects, graphqlFields)
if err != nil {
return err
}
vertexComplete[dg.Name] = true
}
}
}
// now process the parent node
_, ok := vertexComplete[node.(*cuedb.DagNode).Name]
if !ok {
err := s.translateNode(node, graphqlObjects, graphqlFields)
if err != nil {
return err
}
vertexComplete[node.(*cuedb.DagNode).Name] = true
}

}

queryType := graphql.NewObject(
graphql.ObjectConfig{
Name: "Query",
Fields: graphqlFields,
})

schema, err := graphql.NewSchema(
graphql.SchemaConfig{
Query: queryType,
},
)
if err != nil {
return err
}
s.schema = &schema
return nil
}

func (s *Service) translateNode(node interface{}, graphqlObjects map[string]cuedb.GraphQlObjectGlue, graphqlFields map[string]*graphql.Field) error {
dataSet, _ := s.engine.GetDataSet(node.(*cuedb.DagNode).Name)

var objectFields graphql.Fields
objectFields, err := cuedb.CueValueToGraphQlField(graphqlObjects, dataSet.GetSchemaCue())
if err != nil {
cobra.CheckErr(err)
}

// Inject ID field into each object
objectFields["id"] = &graphql.Field{
Type: &graphql.NonNull{
OfType: graphql.String,
},
}

objType := graphql.NewObject(
graphql.ObjectConfig{
Name: dataSet.GetExternalName(),
Fields: objectFields,
},
)

resolver := func(p graphql.ResolveParams) (interface{}, error) {
dataSetName := p.Info.ReturnType.Name()

id, ok := p.Args["id"].(string)
if ok {
data := s.engine.GetAllData(fmt.Sprintf("#%s", dataSetName))

records := make(map[string]interface{})
if err = data.Decode(&records); err != nil {
return nil, err
}

for recordID, record := range records {
if string(recordID) == id {
return record, nil
}
}
}
return nil, nil
}

graphqlObjects[dataSet.GetExternalName()] = cuedb.GraphQlObjectGlue{
Object: objType,
Resolver: resolver,
Engine: s.engine,
}

graphqlFields[dataSet.GetExternalName()] = &graphql.Field{
Name: dataSet.GetExternalName(),
Type: objType,
Args: graphql.FieldConfigArgument{
"id": &graphql.ArgumentConfig{
Type: graphql.String,
},
},
Resolve: resolver,
}

graphqlFields[fmt.Sprintf("all%vs", dataSet.GetExternalName())] = &graphql.Field{
Type: graphql.NewList(objType),
Resolve: func(p graphql.ResolveParams) (interface{}, error) {
dataSetName := p.Info.ReturnType.Name()

data := s.engine.GetAllData(fmt.Sprintf("#%s", dataSetName))

records := make(map[string]interface{})
if err = data.Decode(&records); err != nil {
return nil, err
}

values := []interface{}{}
for _, value := range records {
values = append(values, value)
}

return values, nil
},
}
return nil
}

// GQLHandlerFunc returns a stand alone graphql handler for use
// in netlify/aws/azure serverless scenarios
func (s *Service) GQLHandlerFunc() (http.HandlerFunc, error) {
if s.schema == nil {
err := s.prepGraphQL()
if err != nil {
return nil, err
}
}

hf := func(w http.ResponseWriter, r *http.Request) {
result := s.executeQuery(r.URL.Query().Get("query"))
err := json.NewEncoder(w).Encode(result)
if err != nil {
pterm.Warning.Printf("failed to encode: %v", err)
}
}
return hf, nil
}

// GQLPlaygroundHandler returns a stand alone graphql playground handler for use
// in netlify/aws/azure serverless scenarios
func (s *Service) GQLPlaygroundHandler() (http.Handler, error) {
if s.schema == nil {
err := s.prepGraphQL()
if err != nil {
return nil, err
}
}

h := handler.New(&handler.Config{
Schema: s.schema,
Pretty: true,
GraphiQL: false,
Playground: true,
})
return h, nil
}

func (s *Service) executeQuery(query string) *graphql.Result {
result := graphql.Do(graphql.Params{
Schema: *s.schema,
RequestString: query,
})

if len(result.Errors) > 0 {
pterm.Error.Printf("errors: %v\n", result.Errors)
}

return result
}
91 changes: 91 additions & 0 deletions content/plugins.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package content

import (
"log"
"os"
"os/exec"

"github.com/cueblox/blox/plugins"
"github.com/cueblox/blox/plugins/shared"
"github.com/hashicorp/go-hclog"
"github.com/hashicorp/go-plugin"
"github.com/pterm/pterm"
)

func (s *Service) runPrePlugins() error {

pre, err := s.Cfg.GetList("prebuild")
if err != nil {
return err
}
iter, err := pre.List()
if err != nil {
return err
}
for iter.Next() {
val := iter.Value()

//nolint
name, err := val.FieldByName("name", false)
if err != nil {
return err
}
n, err := name.Value.String()
if err != nil {
return err
}
//nolint
exec, err := val.FieldByName("executable", false)
if err != nil {
return err
}
e, err := exec.Value.String()
if err != nil {
return err
}
prePluginMap[n] = &plugins.PrebuildPlugin{}
pterm.Info.Println("Calling Plugin", n, e)
err = s.callPlugin(n, e)
if err != nil {
return err
}

}
return nil

}
func (s *Service) callPlugin(name, executable string) error {
pterm.Info.Println("calling the plugin")
// Create an hclog.Logger
logger := hclog.New(&hclog.LoggerOptions{
Name: "plugin",
Output: os.Stdout,
Level: hclog.Info,
})

// We're a host! Start by launching the plugin process.
client := plugin.NewClient(&plugin.ClientConfig{
HandshakeConfig: shared.PrebuildHandshakeConfig,
Plugins: prePluginMap,
Cmd: exec.Command(executable),
Logger: logger,
})
defer client.Kill()

// Connect via RPC
rpcClient, err := client.Client()
if err != nil {
log.Fatal(err)
}

// Request the plugin
raw, err := rpcClient.Dispense(name)
if err != nil {
log.Fatal(err)
}

// We should have a Greeter now! This feels like a normal interface
// implementation but is in fact over an RPC connection.
imgs := raw.(plugins.Prebuild)
return imgs.Process(s.rawConfig)
}
55 changes: 55 additions & 0 deletions content/render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package content

import (
"os"
"path"

"github.com/pterm/pterm"
)

func (s *Service) RenderJSON() ([]byte, error) {
if !s.built {
err := s.build()
if err != nil {
return nil, err
}
}
pterm.Debug.Println("Building output data blox")
output, err := s.engine.GetOutput()
if err != nil {
return nil, err
}

pterm.Debug.Println("Rendering data blox to JSON")
return output.MarshalJSON()
}

func (s *Service) RenderAndSave() error {
if !s.built {
err := s.build()
if err != nil {
return err
}
}

bb, err := s.RenderJSON()
if err != nil {
return err
}
buildDir, err := s.Cfg.GetString("build_dir")
if err != nil {
return err
}
err = os.MkdirAll(buildDir, 0o755)
if err != nil {
return err
}
filename := "data.json"
filePath := path.Join(buildDir, filename)
err = os.WriteFile(filePath, bb, 0o755)
if err != nil {
return err
}
pterm.Success.Printf("Data blox written to '%s'\n", filePath)
return nil
}
Loading

0 comments on commit 83eb7eb

Please sign in to comment.