-
Notifications
You must be signed in to change notification settings - Fork 0
/
responseGenerator.go
223 lines (177 loc) · 6.93 KB
/
responseGenerator.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
package hypeql
import (
"encoding/json"
"fmt"
"reflect"
"slices"
"strings"
)
// Processes a request's brances recursively
func (a responseGenerator) recursiveGenerateResponse(r []interface{}, ctx map[string]interface{}, path []string, ds interface{}, deep uint64) (interface{}, error) {
// Map for returning
var ret map[string]interface{} = map[string]interface{}{}
branchRefVal := reflect.ValueOf(ds)
// Receiving the "Resolve" method
if resolveMethod := branchRefVal.MethodByName("Resolve"); resolveMethod.IsValid() {
neededFields := []string{} // List of fields tags that needed for this recursive cycle
// Filling neededFields list
for _, i := range r {
kind := reflect.TypeOf(i).Kind()
if st, ok := i.(string); ok {
neededFields = append(neededFields, st)
} else if kind == reflect.Slice {
q := reflect.ValueOf(i)
if q.Len() > 1 {
if st, ok := q.Index(0).Interface().(string); ok {
neededFields = append(neededFields, st)
}
}
}
}
// Calling the "Resolve" method that can change context values (you can use context values in another resolver functions)
// Use the "Resolve" method to connect with a database for example
// func (a ResponseStruct) Resolve(contextMap *map[string]interface{}, neededFields []string) error {...}
res := resolveMethod.Call([]reflect.Value{
reflect.ValueOf(&ctx),
reflect.ValueOf(neededFields),
})
if len(res) == 1 {
if err, ok := res[0].Interface().(error); ok && err != nil {
return []interface{}{}, err
}
}
}
// List of already processed fields (in the case when one fields mentioned many times in the request body)
checked := []string{}
// Traversing and receiving values of needed fields by listed tags
a:
for _, i := range r {
if key, ok := i.(string); ok { // i's value is a basic (single) data (i = field's tag name)
// Skipping if field already processed
if slices.Contains(checked, key) {
continue
}
checked = append(checked, key)
newPath := strings.Join(append(path, key), ".")
// Finding field by tag
for i := 0; i < branchRefVal.NumField(); i++ {
fieldType := branchRefVal.Type().Field(i)
if fieldType.Tag.Get("json") == key && fieldType.Type.Kind() != reflect.Func {
// If field found
// Getting function middleware name
if funcName := fieldType.Tag.Get("fun"); funcName != "" {
if q := branchRefVal.MethodByName(funcName); q.IsValid() {
// Calling middleware function
// Middleware function can replace value of field and use context values (from argument)
newVal := q.Call([]reflect.Value{
reflect.ValueOf(&ctx),
})
if len(newVal) > 0 && !newVal[0].IsZero() {
ret[key] = newVal[0].Interface()
continue a
}
}
}
// Use field's value if middleware function is not found
ret[key] = branchRefVal.Field(i).Interface()
// Finish field finding process (for loop)
continue a
}
}
// If field does not found
return []interface{}{}, fmt.Errorf(newPath + " not found in the struct")
} else if sliceVal, ok := i.([]interface{}); ok { // i's value is list of objects (branches) (i example: [field's name, object's needed fields, arguments])
arguments := map[string]interface{}{}
if len(sliceVal) != 2 && len(sliceVal) != 3 {
return []interface{}{}, fmt.Errorf(strings.Join(path, ".") + " length of list must have two or three elements")
}
// Slice has arguments values in third element
if len(sliceVal) == 3 {
if newArguments, ok := sliceVal[2].(map[string]interface{}); ok {
arguments = newArguments
}
}
// Getting field's tag name
tagName, ok := sliceVal[0].(string)
if !ok {
return []interface{}{}, fmt.Errorf(strings.Join(path, ".") + " first argument of list must have string type")
}
// Skipping if field already processed
if slices.Contains(checked, tagName) {
continue
}
checked = append(checked, tagName)
newPath := strings.Join(append(path, tagName), ".")
// Needed fields of object from field
neededFields, ok := sliceVal[1].([]interface{})
if !ok {
return []interface{}{}, fmt.Errorf(newPath + " second argument of list must have slice type")
}
// Finding field by tag
for i := 0; i < branchRefVal.NumField(); i++ {
sf := branchRefVal.Type().Field(i)
if sf.Tag.Get("json") == tagName && sf.Type.Kind() == reflect.Slice {
// When field found
if a.Config.MaxDeepRecursion != 0 && deep+1 > a.Config.MaxDeepRecursion {
return []interface{}{}, fmt.Errorf(newPath + ": max deep recursion reached")
}
l := branchRefVal.Field(i)
// Getting middleware function's name
if funcName := sf.Tag.Get("fun"); funcName != "" {
if q := branchRefVal.MethodByName(funcName); q.IsValid() {
// Calling middleware function
// Middleware function can replace value of field and use context values (from argument)
newVal := q.Call([]reflect.Value{
reflect.ValueOf(&ctx),
reflect.ValueOf(arguments), // Arguments of objects list from body
})
if len(newVal) > 0 && !newVal[0].IsZero() && newVal[0].Type().Kind() == reflect.Slice {
l = newVal[0]
}
}
}
objects := []interface{}{}
// Parsing objects in a new recursion iteration (new branch)
for i := 0; i < l.Len(); i++ {
p := l.Index(i).Interface()
if reflect.TypeOf(p).Kind() != reflect.Struct {
continue
}
i, err := a.recursiveGenerateResponse(neededFields, ctx, append(path, tagName), p, deep+1)
if err != nil {
return []interface{}{}, err
}
objects = append(objects, i)
}
// Writing parsed objects
ret[tagName] = objects
continue a
}
}
// Field not found
return []interface{}{}, fmt.Errorf(newPath + " field not found in the struct")
} else {
// Unknown data type
return []interface{}{}, fmt.Errorf(strings.Join(path, ".") + " incorrect data type. The String or Slice types only allowed")
}
}
return ret, nil
}
// Processes a request body and returns a result (the first is JSON string)
func (a responseGenerator) Generate(requestBody []interface{}, dataStruct interface{}, initContext map[string]interface{}) (string, error) {
// dataStruct argument must be Struct
if reflect.TypeOf(dataStruct).Kind() != reflect.Struct {
return "", fmt.Errorf("dataStruct argument must be instance of struct")
}
// Start recursion to process all fields in the request
i, err := a.recursiveGenerateResponse(requestBody, initContext, []string{}, dataStruct, 1)
if err != nil {
return "", err
}
// Converting result to JSON string and return
q, err := json.Marshal(i)
if err != nil {
return "", fmt.Errorf("JSON converting error")
}
return string(q), nil
}