agoo 2.5.7 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of agoo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +38 -0
- data/ext/agoo/agoo.c +11 -2
- data/ext/agoo/bind.c +15 -20
- data/ext/agoo/con.c +32 -25
- data/ext/agoo/debug.c +225 -162
- data/ext/agoo/debug.h +31 -51
- data/ext/agoo/doc.c +278 -5
- data/ext/agoo/doc.h +6 -1
- data/ext/agoo/err.c +1 -0
- data/ext/agoo/err.h +1 -0
- data/ext/agoo/error_stream.c +3 -6
- data/ext/agoo/gqlcobj.c +12 -0
- data/ext/agoo/gqlcobj.h +25 -0
- data/ext/agoo/gqleval.c +520 -0
- data/ext/agoo/gqleval.h +49 -0
- data/ext/agoo/gqlintro.c +1237 -97
- data/ext/agoo/gqlintro.h +8 -0
- data/ext/agoo/gqljson.c +460 -0
- data/ext/agoo/gqljson.h +15 -0
- data/ext/agoo/gqlvalue.c +679 -136
- data/ext/agoo/gqlvalue.h +29 -7
- data/ext/agoo/graphql.c +841 -362
- data/ext/agoo/graphql.h +180 -90
- data/ext/agoo/hook.c +8 -16
- data/ext/agoo/http.c +3 -4
- data/ext/agoo/log.c +22 -25
- data/ext/agoo/log.h +1 -0
- data/ext/agoo/page.c +24 -40
- data/ext/agoo/pub.c +23 -21
- data/ext/agoo/queue.c +2 -4
- data/ext/agoo/ready.c +9 -9
- data/ext/agoo/req.c +80 -5
- data/ext/agoo/req.h +2 -0
- data/ext/agoo/res.c +1 -3
- data/ext/agoo/rgraphql.c +753 -0
- data/ext/agoo/rresponse.c +9 -15
- data/ext/agoo/rserver.c +18 -17
- data/ext/agoo/sdl.c +1264 -120
- data/ext/agoo/sdl.h +8 -1
- data/ext/agoo/sectime.c +136 -0
- data/ext/agoo/sectime.h +19 -0
- data/ext/agoo/server.c +1 -3
- data/ext/agoo/subject.c +2 -4
- data/ext/agoo/text.c +124 -18
- data/ext/agoo/text.h +5 -1
- data/ext/agoo/upgraded.c +2 -4
- data/lib/agoo/version.rb +1 -1
- data/test/base_handler_test.rb +43 -40
- data/test/bind_test.rb +49 -48
- data/test/graphql_test.rb +1019 -0
- data/test/hijack_test.rb +1 -1
- data/test/rack_handler_test.rb +40 -34
- data/test/static_test.rb +33 -32
- metadata +17 -6
data/ext/agoo/err.h
CHANGED
data/ext/agoo/error_stream.c
CHANGED
@@ -16,17 +16,14 @@ static void
|
|
16
16
|
es_free(void *ptr) {
|
17
17
|
ErrorStream es = (ErrorStream)ptr;
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
free(es->text); // allocated with malloc
|
22
|
-
xfree(ptr);
|
19
|
+
AGOO_FREE(es->text); // allocated with malloc
|
20
|
+
AGOO_FREE(ptr);
|
23
21
|
}
|
24
22
|
|
25
23
|
VALUE
|
26
24
|
error_stream_new() {
|
27
|
-
ErrorStream es =
|
25
|
+
ErrorStream es = (ErrorStream)AGOO_MALLOC(sizeof(struct _errorStream));
|
28
26
|
|
29
|
-
DEBUG_ALLOC(mem_err_stream, es)
|
30
27
|
es->text = agoo_text_allocate(1024);
|
31
28
|
|
32
29
|
return Data_Wrap_Struct(es_class, NULL, es_free, es);
|
data/ext/agoo/gqlcobj.c
ADDED
data/ext/agoo/gqlcobj.h
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#ifndef AGOO_GQLCOBJ_H
|
4
|
+
#define AGOO_GQLCOBJ_H
|
5
|
+
|
6
|
+
#include "gqleval.h"
|
7
|
+
|
8
|
+
struct _gqlCobj;
|
9
|
+
|
10
|
+
typedef struct _gqlCmethod {
|
11
|
+
const char *key;
|
12
|
+
int (*func)(agooErr err, struct _gqlDoc *doc, struct _gqlCobj *obj, struct _gqlField *field, struct _gqlSel *sel, struct _gqlValue *result, int depth);
|
13
|
+
} *gqlCmethod;
|
14
|
+
|
15
|
+
typedef struct _gqlCclass {
|
16
|
+
const char *name;
|
17
|
+
gqlCmethod methods; // TBD use a hash instead of a simple array
|
18
|
+
} *gqlCclass;
|
19
|
+
|
20
|
+
typedef struct _gqlCobj {
|
21
|
+
gqlCclass clas;
|
22
|
+
void *ptr;
|
23
|
+
} *gqlCobj;
|
24
|
+
|
25
|
+
#endif // AGOO_GQLCOBJ_H
|
data/ext/agoo/gqleval.c
ADDED
@@ -0,0 +1,520 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#include <stdio.h>
|
4
|
+
#include <string.h>
|
5
|
+
|
6
|
+
#include "debug.h"
|
7
|
+
#include "gqleval.h"
|
8
|
+
#include "gqlintro.h"
|
9
|
+
#include "gqljson.h"
|
10
|
+
#include "gqlvalue.h"
|
11
|
+
#include "graphql.h"
|
12
|
+
#include "http.h"
|
13
|
+
#include "log.h"
|
14
|
+
#include "req.h"
|
15
|
+
#include "res.h"
|
16
|
+
#include "sdl.h"
|
17
|
+
#include "sectime.h"
|
18
|
+
#include "text.h"
|
19
|
+
|
20
|
+
#define MAX_RESOLVE_ARGS 16
|
21
|
+
|
22
|
+
gqlRef gql_root = NULL;
|
23
|
+
gqlResolveFunc gql_resolve_func = NULL;
|
24
|
+
gqlTypeFunc gql_type_func = NULL;
|
25
|
+
gqlRef (*gql_root_op)(const char *op) = NULL;
|
26
|
+
|
27
|
+
static const char graphql_content_type[] = "application/graphql";
|
28
|
+
static const char indent_str[] = "indent";
|
29
|
+
static const char json_content_type[] = "application/json";
|
30
|
+
static const char operation_name_str[] = "operationName";
|
31
|
+
static const char query_str[] = "query";
|
32
|
+
static const char variables_str[] = "variables";
|
33
|
+
|
34
|
+
gqlValue (*gql_doc_eval_func)(agooErr err, gqlDoc doc) = NULL;
|
35
|
+
|
36
|
+
// TBD errors should have message, location, and path
|
37
|
+
static void
|
38
|
+
err_resp(agooRes res, agooErr err, int status) {
|
39
|
+
char buf[256];
|
40
|
+
int cnt;
|
41
|
+
int64_t now = agoo_now_nano();
|
42
|
+
time_t t = (time_t)(now / 1000000000LL);
|
43
|
+
long long frac = (long long)now % 1000000000LL;
|
44
|
+
struct _agooTime at;
|
45
|
+
const char *code = agoo_err_str(err->code);
|
46
|
+
int clen = strlen(code);
|
47
|
+
|
48
|
+
agoo_sectime(t, &at);
|
49
|
+
cnt = snprintf(buf, sizeof(buf),
|
50
|
+
"HTTP/1.1 %d %s\r\nContent-Type: application/json\r\nContent-Length: %ld\r\n\r\n{\"errors\":[{\"message\":\"%s\",\"code\":\"%s\",\"timestamp\":\"%04d-%02d-%02dT%02d:%02d:%02d.%09lldZ\"}]}",
|
51
|
+
status, agoo_http_code_message(status), strlen(err->msg) + 27 + 45 + clen + 10, err->msg,
|
52
|
+
code,
|
53
|
+
at.year, at.mon, at.day, at.hour, at.min, at.sec, frac);
|
54
|
+
|
55
|
+
agoo_res_set_message(res, agoo_text_create(buf, cnt));
|
56
|
+
}
|
57
|
+
|
58
|
+
static void
|
59
|
+
value_resp(agooRes res, gqlValue result, int status, int indent) {
|
60
|
+
char buf[256];
|
61
|
+
struct _agooErr err = AGOO_ERR_INIT;
|
62
|
+
int cnt;
|
63
|
+
agooText text = agoo_text_allocate(4094);
|
64
|
+
gqlValue msg = gql_object_create(&err);
|
65
|
+
|
66
|
+
if (NULL == msg) {
|
67
|
+
agoo_err_set(&err, AGOO_ERR_MEMORY, "Out of memory.");
|
68
|
+
err_resp(res, &err, 500);
|
69
|
+
gql_value_destroy(result);
|
70
|
+
return;
|
71
|
+
}
|
72
|
+
if (AGOO_ERR_OK != gql_object_set(&err, msg, "data", result)) {
|
73
|
+
err_resp(res, &err, 500);
|
74
|
+
gql_value_destroy(result);
|
75
|
+
return;
|
76
|
+
}
|
77
|
+
text = gql_value_json(text, msg, indent, 0);
|
78
|
+
gql_value_destroy(msg); // also destroys result
|
79
|
+
|
80
|
+
cnt = snprintf(buf, sizeof(buf), "HTTP/1.1 %d %s\r\nContent-Type: application/json\r\nContent-Length: %ld\r\n\r\n",
|
81
|
+
status, agoo_http_code_message(status), text->len);
|
82
|
+
text = agoo_text_prepend(text, buf, cnt);
|
83
|
+
agoo_res_set_message(res, text);
|
84
|
+
}
|
85
|
+
|
86
|
+
gqlValue
|
87
|
+
doc_var_value(gqlDoc doc, const char *key) {
|
88
|
+
gqlVar var;
|
89
|
+
|
90
|
+
// look in doc->vars and doc->op->vars
|
91
|
+
if (NULL != doc->op) {
|
92
|
+
for (var = doc->op->vars; NULL != var; var = var->next) {
|
93
|
+
if (0 == strcmp(key, var->name)) {
|
94
|
+
return var->value;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
98
|
+
for (var = doc->vars; NULL != var; var = var->next) {
|
99
|
+
if (0 == strcmp(key, var->name)) {
|
100
|
+
return var->value;
|
101
|
+
}
|
102
|
+
}
|
103
|
+
return NULL;
|
104
|
+
}
|
105
|
+
|
106
|
+
static bool
|
107
|
+
frag_include(gqlDoc doc, gqlFrag frag, gqlRef ref) {
|
108
|
+
gqlDirUse dir;
|
109
|
+
|
110
|
+
if (NULL != frag->on) {
|
111
|
+
if (frag->on != doc->funcs.type(ref)) {
|
112
|
+
return false;
|
113
|
+
}
|
114
|
+
}
|
115
|
+
for (dir = frag->dir; NULL != dir; dir = dir->next) {
|
116
|
+
if (NULL != dir->dir && 0 == strcmp("skip", dir->dir->name)) {
|
117
|
+
gqlLink arg;
|
118
|
+
|
119
|
+
for (arg = dir->args; NULL != arg; arg = arg->next) {
|
120
|
+
if (0 == strcmp("if", arg->key) && NULL != arg->value) {
|
121
|
+
if (&gql_bool_type == arg->value->type && arg->value->b) {
|
122
|
+
return false;
|
123
|
+
} else {
|
124
|
+
const char *key = NULL;
|
125
|
+
|
126
|
+
if (&gql_var_type == arg->value->type) {
|
127
|
+
if (arg->value->str.alloced) {
|
128
|
+
key = arg->value->str.ptr;
|
129
|
+
} else {
|
130
|
+
key = arg->value->str.a;
|
131
|
+
}
|
132
|
+
}
|
133
|
+
if (NULL != key) {
|
134
|
+
gqlValue var = doc_var_value(doc, key);
|
135
|
+
|
136
|
+
if (NULL != var && &gql_bool_type == var->type && var->b) {
|
137
|
+
return false;
|
138
|
+
}
|
139
|
+
}
|
140
|
+
}
|
141
|
+
}
|
142
|
+
}
|
143
|
+
}
|
144
|
+
if (NULL != dir->dir && 0 == strcmp("include", dir->dir->name)) {
|
145
|
+
gqlLink arg;
|
146
|
+
|
147
|
+
for (arg = dir->args; NULL != arg; arg = arg->next) {
|
148
|
+
if (0 == strcmp("if", arg->key) && NULL != arg->value) {
|
149
|
+
if (&gql_bool_type == arg->value->type && !arg->value->b) {
|
150
|
+
return false;
|
151
|
+
} else {
|
152
|
+
const char *key = NULL;
|
153
|
+
|
154
|
+
if (&gql_var_type == arg->value->type) {
|
155
|
+
if (arg->value->str.alloced) {
|
156
|
+
key = arg->value->str.ptr;
|
157
|
+
} else {
|
158
|
+
key = arg->value->str.a;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
if (NULL != key) {
|
162
|
+
gqlValue var = doc_var_value(doc, key);
|
163
|
+
|
164
|
+
if (NULL != var && &gql_bool_type == var->type && !var->b) {
|
165
|
+
return false;
|
166
|
+
}
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|
170
|
+
}
|
171
|
+
}
|
172
|
+
}
|
173
|
+
return true;
|
174
|
+
}
|
175
|
+
|
176
|
+
int
|
177
|
+
gql_set_typename(agooErr err, gqlType type, const char *key, gqlValue result) {
|
178
|
+
gqlValue child;
|
179
|
+
|
180
|
+
if (NULL == type) {
|
181
|
+
return agoo_err_set(err, AGOO_ERR_EVAL, "Internal error, failed to determine the __typename.");
|
182
|
+
}
|
183
|
+
if (NULL == (child = gql_string_create(err, type->name, -1)) ||
|
184
|
+
AGOO_ERR_OK != gql_object_set(err, result, key, child)) {
|
185
|
+
return err->code;
|
186
|
+
}
|
187
|
+
return AGOO_ERR_OK;
|
188
|
+
}
|
189
|
+
|
190
|
+
int
|
191
|
+
gql_eval_sels(agooErr err, gqlDoc doc, gqlRef ref, gqlField field, gqlSel sels, gqlValue result, int depth) {
|
192
|
+
gqlSel sel;
|
193
|
+
gqlField sf = NULL;
|
194
|
+
|
195
|
+
// TBD if depth over max then return an error
|
196
|
+
|
197
|
+
for (sel = sels; NULL != sel; sel = sel->next) {
|
198
|
+
if (NULL != field) {
|
199
|
+
if (NULL == sel->name) {
|
200
|
+
sf = field;
|
201
|
+
} else {
|
202
|
+
sf = gql_type_get_field(field->type, sel->name);
|
203
|
+
}
|
204
|
+
} else {
|
205
|
+
sf = NULL;
|
206
|
+
}
|
207
|
+
if (NULL != sel->inline_frag) {
|
208
|
+
if (frag_include(doc, sel->inline_frag, ref)) {
|
209
|
+
if (AGOO_ERR_OK != gql_eval_sels(err, doc, ref, sf, sel->inline_frag->sels, result, depth)) {
|
210
|
+
return err->code;
|
211
|
+
}
|
212
|
+
}
|
213
|
+
} else if (NULL != sel->frag) {
|
214
|
+
gqlFrag frag;
|
215
|
+
|
216
|
+
for (frag = doc->frags; NULL != frag; frag = frag->next) {
|
217
|
+
if (NULL != frag->name && 0 == strcmp(frag->name, sel->frag)) {
|
218
|
+
if (frag_include(doc, frag, ref)) {
|
219
|
+
if (AGOO_ERR_OK != gql_eval_sels(err, doc, ref, sf, frag->sels, result, depth)) {
|
220
|
+
return err->code;
|
221
|
+
}
|
222
|
+
}
|
223
|
+
}
|
224
|
+
}
|
225
|
+
} else {
|
226
|
+
if (AGOO_ERR_OK != doc->funcs.resolve(err, doc, ref, sf, sel, result, depth)) {
|
227
|
+
return err->code;
|
228
|
+
}
|
229
|
+
}
|
230
|
+
}
|
231
|
+
return AGOO_ERR_OK;
|
232
|
+
}
|
233
|
+
|
234
|
+
gqlValue
|
235
|
+
gql_doc_eval(agooErr err, gqlDoc doc) {
|
236
|
+
gqlValue result;
|
237
|
+
|
238
|
+
if (NULL == doc->op) {
|
239
|
+
agoo_err_set(err, AGOO_ERR_EVAL, "Failed to identify operation in doc.");
|
240
|
+
return NULL;
|
241
|
+
}
|
242
|
+
if (NULL != (result = gql_object_create(err))) {
|
243
|
+
const char *key;
|
244
|
+
gqlType type;
|
245
|
+
gqlField field = NULL;
|
246
|
+
struct _gqlSel sel;
|
247
|
+
|
248
|
+
memset(&sel, 0, sizeof(sel));
|
249
|
+
doc->funcs.resolve = gql_resolve_func;
|
250
|
+
doc->funcs.type = gql_type_func;
|
251
|
+
|
252
|
+
switch (doc->op->kind) {
|
253
|
+
case GQL_QUERY:
|
254
|
+
key = "query";
|
255
|
+
break;
|
256
|
+
case GQL_MUTATION:
|
257
|
+
key = "mutation";
|
258
|
+
break;
|
259
|
+
case GQL_SUBSCRIPTION:
|
260
|
+
key = "subscription";
|
261
|
+
break;
|
262
|
+
default:
|
263
|
+
agoo_err_set(err, AGOO_ERR_EVAL, "Not a valid operation on the root object.");
|
264
|
+
gql_value_destroy(result);
|
265
|
+
return NULL;
|
266
|
+
break;
|
267
|
+
}
|
268
|
+
if (NULL != (type = gql_type_get("schema"))) {
|
269
|
+
field = gql_type_get_field(type, key);
|
270
|
+
}
|
271
|
+
if (NULL == field) {
|
272
|
+
agoo_err_set(err, AGOO_ERR_EVAL, "GraphQL not initialized.");
|
273
|
+
gql_value_destroy(result);
|
274
|
+
return NULL;
|
275
|
+
}
|
276
|
+
sel.name = key;
|
277
|
+
sel.type = type;
|
278
|
+
sel.dir = doc->op->dir;
|
279
|
+
sel.sels = doc->op->sels;
|
280
|
+
if (AGOO_ERR_OK != doc->funcs.resolve(err, doc, gql_root, field, &sel, result, 0)) {
|
281
|
+
gql_value_destroy(result);
|
282
|
+
return NULL;
|
283
|
+
}
|
284
|
+
}
|
285
|
+
return result;
|
286
|
+
}
|
287
|
+
|
288
|
+
static gqlVar
|
289
|
+
parse_query_vars(agooErr err, const char *var_json, int vlen) {
|
290
|
+
gqlValue vlist = NULL;
|
291
|
+
gqlLink link;
|
292
|
+
gqlVar vars = NULL;
|
293
|
+
|
294
|
+
vlen = agoo_req_query_decode((char*)var_json, vlen);
|
295
|
+
if (NULL == (vlist = gql_json_parse(err, var_json, vlen))) {
|
296
|
+
goto DONE;
|
297
|
+
}
|
298
|
+
if (GQL_SCALAR_OBJECT != vlist->type->scalar_kind) {
|
299
|
+
agoo_err_set(err, AGOO_ERR_EVAL, "expected variables to be an object.");
|
300
|
+
goto DONE;
|
301
|
+
}
|
302
|
+
for (link = vlist->members; NULL != link; link = link->next) {
|
303
|
+
gqlVar v = gql_op_var_create(err, link->key, link->value->type, link->value);
|
304
|
+
|
305
|
+
link->value = NULL;
|
306
|
+
if (NULL == v) {
|
307
|
+
goto DONE;
|
308
|
+
}
|
309
|
+
v->next = vars;
|
310
|
+
vars = v;
|
311
|
+
}
|
312
|
+
DONE:
|
313
|
+
if (NULL != vlist) {
|
314
|
+
gql_value_destroy(vlist);
|
315
|
+
}
|
316
|
+
return vars;
|
317
|
+
}
|
318
|
+
|
319
|
+
static void
|
320
|
+
set_doc_op(gqlDoc doc, const char *op_name, int oplen) {
|
321
|
+
if (NULL != op_name) {
|
322
|
+
gqlOp op;
|
323
|
+
|
324
|
+
// This null terminates the string.
|
325
|
+
agoo_req_query_decode((char*)op_name, oplen);
|
326
|
+
for (op = doc->ops; NULL != op; op = op->next) {
|
327
|
+
if (NULL != op->name && 0 == strcmp(op_name, op->name)) {
|
328
|
+
doc->op = op;
|
329
|
+
break;
|
330
|
+
}
|
331
|
+
}
|
332
|
+
} else {
|
333
|
+
doc->op = doc->ops;
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
void
|
338
|
+
gql_eval_get_hook(agooReq req) {
|
339
|
+
struct _agooErr err = AGOO_ERR_INIT;
|
340
|
+
const char *gq; // graphql query
|
341
|
+
const char *op_name = NULL;
|
342
|
+
const char *var_json = NULL;
|
343
|
+
int qlen;
|
344
|
+
int oplen;
|
345
|
+
int vlen;
|
346
|
+
int indent = 0;
|
347
|
+
gqlDoc doc;
|
348
|
+
gqlValue result;
|
349
|
+
gqlVar vars = NULL;
|
350
|
+
|
351
|
+
if (NULL != (gq = agoo_req_query_value(req, indent_str, sizeof(indent_str) - 1, &qlen))) {
|
352
|
+
indent = (int)strtol(gq, NULL, 10);
|
353
|
+
}
|
354
|
+
if (NULL == (gq = agoo_req_query_value(req, query_str, sizeof(query_str) - 1, &qlen))) {
|
355
|
+
err_resp(req->res, &err, 500);
|
356
|
+
return;
|
357
|
+
}
|
358
|
+
op_name = agoo_req_query_value(req, operation_name_str, sizeof(operation_name_str) - 1, &oplen);
|
359
|
+
var_json = agoo_req_query_value(req, variables_str, sizeof(variables_str) - 1, &vlen);
|
360
|
+
|
361
|
+
if (NULL != var_json) {
|
362
|
+
if (NULL == (vars = parse_query_vars(&err, var_json, vlen)) && AGOO_ERR_OK != err.code) {
|
363
|
+
err_resp(req->res, &err, 400);
|
364
|
+
return;
|
365
|
+
}
|
366
|
+
}
|
367
|
+
// Only call after extracting the variables as it terminates the string with a \0.
|
368
|
+
qlen = agoo_req_query_decode((char*)gq, qlen);
|
369
|
+
if (NULL == (doc = sdl_parse_doc(&err, gq, qlen, vars))) {
|
370
|
+
err_resp(req->res, &err, 500);
|
371
|
+
return;
|
372
|
+
}
|
373
|
+
set_doc_op(doc, op_name, oplen);
|
374
|
+
|
375
|
+
if (NULL == gql_doc_eval_func) {
|
376
|
+
result = gql_doc_eval(&err, doc);
|
377
|
+
} else {
|
378
|
+
result = gql_doc_eval_func(&err, doc);
|
379
|
+
}
|
380
|
+
gql_doc_destroy(doc);
|
381
|
+
if (NULL == result) {
|
382
|
+
err_resp(req->res, &err, 500);
|
383
|
+
return;
|
384
|
+
}
|
385
|
+
value_resp(req->res, result, 200, indent);
|
386
|
+
}
|
387
|
+
|
388
|
+
static gqlValue
|
389
|
+
eval_post(agooErr err, agooReq req) {
|
390
|
+
gqlDoc doc = NULL;
|
391
|
+
const char *op_name = NULL;
|
392
|
+
const char *var_json = NULL;
|
393
|
+
const char *query = NULL;
|
394
|
+
int oplen;
|
395
|
+
int vlen;
|
396
|
+
int qlen = 0;
|
397
|
+
gqlVar vars = NULL;
|
398
|
+
const char *s;
|
399
|
+
int len;
|
400
|
+
gqlValue result = NULL;
|
401
|
+
gqlValue j = NULL;
|
402
|
+
|
403
|
+
// TBD handle query parameter and concatenate with body query if present
|
404
|
+
|
405
|
+
op_name = agoo_req_query_value(req, operation_name_str, sizeof(operation_name_str) - 1, &oplen);
|
406
|
+
var_json = agoo_req_query_value(req, variables_str, sizeof(variables_str) - 1, &vlen);
|
407
|
+
|
408
|
+
if (NULL != var_json) {
|
409
|
+
if (NULL == (vars = parse_query_vars(err, var_json, vlen)) && AGOO_ERR_OK != err->code) {
|
410
|
+
return NULL;
|
411
|
+
}
|
412
|
+
}
|
413
|
+
if (NULL == (s = agoo_req_header_value(req, "Content-Type", &len))) {
|
414
|
+
agoo_err_set(err, AGOO_ERR_TYPE, "required Content-Type not in the HTTP header");
|
415
|
+
return NULL;
|
416
|
+
}
|
417
|
+
if (0 == strncmp(graphql_content_type, s, sizeof(graphql_content_type) - 1)) {
|
418
|
+
if (NULL == (doc = sdl_parse_doc(err, req->body.start, req->body.len, vars))) {
|
419
|
+
return NULL;
|
420
|
+
}
|
421
|
+
} else if (0 == strncmp(json_content_type, s, sizeof(json_content_type) - 1)) {
|
422
|
+
gqlLink m;
|
423
|
+
|
424
|
+
if (NULL != (j = gql_json_parse(err, req->body.start, req->body.len))) {
|
425
|
+
if (GQL_SCALAR_OBJECT != j->type->scalar_kind) {
|
426
|
+
agoo_err_set(err, AGOO_ERR_TYPE, "JSON request must be an object");
|
427
|
+
goto DONE;
|
428
|
+
}
|
429
|
+
for (m = j->members; NULL != m; m = m->next) {
|
430
|
+
if (0 == strcmp("query", m->key)) {
|
431
|
+
if (NULL == (s = gql_string_get(m->value))) {
|
432
|
+
agoo_err_set(err, AGOO_ERR_TYPE, "query must be an string");
|
433
|
+
goto DONE;
|
434
|
+
}
|
435
|
+
query = s;
|
436
|
+
qlen = strlen(s);
|
437
|
+
} else if (0 == strcmp("operationName", m->key)) {
|
438
|
+
if (NULL == (s = gql_string_get(m->value))) {
|
439
|
+
agoo_err_set(err, AGOO_ERR_TYPE, "operationName must be an string");
|
440
|
+
goto DONE;
|
441
|
+
}
|
442
|
+
op_name = s;
|
443
|
+
oplen = strlen(s);
|
444
|
+
} else if (0 == strcmp("variables", m->key)) {
|
445
|
+
gqlLink link;
|
446
|
+
|
447
|
+
if (GQL_SCALAR_OBJECT != m->value->type->scalar_kind) {
|
448
|
+
agoo_err_set(err, AGOO_ERR_EVAL, "expected variables to be an object.");
|
449
|
+
goto DONE;
|
450
|
+
}
|
451
|
+
for (link = m->value->members; NULL != link; link = link->next) {
|
452
|
+
gqlVar v = gql_op_var_create(err, link->key, link->value->type, link->value);
|
453
|
+
|
454
|
+
link->value = NULL; // TBD is this correct?
|
455
|
+
if (NULL == v) {
|
456
|
+
goto DONE;
|
457
|
+
}
|
458
|
+
v->next = vars;
|
459
|
+
vars = v;
|
460
|
+
}
|
461
|
+
}
|
462
|
+
}
|
463
|
+
if (NULL == (doc = sdl_parse_doc(err, query, qlen, vars))) {
|
464
|
+
goto DONE;
|
465
|
+
}
|
466
|
+
} else {
|
467
|
+
goto DONE;
|
468
|
+
}
|
469
|
+
} else {
|
470
|
+
agoo_err_set(err, AGOO_ERR_TYPE, "unsupported content type");
|
471
|
+
return NULL;
|
472
|
+
}
|
473
|
+
set_doc_op(doc, op_name, oplen);
|
474
|
+
|
475
|
+
if (NULL == gql_doc_eval_func) {
|
476
|
+
result = gql_doc_eval(err, doc);
|
477
|
+
} else {
|
478
|
+
result = gql_doc_eval_func(err, doc);
|
479
|
+
}
|
480
|
+
|
481
|
+
DONE:
|
482
|
+
gql_doc_destroy(doc);
|
483
|
+
gql_value_destroy(j);
|
484
|
+
|
485
|
+
return result;
|
486
|
+
}
|
487
|
+
|
488
|
+
void
|
489
|
+
gql_eval_post_hook(agooReq req) {
|
490
|
+
struct _agooErr err = AGOO_ERR_INIT;
|
491
|
+
gqlValue result;
|
492
|
+
const char *s;
|
493
|
+
int len;
|
494
|
+
int indent = 0;
|
495
|
+
|
496
|
+
if (NULL != (s = agoo_req_query_value(req, indent_str, sizeof(indent_str) - 1, &len))) {
|
497
|
+
indent = (int)strtol(s, NULL, 10);
|
498
|
+
}
|
499
|
+
|
500
|
+
if (NULL == (result = eval_post(&err, req))) {
|
501
|
+
err_resp(req->res, &err, 400);
|
502
|
+
} else {
|
503
|
+
value_resp(req->res, result, 200, indent);
|
504
|
+
}
|
505
|
+
}
|
506
|
+
|
507
|
+
gqlValue
|
508
|
+
gql_get_arg_value(gqlKeyVal args, const char *key) {
|
509
|
+
gqlValue value = NULL;
|
510
|
+
|
511
|
+
if (NULL != args) {
|
512
|
+
for (; NULL != args->key; args++) {
|
513
|
+
if (0 == strcmp(key, args->key)) {
|
514
|
+
value = args->value;
|
515
|
+
break;
|
516
|
+
}
|
517
|
+
}
|
518
|
+
}
|
519
|
+
return value;
|
520
|
+
}
|