-
Notifications
You must be signed in to change notification settings - Fork 3
/
eggql.go
135 lines (117 loc) · 4.45 KB
/
eggql.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
package eggql
// eggql.go provides the gql type for generating a GraphQL HTTP handler (and/or schema)
// Using eggql.New() and calling the methods of the returned type is more flexible
// but more involved than simply calling eggql.MustRun (see run.go).
// For example, you can obtain the generated schema as a string using the GetSchema method.
// Call eggql.New() to obtain an instance of the type, then call its methods
// (SetEnums, Add, etc), if needed, then call the GetHandler() method to generate
// the HTTP handler or call GetSchema() to obtain the schema as a string.
// You can also set options such as websocket timeouts and ping frequency for subscriptions.
import (
"net/http"
"time"
"github.com/andrewwphillips/eggql/internal/handler"
"github.com/andrewwphillips/eggql/internal/schema"
)
type (
// gql is an internal type, so it is not possible to modify the struct fields
// outside the eggql package, but you can obtain one by calling eggql.New()
// then call its public methods.
gql struct {
enums map[string][]string
qms [][3]interface{} // each slice element represents a schema (with a root query, mutation and subscription)
options []func(*handler.Handler)
}
)
// New creates a new instance with from zero to 3 parameters representing the
// query, mutation, and subscription types of a schema. Further schemas can
// be added (to enable schema stitching) by using the Add method.
// If no parameters are provided then the returned instance will be empty and
// will be of no use unless you subsequently call Add() at least once.
func New(q ...interface{}) gql {
g := gql{}
if len(q) > 0 { // leave g.qms slice empty if no params were supplied
g.Add(q...)
}
return g
}
// Add allows adding of another query, mutation and/or subscription.
// Up to 3 parameters can be given, but they must be structs and passed in that
// order (query, mutation, subscription) but any may be nil (eg use nil for the
// query if you only want to add a mutation).
func (g *gql) Add(q ...interface{}) {
var schemaQMS [3]interface{}
for i := 0; i < 3; i++ {
if len(q) > i {
schemaQMS[i] = q[i]
}
}
g.qms = append(g.qms, schemaQMS)
}
// Len returns the number of query/mutation/subscriptions sets that have been added.
func (g *gql) Len() int {
return len(g.qms)
}
// SetEnums adds or replaces all enums used in generating the schema
func (g *gql) SetEnums(enums map[string][]string) {
g.enums = enums
}
// AddEnum adds one enum to the map of enums used in generating the schema.
// You can call AddEnum repeatedly to add multiple enums, instead of using SetEnums.
func (g *gql) AddEnum(name string, values []string) {
if g.enums == nil {
g.enums = make(map[string][]string)
}
g.enums[name] = values
}
// GetSchema builds and returns the GraphQL schema
func (g *gql) GetSchema() (string, error) {
var schemaString string
for _, schemaQMS := range g.qms {
s, err := schema.Build(g.enums, schemaQMS[:]...)
if err != nil {
return "", err
}
schemaString += s // should we do more than concatenate the schemas?
}
return schemaString, nil
}
// GetHandler uses the previously added Query, Enums, options, etc to build the
// schema and return the HTTP handler
func (g *gql) GetHandler() (http.Handler, error) {
var schemaStrings []string
var schemaQMS [3][]interface{}
for _, qms := range g.qms {
s, err := schema.Build(g.enums, qms[:]...)
if err != nil {
return nil, err
}
if len(schemaStrings) == 0 {
schemaStrings = append(schemaStrings, s)
} else {
schemaStrings = append(schemaStrings, "extend "+s)
}
if qms[0] != nil {
schemaQMS[0] = append(schemaQMS[0], qms[0])
}
if qms[1] != nil {
schemaQMS[1] = append(schemaQMS[1], qms[1])
}
if qms[2] != nil {
schemaQMS[2] = append(schemaQMS[2], qms[2])
}
}
return handler.New(schemaStrings, g.enums, schemaQMS, g.options...), nil
}
// SetInitialTimeout sets the initial websocket (subscription) timeout. This is only used if manually setting
// up a handler before calling the GetHandler method. It's the same as creating the option passed to MustRun()
// using the InitialTimeout() function - see InitialTimeout() function in options.go.
func (g *gql) SetInitialTimeout(timeout time.Duration) {
g.options = append(g.options, handler.InitialTimeout(timeout))
}
func (g *gql) SetPingFrequency(freq time.Duration) {
g.options = append(g.options, handler.PingFrequency(freq))
}
func (g *gql) SetPongTimeout(timeout time.Duration) {
g.options = append(g.options, handler.PongTimeout(timeout))
}