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.

Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +38 -0
  4. data/ext/agoo/agoo.c +11 -2
  5. data/ext/agoo/bind.c +15 -20
  6. data/ext/agoo/con.c +32 -25
  7. data/ext/agoo/debug.c +225 -162
  8. data/ext/agoo/debug.h +31 -51
  9. data/ext/agoo/doc.c +278 -5
  10. data/ext/agoo/doc.h +6 -1
  11. data/ext/agoo/err.c +1 -0
  12. data/ext/agoo/err.h +1 -0
  13. data/ext/agoo/error_stream.c +3 -6
  14. data/ext/agoo/gqlcobj.c +12 -0
  15. data/ext/agoo/gqlcobj.h +25 -0
  16. data/ext/agoo/gqleval.c +520 -0
  17. data/ext/agoo/gqleval.h +49 -0
  18. data/ext/agoo/gqlintro.c +1237 -97
  19. data/ext/agoo/gqlintro.h +8 -0
  20. data/ext/agoo/gqljson.c +460 -0
  21. data/ext/agoo/gqljson.h +15 -0
  22. data/ext/agoo/gqlvalue.c +679 -136
  23. data/ext/agoo/gqlvalue.h +29 -7
  24. data/ext/agoo/graphql.c +841 -362
  25. data/ext/agoo/graphql.h +180 -90
  26. data/ext/agoo/hook.c +8 -16
  27. data/ext/agoo/http.c +3 -4
  28. data/ext/agoo/log.c +22 -25
  29. data/ext/agoo/log.h +1 -0
  30. data/ext/agoo/page.c +24 -40
  31. data/ext/agoo/pub.c +23 -21
  32. data/ext/agoo/queue.c +2 -4
  33. data/ext/agoo/ready.c +9 -9
  34. data/ext/agoo/req.c +80 -5
  35. data/ext/agoo/req.h +2 -0
  36. data/ext/agoo/res.c +1 -3
  37. data/ext/agoo/rgraphql.c +753 -0
  38. data/ext/agoo/rresponse.c +9 -15
  39. data/ext/agoo/rserver.c +18 -17
  40. data/ext/agoo/sdl.c +1264 -120
  41. data/ext/agoo/sdl.h +8 -1
  42. data/ext/agoo/sectime.c +136 -0
  43. data/ext/agoo/sectime.h +19 -0
  44. data/ext/agoo/server.c +1 -3
  45. data/ext/agoo/subject.c +2 -4
  46. data/ext/agoo/text.c +124 -18
  47. data/ext/agoo/text.h +5 -1
  48. data/ext/agoo/upgraded.c +2 -4
  49. data/lib/agoo/version.rb +1 -1
  50. data/test/base_handler_test.rb +43 -40
  51. data/test/bind_test.rb +49 -48
  52. data/test/graphql_test.rb +1019 -0
  53. data/test/hijack_test.rb +1 -1
  54. data/test/rack_handler_test.rb +40 -34
  55. data/test/static_test.rb +33 -32
  56. metadata +17 -6
@@ -26,6 +26,7 @@ typedef enum {
26
26
  AGOO_ERR_IN_USE,
27
27
  AGOO_ERR_TOO_MANY,
28
28
  AGOO_ERR_TYPE,
29
+ AGOO_ERR_EVAL,
29
30
  AGOO_ERR_LAST
30
31
  } agooErrCode;
31
32
 
@@ -16,17 +16,14 @@ static void
16
16
  es_free(void *ptr) {
17
17
  ErrorStream es = (ErrorStream)ptr;
18
18
 
19
- DEBUG_FREE(mem_err_stream, ptr);
20
- DEBUG_FREE(mem_text, es->text)
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 = ALLOC(struct _errorStream);
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);
@@ -0,0 +1,12 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #include <stdio.h>
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+
7
+ #include "debug.h"
8
+ #include "gqlcobj.h"
9
+ #include "gqleval.h"
10
+
11
+
12
+ // TBD when a type is created, add a cobj to it
@@ -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
@@ -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
+ }