-
Notifications
You must be signed in to change notification settings - Fork 3
/
analyze.go
137 lines (118 loc) · 3.47 KB
/
analyze.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package main
import (
"bytes"
"fmt"
"go/build"
"go/format"
"go/token"
"os"
"github.com/pkg/errors"
"github.com/rgraphql/rgraphql/analysis"
"github.com/rgraphql/rgraphql/schema"
"github.com/urfave/cli/v2"
"golang.org/x/tools/go/packages"
)
// .\rgraphql.exe analyze --schema ..\..\example\simple\schema.graphql --go-pkg "github.com/rgraphql/rgraphql/example/simple" --go-query-type RootResolver --go-output "../../example/simple/resolve/resolve_generated.go"
var analyzeArgs struct {
// SchemaPath is the path to the graphql schema file.
SchemaPath string
// PackagePath is the path to the Go package.
PackagePath string
// QueryType is the name of the root query type in Go.
QueryType string
// OutputPath is the path to the output go file.
OutputPath string
}
func init() {
Commands = append(Commands, &cli.Command{
Name: "analyze",
Usage: "builds an execution model from a schema and Go codebase",
Action: runAnalyze,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "schema",
Usage: "path to graphql schema file",
Destination: &analyzeArgs.SchemaPath,
},
&cli.StringFlag{
Name: "go-pkg",
Usage: "import path of the go package",
Destination: &analyzeArgs.PackagePath,
},
&cli.StringFlag{
Name: "go-query-type",
Usage: "the query type in Go",
Destination: &analyzeArgs.QueryType,
},
&cli.StringFlag{
Name: "go-output",
Usage: "path to go output file",
Destination: &analyzeArgs.OutputPath,
},
},
})
}
// runAnalyze runs the analyze subcommand
func runAnalyze(c *cli.Context) error {
queryTypeName := analyzeArgs.QueryType
if len(queryTypeName) == 0 {
return errors.New("specify a go-query-type to use")
}
schemaPath := analyzeArgs.SchemaPath
if schemaPath == "" {
return errors.New("schema path must be specified")
}
goPkgPath := analyzeArgs.PackagePath
if goPkgPath == "" {
return errors.New("go package path must be specified")
}
doc, err := os.ReadFile(schemaPath)
if err != nil {
return errors.Wrap(err, "unable to read schema")
}
scm, err := schema.Parse(string(doc))
if err != nil {
return errors.Wrap(err, "unable to parse schema")
}
// Build starting from the root query.
rootQuery := scm.Definitions.RootQuery
if rootQuery == nil {
return errors.New("schema.query must be defined in schema")
}
builderCtx := build.Default
builderCtx.BuildTags = append(builderCtx.BuildTags, "rgraphql_analyze")
fset := &token.FileSet{}
conf := &packages.Config{
BuildFlags: []string{"-tags", "rgraphql_analyze"},
Fset: fset,
Mode: packages.NeedTypes,
}
pkgs, err := packages.Load(conf, analyzeArgs.PackagePath)
if err != nil || len(pkgs) == 0 {
return errors.Wrap(err, "unable to load go program")
}
initPkg := pkgs[0]
pkgScope := initPkg.Types.Scope()
rootQueryGoObj := pkgScope.Lookup(queryTypeName)
if rootQueryGoObj == nil {
return errors.Errorf("couldn't find go type definition %s", queryTypeName)
}
rootQueryType := rootQueryGoObj.Type()
// Begin analyzing.
model, err := analysis.BuildModel(rootQuery, scm.Definitions, rootQueryType)
if err != nil {
return err
}
fmt.Printf("Generated model successfully.\n")
var outDat bytes.Buffer
outDat.WriteString("//+build !rgraphql_analyze\n\n")
outFile, err := model.GenerateResolverFile()
if err != nil {
return err
}
err = format.Node(&outDat, fset, outFile)
if err != nil {
return err
}
return os.WriteFile(analyzeArgs.OutputPath, outDat.Bytes(), 0644)
}