blob: 73e5bdf77e6a75536e1f56422dbd39e1ec14f7fd [file] [log] [blame]
giolekva0defd602020-04-27 16:48:16 +04001package tests
2
3import (
giolekva26a8b5f2020-05-01 20:01:13 +04004 "fmt"
5 "io"
6 "os"
7 "strings"
giolekva0defd602020-04-27 16:48:16 +04008 "testing"
9
10 "github.com/vektah/gqlparser"
11 "github.com/vektah/gqlparser/ast"
giolekva26a8b5f2020-05-01 20:01:13 +040012
13 "github.com/bradleyjkemp/memviz"
giolekva0defd602020-04-27 16:48:16 +040014)
15
16var gqlSchema = `#######################\n# Input Schema\n#######################\n\ntype Image {\n\tid: ID!\n\tobjectPath: String! @search(by: [exact])\n\tsegments(filter: ImageSegmentFilter, order: ImageSegmentOrder, first: Int, offset: Int): [ImageSegment] @hasInverse(field: sourceImage)\n}\n\ntype ImageSegment {\n\tid: ID!\n\tupperLeftX: Float!\n\tupperLeftY: Float!\n\tlowerRightX: Float!\n\tlowerRightY: Float!\n\tsourceImage(filter: ImageFilter): Image! @hasInverse(field: segments)\n\tobjectPath: String\n}\n\n#######################\n# Extended Definitions\n#######################\n\nscalar DateTime\n\nenum DgraphIndex {\n\tint\n\tfloat\n\tbool\n\thash\n\texact\n\tterm\n\tfulltext\n\ttrigram\n\tregexp\n\tyear\n\tmonth\n\tday\n\thour\n}\n\ndirective @hasInverse(field: String!) on FIELD_DEFINITION\ndirective @search(by: [DgraphIndex!]) on FIELD_DEFINITION\ndirective @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION\ndirective @id on FIELD_DEFINITION\ndirective @secret(field: String!, pred: String) on OBJECT | INTERFACE\n\ninput IntFilter {\n\teq: Int\n\tle: Int\n\tlt: Int\n\tge: Int\n\tgt: Int\n}\n\ninput FloatFilter {\n\teq: Float\n\tle: Float\n\tlt: Float\n\tge: Float\n\tgt: Float\n}\n\ninput DateTimeFilter {\n\teq: DateTime\n\tle: DateTime\n\tlt: DateTime\n\tge: DateTime\n\tgt: DateTime\n}\n\ninput StringTermFilter {\n\tallofterms: String\n\tanyofterms: String\n}\n\ninput StringRegExpFilter {\n\tregexp: String\n}\n\ninput StringFullTextFilter {\n\talloftext: String\n\tanyoftext: String\n}\n\ninput StringExactFilter {\n\teq: String\n\tle: String\n\tlt: String\n\tge: String\n\tgt: String\n}\n\ninput StringHashFilter {\n\teq: String\n}\n\n#######################\n# Generated Types\n#######################\n\ntype AddImagePayload {\n\timage(filter: ImageFilter, order: ImageOrder, first: Int, offset: Int): [Image]\n\tnumUids: Int\n}\n\ntype AddImageSegmentPayload {\n\timagesegment(filter: ImageSegmentFilter, order: ImageSegmentOrder, first: Int, offset: Int): [ImageSegment]\n\tnumUids: Int\n}\n\ntype DeleteImagePayload {\n\tmsg: String\n\tnumUids: Int\n}\n\ntype DeleteImageSegmentPayload {\n\tmsg: String\n\tnumUids: Int\n}\n\ntype UpdateImagePayload {\n\timage(filter: ImageFilter, order: ImageOrder, first: Int, offset: Int): [Image]\n\tnumUids: Int\n}\n\ntype UpdateImageSegmentPayload {\n\timagesegment(filter: ImageSegmentFilter, order: ImageSegmentOrder, first: Int, offset: Int): [ImageSegment]\n\tnumUids: Int\n}\n\n#######################\n# Generated Enums\n#######################\n\nenum ImageOrderable {\n\tobjectPath\n}\n\nenum ImageSegmentOrderable {\n\tupperLeftX\n\tupperLeftY\n\tlowerRightX\n\tlowerRightY\n\tobjectPath\n}\n\n#######################\n# Generated Inputs\n#######################\n\ninput AddImageInput {\n\tobjectPath: String!\n\tsegments: [ImageSegmentRef]\n}\n\ninput AddImageSegmentInput {\n\tupperLeftX: Float!\n\tupperLeftY: Float!\n\tlowerRightX: Float!\n\tlowerRightY: Float!\n\tsourceImage: ImageRef!\n\tobjectPath: String\n}\n\ninput ImageFilter {\n\tid: [ID!]\n\tobjectPath: StringExactFilter\n\tand: ImageFilter\n\tor: ImageFilter\n\tnot: ImageFilter\n}\n\ninput ImageOrder {\n\tasc: ImageOrderable\n\tdesc: ImageOrderable\n\tthen: ImageOrder\n}\n\ninput ImagePatch {\n\tobjectPath: String\n\tsegments: [ImageSegmentRef]\n}\n\ninput ImageRef {\n\tid: ID\n\tobjectPath: String\n\tsegments: [ImageSegmentRef]\n}\n\ninput ImageSegmentFilter {\n\tid: [ID!]\n\tnot: ImageSegmentFilter\n}\n\ninput ImageSegmentOrder {\n\tasc: ImageSegmentOrderable\n\tdesc: ImageSegmentOrderable\n\tthen: ImageSegmentOrder\n}\n\ninput ImageSegmentPatch {\n\tupperLeftX: Float\n\tupperLeftY: Float\n\tlowerRightX: Float\n\tlowerRightY: Float\n\tsourceImage: ImageRef\n\tobjectPath: String\n}\n\ninput ImageSegmentRef {\n\tid: ID\n\tupperLeftX: Float\n\tupperLeftY: Float\n\tlowerRightX: Float\n\tlowerRightY: Float\n\tsourceImage: ImageRef\n\tobjectPath: String\n}\n\ninput UpdateImageInput {\n\tfilter: ImageFilter!\n\tset: ImagePatch\n\tremove: ImagePatch\n}\n\ninput UpdateImageSegmentInput {\n\tfilter: ImageSegmentFilter!\n\tset: ImageSegmentPatch\n\tremove: ImageSegmentPatch\n}\n\n#######################\n# Generated Query\n#######################\n\ntype Query {\n\tgetImage(id: ID!): Image\n\tqueryImage(filter: ImageFilter, order: ImageOrder, first: Int, offset: Int): [Image]\n\tgetImageSegment(id: ID!): ImageSegment\n\tqueryImageSegment(filter: ImageSegmentFilter, order: ImageSegmentOrder, first: Int, offset: Int): [ImageSegment]\n}\n\n#######################\n# Generated Mutations\n#######################\n\ntype Mutation {\n\taddImage(input: [AddImageInput!]!): AddImagePayload\n\tupdateImage(input: UpdateImageInput!): UpdateImagePayload\n\tdeleteImage(filter: ImageFilter!): DeleteImagePayload\n\taddImageSegment(input: [AddImageSegmentInput!]!): AddImageSegmentPayload\n\tupdateImageSegment(input: UpdateImageSegmentInput!): UpdateImageSegmentPayload\n\tdeleteImageSegment(filter: ImageSegmentFilter!): DeleteImageSegmentPayload\n}\n`
17
giolekva26a8b5f2020-05-01 20:01:13 +040018var simpleSchema = `
19type ABC {
20 id: ID!
21 x: Int
22}
23
24input ABCInput {
25 x: Int
26}
27
28extend type ABC {
29 event: ID
30}
31`
32
33var enumSchema = `
34enum EventState {
35 NEW
36 PROCESSING
37 DONE
38}
39
40input Event {
41 id: ID
42 state: EventState!
43}
44
45type E {
46 foo: String
47}
48
49type Mutation {
50 addEvent(input: Event!): E
51}
52`
53
54// TODO(giolekva): will be safer to use directive instead
55func shouldIgnoreDefinition(d *ast.Definition) bool {
56 return d.Kind != ast.Object ||
57 d.Name == "Query" ||
58 d.Name == "Mutation" ||
59 strings.HasPrefix(d.Name, "__") ||
60 strings.HasSuffix(d.Name, "Payload")
61}
62
63func checkErr(err error) {
64 if err != nil {
65 panic(err)
66 }
67}
68
69func TestParseSchema(t *testing.T) {
70 schema := getSchema(gqlSchema)
71 var extended strings.Builder
72 _, err := io.WriteString(&extended, gqlSchema)
73 checkErr(err)
74 for _, n := range schema.Types {
75 if shouldIgnoreDefinition(n) {
76 continue
77 }
78 _, err := fmt.Fprintf(&extended, "\nextend type %s {\n event: ID\n}", n.Name)
79 checkErr(err)
80 }
81 fmt.Print(&extended)
82 f, _ := os.Create("schema.dot")
83 memviz.Map(f, schema)
84 f.Close()
85}
86
giolekva0defd602020-04-27 16:48:16 +040087func TestParseQuery(t *testing.T) {
giolekva26a8b5f2020-05-01 20:01:13 +040088 schema := getSchema(enumSchema)
89 query, err := gqlparser.LoadQuery(schema, `mutation {
90addEvent(input: {
91 state: NEW
92}) {
93 foo
giolekva0defd602020-04-27 16:48:16 +040094}
95}`)
96 if err != nil {
97 panic(err)
98 }
giolekva26a8b5f2020-05-01 20:01:13 +040099 f, _ := os.Create("query.dot")
100 memviz.Map(f, query)
101 f.Close()
giolekva0defd602020-04-27 16:48:16 +0400102}
103
giolekva26a8b5f2020-05-01 20:01:13 +0400104func getSchema(schema string) *ast.Schema {
105 s, err := gqlparser.LoadSchema(&ast.Source{
106 Input: strings.ReplaceAll(strings.ReplaceAll(schema, "\\n", "\n"), "\\t", "\t")})
giolekva0defd602020-04-27 16:48:16 +0400107 if err != nil {
108 panic(err)
109 }
giolekva26a8b5f2020-05-01 20:01:13 +0400110 return s
giolekva0defd602020-04-27 16:48:16 +0400111}