-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdirectives.go
162 lines (142 loc) · 4.64 KB
/
directives.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package yarql
import (
"errors"
"reflect"
)
// DirectiveLocation defines the location a directive can be used in
type DirectiveLocation uint8
const (
// DirectiveLocationField can be called from a field
DirectiveLocationField DirectiveLocation = iota
// DirectiveLocationFragment can be called from a fragment
DirectiveLocationFragment
// DirectiveLocationFragmentInline can be called from a inline fragment
DirectiveLocationFragmentInline
)
// String returns the DirectiveLocation as a string
func (l DirectiveLocation) String() string {
switch l {
case DirectiveLocationField:
return "<DirectiveLocationField>"
case DirectiveLocationFragment:
return "<DirectiveLocationFragment>"
case DirectiveLocationFragmentInline:
return "<DirectiveLocationFragmentInline>"
default:
return "<UNKNOWN DIRECTIVE LOCATION>"
}
}
// ToQlDirectiveLocation returns the matching graphql location
func (l DirectiveLocation) ToQlDirectiveLocation() __DirectiveLocation {
switch l {
case DirectiveLocationField:
return directiveLocationField
case DirectiveLocationFragment:
return directiveLocationFragmentSpread
case DirectiveLocationFragmentInline:
return directiveLocationInlineFragment
default:
return directiveLocationField
}
}
// Directive is what defines a directive
type Directive struct {
// Required
Name string
Where []DirectiveLocation
// Should be of type: func(args like any other method) DirectiveModifier
Method interface{}
methodReflection reflect.Value
parsedMethod *objMethod
// Not required
Description string
}
// TODO
// type ModifyOnWriteContent func(bytes []byte) []byte
// DirectiveModifier defines modifications to the response
// Nothing is this struct is required and will be ignored if not set
type DirectiveModifier struct {
// Skip field/(inline)fragment
Skip bool
// TODO make this
// ModifyOnWriteContent allows you to modify field JSON response data before it's written to the result
// Note that there is no checking for validation here it's up to you to return valid json
// ModifyOnWriteContent ModifyOnWriteContent
}
// RegisterDirective registers a new directive
func (s *Schema) RegisterDirective(directive Directive) error {
if s.parsed {
return errors.New("(*yarql.Schema).RegisterDirective() cannot be ran after (*yarql.Schema).Parse()")
}
err := checkDirective(&directive)
if err != nil {
return err
}
ptrToDirective := &directive
for _, location := range directive.Where {
directivesForLocation, ok := s.definedDirectives[location]
if !ok {
directivesForLocation = []*Directive{}
} else {
// Check for already defined directives with the same name
for _, alreadyDefinedDirective := range directivesForLocation {
if directive.Name == alreadyDefinedDirective.Name {
return errors.New("you cannot have duplicated directive names in " + location.String() + " with name " + directive.Name)
}
}
}
directivesForLocation = append(directivesForLocation, ptrToDirective)
s.definedDirectives[location] = directivesForLocation
}
return nil
}
func checkDirective(directive *Directive) error {
if len(directive.Name) == 0 {
return errors.New("cannot register directive with empty name")
}
for _, char := range directive.Name {
if char >= '0' && char <= '9' || char >= 'A' && char <= 'Z' || char >= 'a' && char <= 'z' || char == '_' {
continue
}
return errors.New(string(char) + " in " + directive.Name + " is not allowed as directive name")
}
if directive.Where == nil {
return errors.New("where must be defined")
}
if directive.Method == nil {
return errors.New("method must be defined")
}
if directive.Method == nil {
return errors.New("method must be defined")
}
directive.methodReflection = reflect.ValueOf(directive.Method)
if directive.methodReflection.IsNil() {
return errors.New("method must be defined")
}
if directive.methodReflection.Kind() != reflect.Func {
return errors.New("method is not a function")
}
methodType := directive.methodReflection.Type()
switch methodType.NumOut() {
case 0:
return errors.New("method should return DirectiveModifier")
case 1:
// OK
default:
return errors.New("method should only return DirectiveModifier")
}
outType := methodType.Out(0)
directiveModifierType := reflect.TypeOf(DirectiveModifier{})
if outType.Name() != directiveModifierType.Name() || outType.PkgPath() != directiveModifierType.PkgPath() {
return errors.New("method should return DirectiveModifier")
}
directive.parsedMethod = &objMethod{
isTypeMethod: false,
goType: methodType,
ins: []baseInput{},
inFields: map[string]referToInput{},
checkedIns: false,
}
// Inputs checked in (s *Schema).Parse(..)
return nil
}