-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathgraphql.go
251 lines (237 loc) · 6.99 KB
/
graphql.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
package base
import (
"fmt"
"reflect"
"strings"
"github.com/graphql-go/graphql"
"github.com/graphql-go/graphql/language/ast"
"github.com/microsvs/base/pkg/rpc"
)
// 针对graphql Enum, Scalar{DateTime,...} 转化为Go支持的String, time.Time , *time.Time类型
// 主要用于rpc调用
/* example
var t time.Time= time.Now()
FixTypeFromGoToGraphql(t, graphql.DateTime).(string)//2006-01-02 15:04:05 -> "2006-01-02 15:04:05"
FixTypeFromGoToGraphql(fency.ParkZone, fency.GLPOIType) // ParkZone ---> "parkzone"
*/
func FixTypeFromGoToGraphql(v interface{}, argType graphql.Input) (result interface{}) {
var ret interface{}
switch val := argType.(type) {
case *graphql.List:
var partialRes = []interface{}{}
value := reflect.ValueOf(v)
if value.Kind() != reflect.Slice {
break
}
for i := 0; i < value.Len(); i++ {
partialRes = append(partialRes, FixTypeFromGoToGraphql(value.Index(i).Interface(), val.OfType))
}
return partialRes
case *graphql.Scalar:
ret = val.Serialize(v)
case *graphql.Enum:
return val.Serialize(v)
case *graphql.NonNull:
return FixTypeFromGoToGraphql(v, val.OfType)
case *graphql.Object:
var imap = map[string]interface{}{}
value := reflect.ValueOf(v)
for value.Kind() == reflect.Ptr {
value = value.Elem()
}
types := value.Type()
fmap := val.Fields()
for i := 0; i < types.NumField(); i++ {
key := types.Field(i).Tag.Get("json")
if _, ok := fmap[key]; !ok {
continue
}
imap[key] = FixTypeFromGoToGraphql(value.Field(i).Interface(), fmap[key].Type)
}
ret = imap
}
if _, ok := v.(string); ok {
return fmt.Sprintf("\"%s\"", ret)
}
return ret
}
func buildSlice(items []interface{}, typ graphql.Type) (values []interface{}) {
for _, item := range items {
if enum, eok := typ.(*graphql.Enum); eok {
//convert int value to string value
values = append(values, enum.Serialize(item))
} else if v, ok := item.(string); ok {
values = append(values, fmt.Sprintf("\"%s\"", v))
} else {
values = append(values, item)
}
}
return values
}
// 针对graphql内部能够处理的类型, 转化为golang内部的枚举类型、[]byte、time.Time、*time.Time等类型
// FixTypeFromGraphqlToGo与上面的FixTypeFromGoToGraphql作用相反
/* example
FixTypeFromGraphqlToGo("2006-01-02 15:04:05", graphql.DateTime) // return time.Time
FixTypeFromGraphqlToGo("parkzone", graphql.Enum) // return ParkZone
*/
func FixTypeFromGraphqlToGo(data interface{}, t graphql.Output) interface{} {
switch val := t.(type) {
case *graphql.List:
for idx, v := range data.([]interface{}) {
data.([]interface{})[idx] = FixTypeFromGraphqlToGo(v, val.OfType)
}
case *graphql.NonNull:
data = FixTypeFromGraphqlToGo(data, val.OfType)
case *graphql.Enum:
data = val.ParseValue(data)
case *graphql.Scalar:
data = val.ParseValue(data)
case *graphql.Object:
imap, _ := data.(map[string]interface{})
fmap := val.Fields()
for key, value := range imap {
if _, ok := fmap[key]; !ok {
continue
}
imap[key] = FixTypeFromGraphqlToGo(value, fmap[key].Type)
}
data = imap
default:
}
return data
}
// graphql request method = {query, mutation}
func GetSchemeMethodName(p graphql.ResolveParams) (method string) {
if len(p.Info.FieldASTs) > 0 && p.Info.FieldASTs[0].Name != nil {
return p.Info.FieldASTs[0].Name.Value
}
return
}
func getSchemeArgs(
p graphql.ResolveParams,
exArgs map[string]interface{},
excommon map[string]graphql.Input,
) (params string, output graphql.Output) {
if len(p.Info.FieldASTs) <= 0 || p.Info.FieldASTs[0].Name == nil {
return
}
var defArgs *graphql.FieldDefinition
// find input field definition
switch p.Info.Operation.GetOperation() {
case ast.OperationTypeQuery:
defArgs = p.Info.Schema.QueryType().Fields()[p.Info.FieldASTs[0].Name.Value]
case ast.OperationTypeMutation:
defArgs = p.Info.Schema.MutationType().Fields()[p.Info.FieldASTs[0].Name.Value]
}
output = defArgs.Type
// build args: a: a-value, b: b-value, ...
for i, arg := range defArgs.Args {
if _, ok := p.Args[arg.PrivateName]; ok {
if i == 0 {
params = fmt.Sprintf("%s:%v", arg.PrivateName,
FixTypeFromGoToGraphql(p.Args[arg.PrivateName], arg.Type))
} else {
params = fmt.Sprintf("%s, %s:%v", params, arg.PrivateName,
FixTypeFromGoToGraphql(p.Args[arg.PrivateName], defArgs.Args[i].Type))
}
}
}
// expand
for arg, value := range exArgs {
if len(params) > 0 {
params = fmt.Sprintf("%s, %s:%v", params, arg, FixTypeFromGoToGraphql(value, excommon[arg]))
} else {
params = fmt.Sprintf("%s:%v", arg, FixTypeFromGoToGraphql(value, excommon[arg]))
}
}
if len(params) > 0 {
params = fmt.Sprintf("(%s)", params)
}
return
}
// 获取返回参数列表
func getSchemaSelections(p graphql.ResolveParams) string {
var ss *ast.SelectionSet
if ss = p.Info.FieldASTs[0].GetSelectionSet(); ss == nil {
return ""
}
return reverseSelection(ss.Selections)
}
func reverseSelection(sset []ast.Selection) string {
var result []string
for _, item := range sset {
switch f := item.(type) {
case *ast.Field:
if f.Name != nil {
result = append(result, f.Name.Value)
}
}
temp := item.GetSelectionSet()
if temp != nil && len(temp.Selections) > 0 {
if str := reverseSelection(temp.Selections); len(str) > 0 {
result = append(result, str)
}
}
}
if len(result) > 0 {
return fmt.Sprintf("{%s}", strings.Join(result, " "))
}
return ""
}
func RedirectRequestEx(
p graphql.ResolveParams,
exArgs map[string]interface{},
excommon map[string]graphql.Input,
targetService rpc.FGService,
targetObj interface{}) (interface{}, error) {
var (
params string // 参数列表
selections string // 返回列表
method string //方法名
output graphql.Output
)
// 看看自己对schema的AST了解有多深
// get method name
method = GetSchemeMethodName(p)
// build args
params, output = getSchemeArgs(p, exArgs, excommon)
// build selections
selections = getSchemaSelections(p)
// composite schema
schema := fmt.Sprintf("%s{%s%s%s}",
p.Info.Operation.GetOperation(),
method,
params,
selections,
)
// rpc call service
data, err := rpc.CallService(p.Context, Service2Url(targetService), schema)
if err != nil {
return nil, err
}
// parse result
return FixTypeFromGraphqlToGo(data[method], output), nil
}
//RedirectRequest redirect request from one resolve function to another service
func RedirectRequest(p graphql.ResolveParams, target rpc.FGService) (interface{}, error) {
return RedirectRequestEx(p, nil, nil, target, nil)
}
// GLObjectFields获取graphql.Object的所有字段, 作为graphql Query/Mutation的返回值
func GLObjectFields(obj *graphql.Object) string {
keys := make([]string, 0, len(obj.Fields()))
for key, v := range obj.Fields() {
switch typ := v.Type.(type) {
case *graphql.Object:
key += GLObjectFields(typ)
}
keys = append(keys, key)
}
return "{" + strings.Join(keys, "\n") + "}"
}
// 隐藏某些不愿意暴露的字段
func HideGLFields(obj *graphql.Object, v ...string) *graphql.Object {
for _, item := range v {
delete(obj.Fields(), item)
}
return obj
}