blob: 6725cbe0bb5e0b0090eff90fc985a0a7c77d4630 [file] [log] [blame]
giolekvac76b21b2020-04-18 19:28:43 +04001package schema
2
3import (
4 "bytes"
giolekvafb52e0d2020-04-23 22:52:13 +04005 "errors"
giolekvac76b21b2020-04-18 19:28:43 +04006 "fmt"
7 "io/ioutil"
8 "net/http"
9 "strings"
10
11 "github.com/golang/glog"
12 "github.com/itaysk/regogo"
giolekvafb52e0d2020-04-23 22:52:13 +040013 "github.com/vektah/gqlparser"
giolekvac76b21b2020-04-18 19:28:43 +040014 "github.com/vektah/gqlparser/ast"
giolekvac76b21b2020-04-18 19:28:43 +040015)
16
17const jsonContentType = "application/json"
giolekvafb52e0d2020-04-23 22:52:13 +040018const textContentType = "text/plain"
giolekvac76b21b2020-04-18 19:28:43 +040019
giolekvaa374a582020-04-30 11:43:13 +040020// TODO(giolekva): escape
21const getSchemaQuery = `{ getGQLSchema() { schema generatedSchema } }`
22const setSchemaQuery = `mutation { updateGQLSchema(input: { set: { schema: "%s" } }) { gqlSchema { schema generatedSchema } } }`
giolekvafb52e0d2020-04-23 22:52:13 +040023const runQuery = `{ "query": "%s" }`
giolekvac76b21b2020-04-18 19:28:43 +040024
giolekvafb52e0d2020-04-23 22:52:13 +040025type DgraphClient struct {
giolekvaa374a582020-04-30 11:43:13 +040026 gqlAddress string
27 schemaAddress string
28 userSchema string
29 generatedSchema string
30 schema *ast.Schema
giolekvac76b21b2020-04-18 19:28:43 +040031}
32
giolekvafb52e0d2020-04-23 22:52:13 +040033func NewDgraphClient(gqlAddress, schemaAddress string) (GraphQLClient, error) {
34 ret := &DgraphClient{
giolekvaa374a582020-04-30 11:43:13 +040035 gqlAddress: gqlAddress,
36 schemaAddress: schemaAddress,
37 userSchema: "",
38 generatedSchema: ""}
giolekvac76b21b2020-04-18 19:28:43 +040039 if err := ret.fetchSchema(); err != nil {
40 return nil, err
41 }
42 return ret, nil
43}
44
giolekvafb52e0d2020-04-23 22:52:13 +040045func (s *DgraphClient) Schema() *ast.Schema {
giolekvac76b21b2020-04-18 19:28:43 +040046 return s.schema
47}
48
giolekvafb52e0d2020-04-23 22:52:13 +040049func (s *DgraphClient) AddSchema(gqlSchema string) error {
giolekvaa374a582020-04-30 11:43:13 +040050 return s.SetSchema(s.userSchema + gqlSchema)
giolekvac76b21b2020-04-18 19:28:43 +040051}
52
giolekvafb52e0d2020-04-23 22:52:13 +040053func (s *DgraphClient) SetSchema(gqlSchema string) error {
giolekvaa374a582020-04-30 11:43:13 +040054 glog.Info("Setting GraphQL schema")
giolekvac76b21b2020-04-18 19:28:43 +040055 glog.Info(gqlSchema)
giolekvaa374a582020-04-30 11:43:13 +040056 resp, err := s.runQuery(
57 fmt.Sprintf(setSchemaQuery, sanitizeSchema(gqlSchema)),
58 s.schemaAddress)
giolekvac76b21b2020-04-18 19:28:43 +040059 if err != nil {
60 return err
61 }
giolekvaa374a582020-04-30 11:43:13 +040062 return s.updateSchema(resp)
giolekvac76b21b2020-04-18 19:28:43 +040063}
64
giolekvafb52e0d2020-04-23 22:52:13 +040065func (s *DgraphClient) fetchSchema() error {
giolekvaa374a582020-04-30 11:43:13 +040066 glog.Infof("Getting GraphQL schema")
67 resp, err := s.runQuery(getSchemaQuery, s.schemaAddress)
giolekvac76b21b2020-04-18 19:28:43 +040068 if err != nil {
69 return err
70 }
giolekvaa374a582020-04-30 11:43:13 +040071 return s.updateSchema(resp)
72}
73
74func (s *DgraphClient) updateSchema(resp string) error {
75 userSchema, err := regogo.Get(resp, "input.getGQLSchema.schema")
giolekvac76b21b2020-04-18 19:28:43 +040076 if err != nil {
77 return err
78 }
giolekvaa374a582020-04-30 11:43:13 +040079 generatedSchema, err := regogo.Get(resp, "input.getGQLSchema.generatedSchema")
giolekvac76b21b2020-04-18 19:28:43 +040080 if err != nil {
81 return err
82 }
giolekvaa374a582020-04-30 11:43:13 +040083 schema, gqlErr := gqlparser.LoadSchema(&ast.Source{Input: generatedSchema.String()})
giolekvac76b21b2020-04-18 19:28:43 +040084 if gqlErr != nil {
85 return gqlErr
86 }
giolekvaa374a582020-04-30 11:43:13 +040087 s.userSchema = userSchema.String()
88 s.generatedSchema = generatedSchema.String()
giolekvac76b21b2020-04-18 19:28:43 +040089 s.schema = schema
90 return nil
91}
giolekvafb52e0d2020-04-23 22:52:13 +040092
93func (s *DgraphClient) RunQuery(query string) (string, error) {
94 _, gqlErr := gqlparser.LoadQuery(s.Schema(), query)
95 if gqlErr != nil {
96 return "", errors.New(gqlErr.Error())
97 }
giolekvaa374a582020-04-30 11:43:13 +040098 return s.runQuery(query, s.gqlAddress)
99}
100
101func (s *DgraphClient) runQuery(query string, onAddr string) (string, error) {
giolekvafb52e0d2020-04-23 22:52:13 +0400102 glog.Infof("Running GraphQL query: %s", query)
103 queryJson := fmt.Sprintf(runQuery, sanitizeQuery(query))
104 resp, err := http.Post(
giolekvaa374a582020-04-30 11:43:13 +0400105 onAddr,
giolekvafb52e0d2020-04-23 22:52:13 +0400106 jsonContentType,
107 bytes.NewReader([]byte(queryJson)))
108 if err != nil {
109 return "", err
110 }
111 respBody, err := ioutil.ReadAll(resp.Body)
112 if err != nil {
113 return "", err
114 }
115 respStr := string(respBody)
116 glog.Infof("Result: %s", string(respStr))
117 // errStr, err := regogo.Get(respStr, "input.errors")
118 // if err == nil {
119 // return "", errors.New(errStr.JSON())
120 // }
121 data, err := regogo.Get(respStr, "input.data")
122 if err != nil {
123 return "", err
124 }
125 return data.JSON(), nil
126}
127
128func sanitizeSchema(schema string) string {
129 return strings.ReplaceAll(
130 strings.ReplaceAll(schema, "\n", " "), "\t", " ")
131}
132
133func sanitizeQuery(query string) string {
134 return strings.ReplaceAll(query, "\"", "\\\"")
135}