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
@@ -17,20 +17,17 @@ response_free(void *ptr) {
17
17
 
18
18
  while (NULL != (h = res->headers)) {
19
19
  res->headers = h->next;
20
- DEBUG_FREE(mem_header, h);
20
+ AGOO_FREE(h);
21
21
  xfree(h);
22
22
  }
23
- DEBUG_FREE(mem_res_body, res->body);
24
- DEBUG_FREE(mem_response, ptr);
25
- free(res->body); // allocated with strdup
26
- xfree(ptr);
23
+ AGOO_FREE(res->body); // allocated with strdup
24
+ AGOO_FREE(ptr);
27
25
  }
28
26
 
29
27
  VALUE
30
- response_new( ) {
31
- agooResponse res = ALLOC(struct _agooResponse);
28
+ response_new() {
29
+ agooResponse res = (agooResponse)AGOO_MALLOC(sizeof(struct _agooResponse));
32
30
 
33
- DEBUG_ALLOC(mem_response, res)
34
31
  memset(res, 0, sizeof(struct _agooResponse));
35
32
  res->code = 200;
36
33
 
@@ -47,9 +44,8 @@ static VALUE
47
44
  to_s(VALUE self) {
48
45
  agooResponse res = (agooResponse)DATA_PTR(self);
49
46
  int len = agoo_response_len(res);
50
- char *s = ALLOC_N(char, len + 1);
47
+ char *s = (char*)AGOO_MALLOC(len + 1);
51
48
 
52
- DEBUG_ALLOC(mem_to_s, s)
53
49
  agoo_response_fill(res, s);
54
50
 
55
51
  return rb_str_new(s, len);
@@ -96,10 +92,9 @@ body_set(VALUE self, VALUE val) {
96
92
  agooResponse res = (agooResponse)DATA_PTR(self);
97
93
 
98
94
  if (T_STRING == rb_type(val)) {
99
- if (NULL == (res->body = strdup(StringValuePtr(val)))) {
95
+ if (NULL == (res->body = AGOO_STRDUP(StringValuePtr(val)))) {
100
96
  rb_raise(rb_eArgError, "failed to copy body");
101
97
  }
102
- DEBUG_ALLOC(mem_res_body, res->body)
103
98
  res->blen = (int)RSTRING_LEN(val);
104
99
  } else {
105
100
  rb_raise(rb_eArgError, "Expected a string");
@@ -182,7 +177,7 @@ head_set(VALUE self, VALUE key, VALUE val) {
182
177
  } else {
183
178
  prev->next = h->next;
184
179
  }
185
- DEBUG_FREE(mem_header, h);
180
+ AGOO_FREED(h);
186
181
  xfree(h);
187
182
  break;
188
183
  }
@@ -202,8 +197,7 @@ head_set(VALUE self, VALUE key, VALUE val) {
202
197
  }
203
198
  }
204
199
  hlen = klen + vlen + 4;
205
- h = (agooHeader)ALLOC_N(char, sizeof(struct _agooHeader) - 8 + hlen + 1);
206
- DEBUG_ALLOC(mem_header, h)
200
+ h = (agooHeader)AGOO_MALLOC(sizeof(struct _agooHeader) - 8 + hlen + 1);
207
201
 
208
202
  h->next = NULL;
209
203
  h->len = hlen;
@@ -175,7 +175,9 @@ configure(agooErr err, int port, const char *root, VALUE options) {
175
175
  }
176
176
  if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("graphql"))))) {
177
177
  const char *path;
178
- agooHook hook;
178
+ agooHook dump_hook;
179
+ agooHook get_hook;
180
+ agooHook post_hook;
179
181
  char schema_path[256];
180
182
  long plen;
181
183
 
@@ -191,13 +193,13 @@ configure(agooErr err, int port, const char *root, VALUE options) {
191
193
  memcpy(schema_path, path, plen);
192
194
  memcpy(schema_path + plen, "/schema", 8);
193
195
 
194
- hook = agoo_hook_func_create(AGOO_GET, schema_path, gql_dump_hook, &agoo_server.eval_queue);
195
- hook->next = agoo_server.hooks;
196
- agoo_server.hooks = hook;
197
-
198
- hook = agoo_hook_func_create(AGOO_GET, path, gql_eval_hook, &agoo_server.eval_queue);
199
- hook->next = agoo_server.hooks;
200
- agoo_server.hooks = hook;
196
+ dump_hook = agoo_hook_func_create(AGOO_GET, schema_path, gql_dump_hook, &agoo_server.eval_queue);
197
+ get_hook = agoo_hook_func_create(AGOO_GET, path, gql_eval_get_hook, &agoo_server.eval_queue);
198
+ post_hook = agoo_hook_func_create(AGOO_POST, path, gql_eval_post_hook, &agoo_server.eval_queue);
199
+ dump_hook->next = get_hook;
200
+ get_hook->next = post_hook;
201
+ post_hook->next = agoo_server.hooks;
202
+ agoo_server.hooks = dump_hook;
201
203
  }
202
204
  if (Qnil != (v = rb_hash_lookup(options, ID2SYM(rb_intern("quiet"))))) {
203
205
  if (Qtrue == v) {
@@ -247,6 +249,8 @@ configure(agooErr err, int port, const char *root, VALUE options) {
247
249
  * - *:worker_count* [_Integer_] number of workers to fork. Defaults to one which is not to fork.
248
250
  *
249
251
  * - *:bind* [_String_|_Array_] a binding or array of binds. Examples are: "http ://127.0.0.1:6464", "unix:///tmp/agoo.socket", "http ://[::1]:6464, or to not restrict the address "http ://:6464".
252
+ *
253
+ * - *:graphql* [_String_] path to GraphQL endpoint if support for GraphQL is desired.
250
254
  */
251
255
  static VALUE
252
256
  rserver_init(int argc, VALUE *argv, VALUE self) {
@@ -319,7 +323,7 @@ rescue_error(VALUE x) {
319
323
 
320
324
  static VALUE
321
325
  handle_base_inner(void *x) {
322
- agooReq req = (agooReq)x;
326
+ agooReq req = (agooReq)x;
323
327
  volatile VALUE rr = request_wrap(req);
324
328
  volatile VALUE rres = response_new();
325
329
 
@@ -489,7 +493,6 @@ handle_rack_inner(void *x) {
489
493
  }
490
494
  req->hook = agoo_hook_create(AGOO_NONE, NULL, (void*)handler, PUSH_HOOK, &agoo_server.eval_queue);
491
495
  rupgraded_create(req->res->con, handler, request_env(req, Qnil));
492
-
493
496
  t->len = snprintf(t->text, 1024, "HTTP/1.1 101 %s\r\n", status_msg);
494
497
  t = agoo_ws_add_headers(req, t);
495
498
  break;
@@ -552,7 +555,7 @@ handle_rack(void *x) {
552
555
 
553
556
  static VALUE
554
557
  handle_wab_inner(void *x) {
555
- agooReq req = (agooReq)x;
558
+ agooReq req = (agooReq)x;
556
559
  volatile VALUE rr = request_wrap(req);
557
560
  volatile VALUE rres = response_new();
558
561
 
@@ -756,10 +759,10 @@ rserver_start(VALUE self) {
756
759
  rb_raise(rb_eStandardError, "%s", err.msg);
757
760
  }
758
761
  if (0 >= agoo_server.thread_cnt) {
759
- agooReq req;
762
+ agooReq req;
760
763
 
761
764
  while (agoo_server.active) {
762
- if (NULL != (req = (agooReq)agoo_queue_pop(&agoo_server.eval_queue, 0.1))) {
765
+ if (NULL != (req = (agooReq)agoo_queue_pop(&agoo_server.eval_queue, 0.01))) { // TBD 0.1
763
766
  handle_protected(req, false);
764
767
  agoo_req_destroy(req);
765
768
  } else {
@@ -768,8 +771,7 @@ rserver_start(VALUE self) {
768
771
 
769
772
  }
770
773
  } else {
771
- the_rserver.eval_threads = (VALUE*)malloc(sizeof(VALUE) * (agoo_server.thread_cnt + 1));
772
- DEBUG_ALLOC(mem_eval_threads, agoo_server.eval_threads);
774
+ the_rserver.eval_threads = (VALUE*)AGOO_MALLOC(sizeof(VALUE) * (agoo_server.thread_cnt + 1));
773
775
 
774
776
  for (i = agoo_server.thread_cnt, vp = the_rserver.eval_threads; 0 < i; i--, vp++) {
775
777
  *vp = rb_thread_create(wrap_process_loop, NULL);
@@ -805,8 +807,7 @@ stop_runners() {
805
807
  }
806
808
  dsleep(0.02);
807
809
  }
808
- DEBUG_FREE(mem_eval_threads, the_rserver.eval_threads);
809
- free(the_rserver.eval_threads);
810
+ AGOO_FREE(the_rserver.eval_threads);
810
811
  the_rserver.eval_threads = NULL;
811
812
  }
812
813
  }
@@ -3,35 +3,80 @@
3
3
  #include <stdio.h>
4
4
  #include <string.h>
5
5
 
6
+ #include "debug.h"
6
7
  #include "doc.h"
7
8
  #include "gqlvalue.h"
8
9
  #include "graphql.h"
9
10
  #include "sdl.h"
10
11
 
12
+ static const char query_str[] = "query";
13
+ static const char union_str[] = "union";
14
+ static const char enum_str[] = "enum";
15
+ static const char mutation_str[] = "mutation";
16
+ static const char subscription_str[] = "subscription";
17
+ static const char interface_str[] = "interface";
18
+ static const char input_str[] = "input";
19
+ static const char type_str[] = "type";
20
+
21
+ static int make_sel(agooErr err, agooDoc doc, gqlDoc gdoc, gqlOp op, gqlSel *parentp);
22
+
11
23
  static int
12
- extract_name(agooErr err, agooDoc doc, const char *key, int klen, char *name, size_t max) {
13
- const char *start;
24
+ extract_desc(agooErr err, agooDoc doc, const char **descp, size_t *lenp) {
25
+ agoo_doc_skip_white(doc);
26
+ *descp = NULL;
27
+ *lenp = 0;
28
+ if ('"' == *doc->cur) {
29
+ const char *desc_end = NULL;
30
+ const char *desc = doc->cur + 1;
31
+
32
+ if (AGOO_ERR_OK != agoo_doc_read_string(err, doc)) {
33
+ return err->code;
34
+ }
35
+ if ('"' == *desc && '"' == desc[1]) { // must be a """
36
+ desc += 2;
37
+ desc_end = doc->cur - 3;
38
+ } else {
39
+ desc_end = doc->cur - 1;
40
+ }
41
+ *descp = desc;
42
+ *lenp = desc_end - *descp;
43
+ }
44
+ return AGOO_ERR_OK;
45
+ }
46
+
47
+ static size_t
48
+ read_name(agooErr err, agooDoc doc, char *name, size_t max) {
14
49
  size_t nlen;
50
+ const char *start = doc->cur;
15
51
 
16
- if (0 != strncmp(doc->cur, key, klen)) {
17
- return agoo_doc_err(doc, err, "Expected %s key word", key);
18
- }
19
- doc->cur += klen;
20
- if (0 == agoo_doc_skip_white(doc)) {
21
- return agoo_doc_err(doc, err, "Expected %s key word", key);
22
- }
23
- start = doc->cur;
24
52
  agoo_doc_read_token(doc);
25
53
  if (doc->cur == start) {
26
- return agoo_doc_err(doc, err, "Name not provided");
54
+ agoo_doc_err(doc, err, "Name not provided");
55
+ return 0;
27
56
  }
28
57
  nlen = doc->cur - start;
29
58
  if (max <= nlen) {
30
- return agoo_doc_err(doc, err, "Name too long");
59
+ agoo_doc_err(doc, err, "Name too long");
60
+ return 0;
31
61
  }
32
62
  strncpy(name, start, nlen);
33
63
  name[nlen] = '\0';
34
-
64
+
65
+ return nlen;
66
+ }
67
+
68
+ static int
69
+ extract_name(agooErr err, agooDoc doc, const char *key, int klen, char *name, size_t max) {
70
+ if (0 != strncmp(doc->cur, key, klen)) {
71
+ return agoo_doc_err(doc, err, "Expected %s key word", key);
72
+ }
73
+ doc->cur += klen;
74
+ if (0 == agoo_doc_skip_white(doc)) {
75
+ return agoo_doc_err(doc, err, "Expected %s key word", key);
76
+ }
77
+ if (0 == read_name(err, doc, name, max)) {
78
+ return err->code;
79
+ }
35
80
  return AGOO_ERR_OK;
36
81
  }
37
82
 
@@ -42,18 +87,126 @@ make_scalar(agooErr err, agooDoc doc, const char *desc, int len) {
42
87
  if (AGOO_ERR_OK != extract_name(err, doc, "scalar", 6, name, sizeof(name))) {
43
88
  return err->code;
44
89
  }
45
- gql_scalar_create(err, name, desc, len, false);
90
+ gql_scalar_create(err, name, desc, len);
46
91
 
47
92
  return AGOO_ERR_OK;
48
93
  }
49
94
 
50
95
  static int
51
- make_enum(agooErr err, agooDoc doc, const char *desc, int len) {
96
+ read_type(agooErr err, agooDoc doc, gqlType *typep, bool *required) {
97
+ agoo_doc_skip_white(doc);
98
+ if ('[' == *doc->cur) {
99
+ gqlType base;
100
+ bool not_empty = false;
101
+
102
+ doc->cur++;
103
+ if (AGOO_ERR_OK != read_type(err, doc, &base, &not_empty)) {
104
+ return err->code;
105
+ }
106
+ agoo_doc_skip_white(doc);
107
+ if (']' != *doc->cur) {
108
+ return agoo_doc_err(doc, err, "List type not terminated with a ]");
109
+ }
110
+ doc->cur++;
111
+ if (NULL == (*typep = gql_assure_list(err, base, not_empty))) {
112
+ return err->code;
113
+ }
114
+ } else {
115
+ char name[256];
116
+
117
+ if (0 == read_name(err, doc, name, sizeof(name))) {
118
+ return err->code;
119
+ }
120
+ if (NULL == (*typep = gql_assure_type(err, name))) {
121
+ return err->code;
122
+ }
123
+ }
124
+ *required = false;
125
+ agoo_doc_skip_white(doc);
126
+ if ('!' == *doc->cur) {
127
+ *required = true;
128
+ doc->cur++;
129
+ }
130
+ return AGOO_ERR_OK;
131
+ }
132
+
133
+ static int
134
+ make_use_arg(agooErr err, agooDoc doc, gqlDirUse use) {
135
+ char name[256];
136
+ gqlValue value = NULL;
137
+
138
+ if (0 == read_name(err, doc, name, sizeof(name))) {
139
+ return err->code;
140
+ }
141
+ agoo_doc_skip_white(doc);
142
+ if (':' != *doc->cur) {
143
+ return agoo_doc_err(doc, err, "Expected ':'");
144
+ }
145
+ doc->cur++;
146
+ if (NULL == (value = agoo_doc_read_value(err, doc, NULL))) {
147
+ return err->code;
148
+ }
149
+ if (AGOO_ERR_OK == gql_dir_use_arg(err, use, name, value)) {
150
+ return err->code;
151
+ }
152
+ return AGOO_ERR_OK;
153
+ }
154
+
155
+ static int
156
+ extract_dir_use(agooErr err, agooDoc doc, gqlDirUse *uses) {
157
+ char name[256];
158
+ gqlDirUse use;
159
+
160
+ agoo_doc_skip_white(doc);
161
+ if ('@' != *doc->cur) {
162
+ return AGOO_ERR_OK;
163
+ }
164
+ doc->cur++;
165
+ if (0 == read_name(err, doc, name, sizeof(name))) {
166
+ return err->code;
167
+ }
168
+ if (NULL == (use = gql_dir_use_create(err, name))) {
169
+ return err->code;
170
+ }
171
+ agoo_doc_skip_white(doc);
172
+ if ('(' == *doc->cur) {
173
+ doc->cur++;
174
+ while (doc->cur < doc->end) {
175
+ if (AGOO_ERR_OK != make_use_arg(err, doc, use)) {
176
+ return err->code;
177
+ }
178
+ agoo_doc_skip_white(doc);
179
+ if (')' == *doc->cur) {
180
+ doc->cur++;
181
+ break;
182
+ }
183
+ }
184
+ }
185
+ if (NULL == *uses) {
186
+ *uses = use;
187
+ } else {
188
+ gqlDirUse u = *uses;
189
+
190
+ for (; NULL != u->next; u = u->next) {
191
+ }
192
+ u->next = use;
193
+ }
194
+ return AGOO_ERR_OK;
195
+ }
196
+
197
+ static int
198
+ make_enum(agooErr err, agooDoc doc, const char *desc, size_t dlen) {
52
199
  char name[256];
53
200
  const char *start;
54
201
  gqlType type;
202
+ gqlDirUse uses = NULL;
203
+ gqlEnumVal ev;
204
+ size_t len;
55
205
 
56
- if (AGOO_ERR_OK != extract_name(err, doc, "enum", 4, name, sizeof(name))) {
206
+ if (AGOO_ERR_OK != extract_name(err, doc, enum_str, sizeof(enum_str) - 1, name, sizeof(name))) {
207
+ return err->code;
208
+ }
209
+ if (AGOO_ERR_OK != extract_dir_use(err, doc, &uses)) {
57
210
  return err->code;
58
211
  }
59
212
  agoo_doc_skip_white(doc);
@@ -62,13 +215,23 @@ make_enum(agooErr err, agooDoc doc, const char *desc, int len) {
62
215
  }
63
216
  doc->cur++;
64
217
 
65
- if (NULL == (type = gql_enum_create(err, name, desc, len, false))) {
218
+ if (NULL == (type = gql_enum_create(err, name, desc, dlen))) {
66
219
  return err->code;
67
220
  }
221
+ type->dir = uses;
68
222
  while (doc->cur < doc->end) {
69
- agoo_doc_skip_white(doc);
223
+ desc = NULL;
224
+ uses = NULL;
225
+
226
+ if (AGOO_ERR_OK != extract_desc(err, doc, &desc, &dlen)) {
227
+ return err->code;
228
+ }
70
229
  start = doc->cur;
71
230
  agoo_doc_read_token(doc);
231
+ len = doc->cur - start;
232
+ if (AGOO_ERR_OK != extract_dir_use(err, doc, &uses)) {
233
+ return err->code;
234
+ }
72
235
  if (doc->cur == start) {
73
236
  if ('}' == *doc->cur) {
74
237
  doc->cur++;
@@ -76,9 +239,10 @@ make_enum(agooErr err, agooDoc doc, const char *desc, int len) {
76
239
  }
77
240
  return agoo_doc_err(doc, err, "Invalid Enum value");
78
241
  }
79
- if (AGOO_ERR_OK != gql_enum_add(err, type, start, (int)(doc->cur - start))) {
242
+ if (NULL == (ev = gql_enum_append(err, type, start, len, desc, dlen))) {
80
243
  return err->code;
81
244
  }
245
+ ev->dir = uses;
82
246
  }
83
247
  return AGOO_ERR_OK;
84
248
  }
@@ -86,10 +250,15 @@ make_enum(agooErr err, agooDoc doc, const char *desc, int len) {
86
250
  static int
87
251
  make_union(agooErr err, agooDoc doc, const char *desc, int len) {
88
252
  char name[256];
89
- const char *start;
90
253
  gqlType type;
254
+ gqlDirUse uses = NULL;
255
+ gqlType member;
256
+ bool required;
91
257
 
92
- if (AGOO_ERR_OK != extract_name(err, doc, "union", 5, name, sizeof(name))) {
258
+ if (AGOO_ERR_OK != extract_name(err, doc, union_str, sizeof(union_str) - 1, name, sizeof(name))) {
259
+ return err->code;
260
+ }
261
+ if (AGOO_ERR_OK != extract_dir_use(err, doc, &uses)) {
93
262
  return err->code;
94
263
  }
95
264
  agoo_doc_skip_white(doc);
@@ -99,14 +268,15 @@ make_union(agooErr err, agooDoc doc, const char *desc, int len) {
99
268
  doc->cur++;
100
269
  agoo_doc_skip_white(doc);
101
270
 
102
- if (NULL == (type = gql_union_create(err, name, desc, len, false))) {
271
+ if (NULL == (type = gql_union_create(err, name, desc, len))) {
103
272
  return err->code;
104
273
  }
274
+ type->dir = uses;
105
275
  while (doc->cur < doc->end) {
106
- agoo_doc_skip_white(doc);
107
- start = doc->cur;
108
- agoo_doc_read_token(doc);
109
- if (AGOO_ERR_OK != gql_union_add(err, type, start, (int)(doc->cur - start))) {
276
+ if (AGOO_ERR_OK != read_type(err, doc, &member, &required)) {
277
+ return err->code;
278
+ }
279
+ if (AGOO_ERR_OK != gql_union_add(err, type, member)) {
110
280
  return err->code;
111
281
  }
112
282
  agoo_doc_skip_white(doc);
@@ -119,28 +289,19 @@ make_union(agooErr err, agooDoc doc, const char *desc, int len) {
119
289
  }
120
290
 
121
291
  static int
122
- make_arg(agooErr err, agooDoc doc, gqlDir dir) {
292
+ make_dir_arg(agooErr err, agooDoc doc, gqlDir dir) {
123
293
  char name[256];
124
294
  char type_name[256];
295
+ gqlType type;
125
296
  const char *start;
126
297
  const char *desc = NULL;
127
- const char *desc_end = NULL;
298
+ size_t dlen;
128
299
  size_t nlen;
129
300
  bool required = false;
130
301
  gqlValue dv = NULL;
131
-
132
- agoo_doc_skip_white(doc);
133
- if ('"' == *doc->cur) {
134
- desc = doc->cur + 1;
135
- if (AGOO_ERR_OK != agoo_doc_read_string(err, doc)) {
136
- return err->code;
137
- }
138
- if ('"' == *desc) { // must be a """
139
- desc += 2;
140
- desc_end = doc->cur - 3;
141
- } else {
142
- desc_end = doc->cur - 1;
143
- }
302
+
303
+ if (AGOO_ERR_OK != extract_desc(err, doc, &desc, &dlen)) {
304
+ return err->code;
144
305
  }
145
306
  agoo_doc_skip_white(doc);
146
307
  start = doc->cur;
@@ -161,6 +322,7 @@ make_arg(agooErr err, agooDoc doc, gqlDir dir) {
161
322
 
162
323
  // read type
163
324
  agoo_doc_skip_white(doc);
325
+
164
326
  start = doc->cur;
165
327
  agoo_doc_read_token(doc);
166
328
  if (doc->cur == start) {
@@ -173,11 +335,15 @@ make_arg(agooErr err, agooDoc doc, gqlDir dir) {
173
335
  strncpy(type_name, start, nlen);
174
336
  type_name[nlen] = '\0';
175
337
 
338
+ if (NULL == (type = gql_assure_type(err, type_name))) {
339
+ return err->code;
340
+ }
176
341
  agoo_doc_skip_white(doc);
177
342
  if ('!' == *doc->cur) {
178
343
  required = true;
344
+ doc->cur++;
179
345
  } else if ('=' == *doc->cur) {
180
- if (NULL == (dv = agoo_doc_read_value(err, doc))) {
346
+ if (NULL == (dv = agoo_doc_read_value(err, doc, type))) {
181
347
  return err->code;
182
348
  }
183
349
  }
@@ -185,7 +351,7 @@ make_arg(agooErr err, agooDoc doc, gqlDir dir) {
185
351
  if ('@' == *doc->cur) {
186
352
  // TBD directive
187
353
  }
188
- if (NULL == gql_dir_arg(err, dir, name, type_name, desc, (int)(desc_end - desc), dv, required)) {
354
+ if (NULL == gql_dir_arg(err, dir, name, type, desc, dlen, dv, required)) {
189
355
  return err->code;
190
356
  }
191
357
  return AGOO_ERR_OK;
@@ -209,23 +375,16 @@ make_directive(agooErr err, agooDoc doc, const char *desc, int len) {
209
375
  return agoo_doc_err(doc, err, "Expected '@'");
210
376
  }
211
377
  doc->cur++;
212
- start = doc->cur;
213
- agoo_doc_read_token(doc);
214
- if (doc->cur == start) {
215
- return agoo_doc_err(doc, err, "Name not provided");
216
- }
217
- nlen = doc->cur - start;
218
- if (sizeof(name) <= nlen) {
219
- return agoo_doc_err(doc, err, "Name too long");
378
+ if (0 == (nlen = read_name(err, doc, name, sizeof(name)))) {
379
+ return err->code;
220
380
  }
221
- strncpy(name, start, nlen);
222
- name[nlen] = '\0';
223
- if (NULL == (dir = gql_directive_create(err, name, desc, len, false))) {
381
+ if (NULL == (dir = gql_directive_create(err, name, desc, len))) {
224
382
  return err->code;
225
383
  }
226
384
  if ('(' == *doc->cur) {
385
+ doc->cur++;
227
386
  while (doc->cur < doc->end) {
228
- if (AGOO_ERR_OK != make_arg(err, doc, dir)) {
387
+ if (AGOO_ERR_OK != make_dir_arg(err, doc, dir)) {
229
388
  return err->code;
230
389
  }
231
390
  agoo_doc_skip_white(doc);
@@ -258,77 +417,1062 @@ make_directive(agooErr err, agooDoc doc, const char *desc, int len) {
258
417
  return AGOO_ERR_OK;
259
418
  }
260
419
 
261
- int
262
- sdl_parse(agooErr err, const char *str, int len) {
263
- struct _agooDoc doc;
264
- const char *desc = NULL;
265
- const char *desc_end = NULL;
420
+ static int
421
+ make_field_arg(agooErr err, agooDoc doc, gqlField field) {
422
+ char name[256];
423
+ gqlType type;
424
+ const char *desc = NULL;
425
+ size_t dlen;
426
+ bool required = false;
427
+ gqlValue dval = NULL;
266
428
 
267
- agoo_doc_init(&doc, str, len);
429
+ if (AGOO_ERR_OK != extract_desc(err, doc, &desc, &dlen)) {
430
+ return err->code;
431
+ }
432
+ agoo_doc_skip_white(doc);
433
+ if (0 == read_name(err, doc, name, sizeof(name))) {
434
+ return err->code;
435
+ }
436
+ agoo_doc_skip_white(doc);
437
+ if (':' != *doc->cur) {
438
+ return agoo_doc_err(doc, err, "Expected :");
439
+ }
440
+ doc->cur++;
441
+
442
+ if (AGOO_ERR_OK != read_type(err, doc, &type, &required)) {
443
+ return err->code;
444
+ }
445
+ agoo_doc_skip_white(doc);
268
446
 
269
- while (doc.cur < doc.end) {
270
- agoo_doc_next_token(&doc);
271
- switch (*doc.cur) {
272
- case '"':
273
- desc = doc.cur + 1;
274
- if (AGOO_ERR_OK != agoo_doc_read_string(err, &doc)) {
275
- return err->code;
276
- }
277
- if ('"' == *desc) { // must be a """
278
- desc += 2;
279
- desc_end = doc.cur - 3;
280
- } else {
281
- desc_end = doc.cur - 1;
282
- }
283
- break;
284
- case 's': // scalar
285
- if (AGOO_ERR_OK != make_scalar(err, &doc, desc, (int)(desc_end - desc))) {
286
- return err->code;
287
- }
288
- break;
289
- case 'e': // enum, and extend interface or type
290
- if (4 < (doc.end - doc.cur)) {
291
- if ('n' == doc.cur[1]) {
292
- if (AGOO_ERR_OK != make_enum(err, &doc, desc, (int)(desc_end - desc))) {
293
- return err->code;
294
- }
295
- break;
296
- } else {
297
- // TBD extend
298
- break;
299
- }
300
- }
301
- return agoo_doc_err(&doc, err, "Unknown directive");
302
- case 'u': // union
303
- if (AGOO_ERR_OK != make_union(err, &doc, desc, (int)(desc_end - desc))) {
304
- return err->code;
305
- }
306
- break;
307
- case 'd': // directive
308
- if (AGOO_ERR_OK != make_directive(err, &doc, desc, (int)(desc_end - desc))) {
447
+ switch (*doc->cur) {
448
+ case '=':
449
+ if (NULL == (dval = agoo_doc_read_value(err, doc, type))) {
450
+ return err->code;
451
+ }
452
+ break;
453
+ case '@':
454
+ // TBD read directives
455
+ break;
456
+ default: // ) or next arg
457
+ break;
458
+ }
459
+ if (NULL == gql_field_arg(err, field, name, type, desc, dlen, dval, required)) {
460
+ return err->code;
461
+ }
462
+ return AGOO_ERR_OK;
463
+ }
464
+
465
+ static int
466
+ make_field(agooErr err, agooDoc doc, gqlType type, bool allow_args) {
467
+ char name[256];
468
+ gqlType return_type;
469
+ const char *arg_start = NULL;
470
+ gqlField field;
471
+ gqlDirUse uses = NULL;
472
+ gqlValue dval = NULL;
473
+ const char *desc = NULL;
474
+ size_t dlen;
475
+ bool required = false;
476
+
477
+ if (AGOO_ERR_OK != extract_desc(err, doc, &desc, &dlen)) {
478
+ return err->code;
479
+ }
480
+ if (0 == read_name(err, doc, name, sizeof(name))) {
481
+ return err->code;
482
+ }
483
+ agoo_doc_skip_white(doc);
484
+ switch (*doc->cur) {
485
+ case '(':
486
+ if (!allow_args) {
487
+ return agoo_doc_err(doc, err, "Input fields can not have arguments.");
488
+ }
489
+ doc->cur++;
490
+ arg_start = doc->cur;
491
+ if (!agoo_doc_skip_to(doc, ')')) {
492
+ return agoo_doc_err(doc, err, "Argument list not terminated with a )");
493
+ }
494
+ doc->cur++;
495
+ agoo_doc_skip_white(doc);
496
+ if (':' != *doc->cur) {
497
+ return agoo_doc_err(doc, err, "Expected :");
498
+ }
499
+ doc->cur++;
500
+ break;
501
+ case ':':
502
+ doc->cur++;
503
+ // okay
504
+ break;
505
+ default:
506
+ return agoo_doc_err(doc, err, "Expected : or (");
507
+ }
508
+ if (AGOO_ERR_OK != read_type(err, doc, &return_type, &required)) {
509
+ return err->code;
510
+ }
511
+ if ('=' == *doc->cur) {
512
+ doc->cur++;
513
+ if (NULL == (dval = agoo_doc_read_value(err, doc, return_type))) {
514
+ return err->code;
515
+ }
516
+ }
517
+ if (AGOO_ERR_OK != extract_dir_use(err, doc, &uses)) {
518
+ return err->code;
519
+ }
520
+ if (NULL == (field = gql_type_field(err, type, name, return_type, dval, desc, dlen, required))) {
521
+ return err->code;
522
+ }
523
+ field->dir = uses;
524
+ if (NULL != arg_start) {
525
+ const char *cur = doc->cur;
526
+
527
+ doc->cur = arg_start;
528
+ while (true) {
529
+ if (AGOO_ERR_OK != make_field_arg(err, doc, field)) {
309
530
  return err->code;
310
531
  }
311
- break;
312
- case 't': // type
313
- break;
314
- case 'i': // interface, input
315
- if (5 < (doc.end - doc.cur) && 'n' == doc.cur[1]) {
316
- if ('p' == doc.cur[2]) {
317
- // input
318
- break;
319
- } else {
320
- // TBD interface
321
- break;
322
- }
532
+ agoo_doc_skip_white(doc);
533
+ if (')' == *doc->cur) {
534
+ break;
323
535
  }
324
- return agoo_doc_err(&doc, err, "Unknown directive");
325
- case 'f': // fragment
326
- break;
327
- case '\0':
328
- return AGOO_ERR_OK;
329
- default:
330
- return agoo_doc_err(&doc, err, "Unknown directive");
331
536
  }
537
+ doc->cur = cur;
332
538
  }
333
539
  return AGOO_ERR_OK;
334
540
  }
541
+
542
+ static int
543
+ make_interface(agooErr err, agooDoc doc, const char *desc, int len) {
544
+ char name[256];
545
+ gqlType type;
546
+ gqlDirUse uses = NULL;
547
+
548
+ if (AGOO_ERR_OK != extract_name(err, doc, interface_str, sizeof(interface_str) - 1, name, sizeof(name))) {
549
+ return err->code;
550
+ }
551
+ if (AGOO_ERR_OK != extract_dir_use(err, doc, &uses)) {
552
+ return err->code;
553
+ }
554
+ agoo_doc_skip_white(doc);
555
+ if ('{' != *doc->cur) {
556
+ return agoo_doc_err(doc, err, "Expected '{'");
557
+ }
558
+ doc->cur++;
559
+ agoo_doc_skip_white(doc);
560
+
561
+ if (NULL == (type = gql_interface_create(err, name, desc, len))) {
562
+ return err->code;
563
+ }
564
+ type->dir = uses;
565
+ while (doc->cur < doc->end) {
566
+ if ('}' == *doc->cur) {
567
+ doc->cur++; // skip }
568
+ break;
569
+ }
570
+ if (AGOO_ERR_OK != make_field(err, doc, type, true)) {
571
+ return err->code;
572
+ }
573
+ agoo_doc_skip_white(doc);
574
+ }
575
+ return AGOO_ERR_OK;
576
+ }
577
+
578
+ static int
579
+ make_input(agooErr err, agooDoc doc, const char *desc, int len) {
580
+ char name[256];
581
+ gqlType type;
582
+ gqlDirUse uses = NULL;
583
+
584
+ if (AGOO_ERR_OK != extract_name(err, doc, input_str, sizeof(input_str) - 1, name, sizeof(name))) {
585
+ return err->code;
586
+ }
587
+ if (AGOO_ERR_OK != extract_dir_use(err, doc, &uses)) {
588
+ return err->code;
589
+ }
590
+ agoo_doc_skip_white(doc);
591
+ if ('{' != *doc->cur) {
592
+ return agoo_doc_err(doc, err, "Expected '{'");
593
+ }
594
+ doc->cur++;
595
+ agoo_doc_skip_white(doc);
596
+
597
+ if (NULL == (type = gql_input_create(err, name, desc, len))) {
598
+ return err->code;
599
+ }
600
+ type->dir = uses;
601
+
602
+ while (doc->cur < doc->end) {
603
+ if ('}' == *doc->cur) {
604
+ doc->cur++; // skip }
605
+ break;
606
+ }
607
+ if (AGOO_ERR_OK != make_field(err, doc, type, false)) {
608
+ return err->code;
609
+ }
610
+ agoo_doc_skip_white(doc);
611
+ }
612
+ return AGOO_ERR_OK;
613
+ }
614
+
615
+ static int
616
+ extract_interfaces(agooErr err, agooDoc doc, gqlTypeLink *interfacesp) {
617
+ gqlType type;
618
+ gqlTypeLink link;
619
+ bool required;
620
+ bool first = true;
621
+
622
+ agoo_doc_skip_white(doc);
623
+ if (0 != strncmp("implements", doc->cur, 10)) {
624
+ return AGOO_ERR_OK;
625
+ }
626
+ doc->cur += 10;
627
+ if (0 == agoo_doc_skip_white(doc)) {
628
+ return agoo_doc_err(doc, err, "Expected white after 'implements'");
629
+ }
630
+ while (doc->cur < doc->end) {
631
+ agoo_doc_skip_white(doc);
632
+ if ('{' == *doc->cur || '@' == *doc->cur) {
633
+ break;
634
+ }
635
+ if ('&' == *doc->cur) {
636
+ doc->cur++;
637
+ agoo_doc_skip_white(doc);
638
+ } else if (!first) {
639
+ return agoo_doc_err(doc, err, "Expected &");
640
+ }
641
+ first = false;
642
+ required = false;
643
+ type = NULL;
644
+ if (AGOO_ERR_OK != read_type(err, doc, &type, &required)) {
645
+ return err->code;
646
+ }
647
+ if (NULL == (link = (gqlTypeLink)AGOO_MALLOC(sizeof(struct _gqlTypeLink)))) {
648
+ return agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a GraphQL interface.");
649
+ }
650
+ link->next = NULL;
651
+ link->type = type;
652
+ if (NULL == *interfacesp) {
653
+ *interfacesp = link;
654
+ } else {
655
+ gqlTypeLink tl = *interfacesp;
656
+
657
+ for (; NULL != tl->next; tl = tl->next) {
658
+ }
659
+ tl->next = link;
660
+ }
661
+ }
662
+ return AGOO_ERR_OK;
663
+ }
664
+
665
+ static int
666
+ make_type(agooErr err, agooDoc doc, const char *desc, int len) {
667
+ char name[256];
668
+ gqlType type;
669
+ gqlDirUse uses = NULL;
670
+ gqlTypeLink interfaces = NULL;
671
+
672
+ if (AGOO_ERR_OK != extract_name(err, doc, type_str, sizeof(type_str) - 1, name, sizeof(name))) {
673
+ return err->code;
674
+ }
675
+ if (AGOO_ERR_OK != extract_interfaces(err, doc, &interfaces)) {
676
+ return err->code;
677
+ }
678
+ if (AGOO_ERR_OK != extract_dir_use(err, doc, &uses)) {
679
+ return err->code;
680
+ }
681
+ agoo_doc_skip_white(doc);
682
+ if ('{' != *doc->cur) {
683
+ return agoo_doc_err(doc, err, "Expected '{'");
684
+ }
685
+ doc->cur++;
686
+ agoo_doc_skip_white(doc);
687
+
688
+ if (NULL == (type = gql_type_create(err, name, desc, len, interfaces))) {
689
+ return err->code;
690
+ }
691
+ type->dir = uses;
692
+
693
+ while (doc->cur < doc->end) {
694
+ if ('}' == *doc->cur) {
695
+ doc->cur++; // skip }
696
+ break;
697
+ }
698
+ if (AGOO_ERR_OK != make_field(err, doc, type, true)) {
699
+ return err->code;
700
+ }
701
+ agoo_doc_skip_white(doc);
702
+ }
703
+ return AGOO_ERR_OK;
704
+ }
705
+
706
+ int
707
+ sdl_parse(agooErr err, const char *str, int len) {
708
+ struct _agooDoc doc;
709
+ const char *desc = NULL;
710
+ size_t dlen = 0;
711
+
712
+ agoo_doc_init(&doc, str, len);
713
+
714
+ while (doc.cur < doc.end) {
715
+ agoo_doc_next_token(&doc);
716
+ switch (*doc.cur) {
717
+ case '"':
718
+ if (AGOO_ERR_OK != extract_desc(err, &doc, &desc, &dlen)) {
719
+ return err->code;
720
+ }
721
+ break;
722
+ case 's': // scalar
723
+ if (AGOO_ERR_OK != make_scalar(err, &doc, desc, dlen)) {
724
+ return err->code;
725
+ }
726
+ desc = NULL;
727
+ dlen = 0;
728
+ break;
729
+ case 'e': // enum, and extend interface or type
730
+ if (4 < (doc.end - doc.cur)) {
731
+ if ('n' == doc.cur[1]) {
732
+ if (AGOO_ERR_OK != make_enum(err, &doc, desc, dlen)) {
733
+ return err->code;
734
+ }
735
+ desc = NULL;
736
+ dlen = 0;
737
+ break;
738
+ } else {
739
+ // TBD extend
740
+ desc = NULL;
741
+ dlen = 0;
742
+ break;
743
+ }
744
+ }
745
+ return agoo_doc_err(&doc, err, "Unknown directive");
746
+ case 'u': // union
747
+ if (AGOO_ERR_OK != make_union(err, &doc, desc, dlen)) {
748
+ return err->code;
749
+ }
750
+ desc = NULL;
751
+ dlen = 0;
752
+ break;
753
+ case 'd': // directive
754
+ if (AGOO_ERR_OK != make_directive(err, &doc, desc, dlen)) {
755
+ return err->code;
756
+ }
757
+ desc = NULL;
758
+ dlen = 0;
759
+ break;
760
+ case 't': // type
761
+ if (AGOO_ERR_OK != make_type(err, &doc, desc, dlen)) {
762
+ return err->code;
763
+ }
764
+ desc = NULL;
765
+ dlen = 0;
766
+ break;
767
+ case 'i': // interface, input
768
+ if (5 < (doc.end - doc.cur) && 'n' == doc.cur[1]) {
769
+ if ('p' == doc.cur[2]) {
770
+ if (AGOO_ERR_OK != make_input(err, &doc, desc, dlen)) {
771
+ return err->code;
772
+ }
773
+ desc = NULL;
774
+ dlen = 0;
775
+ break;
776
+ } else {
777
+ if (AGOO_ERR_OK != make_interface(err, &doc, desc, dlen)) {
778
+ return err->code;
779
+ }
780
+ desc = NULL;
781
+ dlen = 0;
782
+ break;
783
+ }
784
+ }
785
+ return agoo_doc_err(&doc, err, "Unknown directive");
786
+ case '\0':
787
+ return AGOO_ERR_OK;
788
+ default:
789
+ return agoo_doc_err(&doc, err, "Unknown directive");
790
+ }
791
+ }
792
+ return AGOO_ERR_OK;
793
+ }
794
+
795
+ // Parse Execution Definition.
796
+
797
+ static gqlSelArg
798
+ sel_arg_create(agooErr err, const char *name, gqlValue value, gqlVar var) {
799
+ gqlSelArg arg;
800
+
801
+ if (NULL == (arg = (gqlSelArg)AGOO_MALLOC(sizeof(struct _gqlSelArg)))) {
802
+ agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a selection field argument.");
803
+ } else {
804
+ arg->next = NULL;
805
+ if (NULL == (arg->name = AGOO_STRDUP(name))) {
806
+ agoo_err_set(err, AGOO_ERR_MEMORY, "strdup of field name failed. %s:%d", __FILE__, __LINE__);
807
+ return NULL;
808
+ }
809
+ arg->var = var;
810
+ arg->value = value;
811
+ }
812
+ return arg;
813
+ }
814
+ static int
815
+ make_sel_arg(agooErr err, agooDoc doc, gqlDoc gdoc, gqlOp op, gqlSel sel) {
816
+ char name[256];
817
+ gqlValue value = NULL;
818
+ gqlVar var = NULL;
819
+ gqlSelArg arg;
820
+
821
+ agoo_doc_skip_white(doc);
822
+ if (0 == read_name(err, doc, name, sizeof(name))) {
823
+ return err->code;
824
+ }
825
+ agoo_doc_skip_white(doc);
826
+ if (':' != *doc->cur) {
827
+ return agoo_doc_err(doc, err, "Expected :");
828
+ }
829
+ doc->cur++;
830
+ agoo_doc_skip_white(doc);
831
+ if ('$' == *doc->cur && NULL != op) {
832
+ char var_name[256];
833
+
834
+ doc->cur++;
835
+ if (0 == read_name(err, doc, var_name, sizeof(var_name))) {
836
+ return err->code;
837
+ }
838
+ for (var = op->vars; NULL != var; var = var->next) {
839
+ if (0 == strcmp(var_name, var->name)) {
840
+ break;
841
+ }
842
+ }
843
+ if (NULL == var) {
844
+ for (var = gdoc->vars; NULL != var; var = var->next) {
845
+ if (0 == strcmp(var_name, var->name)) {
846
+ break;
847
+ }
848
+ }
849
+ }
850
+ if (NULL == var) {
851
+ return agoo_doc_err(doc, err, "variable $%s not defined for operation or document", var_name);
852
+ }
853
+ } else if (NULL == (value = agoo_doc_read_value(err, doc, NULL))) {
854
+ return err->code;
855
+ }
856
+ if (NULL == (arg = sel_arg_create(err, name, value, var))) {
857
+ return err->code;
858
+ }
859
+ if (NULL == sel->args) {
860
+ sel->args = arg;
861
+ } else {
862
+ gqlSelArg a;
863
+
864
+ for (a = sel->args; NULL != a->next; a = a->next) {
865
+ }
866
+ a->next = arg;
867
+ }
868
+ return AGOO_ERR_OK;
869
+ }
870
+
871
+ gqlVar
872
+ gql_op_var_create(agooErr err, const char *name, gqlType type, gqlValue value) {
873
+ gqlVar var;
874
+
875
+ if (NULL == (var = (gqlVar)AGOO_MALLOC(sizeof(struct _gqlVar)))) {
876
+ agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a operation variable.");
877
+ } else {
878
+ var->next = NULL;
879
+ if (NULL == (var->name = AGOO_STRDUP(name))) {
880
+ agoo_err_set(err, AGOO_ERR_MEMORY, "strdup of variable name failed. %s:%d", __FILE__, __LINE__);
881
+ return NULL;
882
+ }
883
+ var->type = type;
884
+ var->value = value;
885
+ }
886
+ return var;
887
+ }
888
+
889
+ static int
890
+ make_op_var(agooErr err, agooDoc doc, gqlOp op) {
891
+ char name[256];
892
+ gqlType type;
893
+ bool ignore;
894
+ gqlValue value = NULL;
895
+ gqlVar var;
896
+
897
+ agoo_doc_skip_white(doc);
898
+ if ('$' != *doc->cur) {
899
+ return agoo_doc_err(doc, err, "Expected $");
900
+ }
901
+ doc->cur++;
902
+ if (0 == read_name(err, doc, name, sizeof(name))) {
903
+ return err->code;
904
+ }
905
+ agoo_doc_skip_white(doc);
906
+ if (':' != *doc->cur) {
907
+ return agoo_doc_err(doc, err, "Expected :");
908
+ }
909
+ doc->cur++;
910
+
911
+ if (AGOO_ERR_OK != read_type(err, doc, &type, &ignore)) {
912
+ return err->code;
913
+ }
914
+ agoo_doc_skip_white(doc);
915
+
916
+ if ('=' == *doc->cur) {
917
+ doc->cur++;
918
+ if (NULL == (value = agoo_doc_read_value(err, doc, type))) {
919
+ return err->code;
920
+ }
921
+ }
922
+ if (NULL == (var = gql_op_var_create(err, name, type, value))) {
923
+ return err->code;
924
+ }
925
+ if (NULL == op->vars) {
926
+ op->vars = var;
927
+ } else {
928
+ gqlVar v;
929
+
930
+ for (v = op->vars; NULL != v->next; v = v->next) {
931
+ }
932
+ v->next = var;
933
+ }
934
+ return AGOO_ERR_OK;
935
+ }
936
+
937
+ static gqlSel
938
+ sel_create(agooErr err, const char *alias, const char *name, const char *frag) {
939
+ gqlSel sel;
940
+
941
+ if (NULL == (sel = (gqlSel)AGOO_MALLOC(sizeof(struct _gqlSel)))) {
942
+ agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a selection set.");
943
+ } else {
944
+ sel->next = NULL;
945
+ if (NULL == name) {
946
+ sel->name = NULL;
947
+ } else {
948
+ if (NULL == (sel->name = AGOO_STRDUP(name))) {
949
+ agoo_err_set(err, AGOO_ERR_MEMORY, "strdup of selection name failed. %s:%d", __FILE__, __LINE__);
950
+ return NULL;
951
+ }
952
+ }
953
+ if (NULL == alias) {
954
+ sel->alias = NULL;
955
+ } else {
956
+ if (NULL == (sel->alias = AGOO_STRDUP(alias))) {
957
+ agoo_err_set(err, AGOO_ERR_MEMORY, "strdup of selection alias failed. %s:%d", __FILE__, __LINE__);
958
+ return NULL;
959
+ }
960
+ }
961
+ if (NULL == frag) {
962
+ sel->frag = NULL;
963
+ } else {
964
+ if (NULL == (sel->frag = AGOO_STRDUP(frag))) {
965
+ agoo_err_set(err, AGOO_ERR_MEMORY, "strdup of selection fragment failed. %s:%d", __FILE__, __LINE__);
966
+ return NULL;
967
+ }
968
+ }
969
+ sel->type = NULL;
970
+ sel->dir = NULL;
971
+ sel->args = NULL;
972
+ sel->sels = NULL;
973
+ sel->inline_frag = NULL;
974
+ }
975
+ return sel;
976
+ }
977
+
978
+ static gqlSel
979
+ make_sel_inline(agooErr err, agooDoc doc, gqlDoc gdoc, gqlOp op) {
980
+ gqlSel sel = NULL;
981
+
982
+ doc->cur += 3;
983
+ agoo_doc_skip_white(doc);
984
+ if ('o' == *doc->cur && 'n' == doc->cur[1]) { // inline fragment
985
+ char type_name[256];
986
+
987
+ doc->cur += 2;
988
+ agoo_doc_skip_white(doc);
989
+ if (0 == read_name(err, doc, type_name, sizeof(type_name))) {
990
+ return NULL;
991
+ }
992
+ if (NULL == (sel = sel_create(err, NULL, NULL, NULL))) {
993
+ return NULL;
994
+ }
995
+ if (NULL == (sel->inline_frag = gql_fragment_create(err, NULL, gql_assure_type(err, type_name)))) {
996
+ return NULL;
997
+ }
998
+ if (AGOO_ERR_OK != extract_dir_use(err, doc, &sel->dir)) {
999
+ return NULL;
1000
+ }
1001
+ agoo_doc_skip_white(doc);
1002
+ if ('{' == *doc->cur) {
1003
+ doc->cur++;
1004
+ while (doc->cur < doc->end) {
1005
+ agoo_doc_skip_white(doc);
1006
+ if ('}' == *doc->cur) {
1007
+ doc->cur++;
1008
+ break;
1009
+ }
1010
+ if (AGOO_ERR_OK != make_sel(err, doc, gdoc, op, &sel->inline_frag->sels)) {
1011
+ return NULL;
1012
+ }
1013
+ }
1014
+ if (doc->end <= doc->cur && '}' != doc->cur[-1]) {
1015
+ agoo_doc_err(doc, err, "Expected a }");
1016
+ return NULL;
1017
+ }
1018
+ }
1019
+ } else if ('@' == *doc->cur) {
1020
+ if (NULL == (sel = sel_create(err, NULL, NULL, NULL))) {
1021
+ return NULL;
1022
+ }
1023
+ if (NULL == (sel->inline_frag = gql_fragment_create(err, NULL, NULL))) {
1024
+ return NULL;
1025
+ }
1026
+ if (AGOO_ERR_OK != extract_dir_use(err, doc, &sel->inline_frag->dir)) {
1027
+ return NULL;
1028
+ }
1029
+ agoo_doc_skip_white(doc);
1030
+ if ('{' == *doc->cur) {
1031
+ doc->cur++;
1032
+ while (doc->cur < doc->end) {
1033
+ agoo_doc_skip_white(doc);
1034
+ if ('}' == *doc->cur) {
1035
+ doc->cur++;
1036
+ break;
1037
+ }
1038
+ if (AGOO_ERR_OK != make_sel(err, doc, gdoc, op, &sel->inline_frag->sels)) {
1039
+ return NULL;
1040
+ }
1041
+ }
1042
+ if (doc->end <= doc->cur && '}' != doc->cur[-1]) {
1043
+ agoo_doc_err(doc, err, "Expected a }");
1044
+ return NULL;
1045
+ }
1046
+ }
1047
+ } else { // reference to a fragment
1048
+ char frag_name[256];
1049
+
1050
+ if (0 == read_name(err, doc, frag_name, sizeof(frag_name))) {
1051
+ return NULL;
1052
+ }
1053
+ sel = sel_create(err, NULL, NULL, frag_name);
1054
+ }
1055
+ return sel;
1056
+ }
1057
+
1058
+ static int
1059
+ make_sel(agooErr err, agooDoc doc, gqlDoc gdoc, gqlOp op, gqlSel *parentp) {
1060
+ char alias[256];
1061
+ char name[256];
1062
+ gqlSel sel = NULL;
1063
+
1064
+ if (NULL == op && NULL == parentp) {
1065
+ return agoo_doc_err(doc, err, "Fields can only be in a fragment, operation, or another field.");
1066
+ }
1067
+ *alias = '\0';
1068
+ *name = '\0';
1069
+ agoo_doc_skip_white(doc);
1070
+ if ('.' == *doc->cur && '.' == doc->cur[1] && '.' == doc->cur[2]) {
1071
+ if (NULL == (sel = make_sel_inline(err, doc, gdoc, op))) {
1072
+ return err->code;
1073
+ }
1074
+ } else {
1075
+ if (0 == read_name(err, doc, alias, sizeof(alias))) {
1076
+ return err->code;
1077
+ }
1078
+ agoo_doc_skip_white(doc);
1079
+ if (':' == *doc->cur) {
1080
+ doc->cur++;
1081
+ agoo_doc_skip_white(doc);
1082
+ if (0 == read_name(err, doc, name, sizeof(name))) {
1083
+ return err->code;
1084
+ }
1085
+ agoo_doc_skip_white(doc);
1086
+ }
1087
+
1088
+ if ('\0' == *name) { // no alias
1089
+ sel = sel_create(err, NULL, alias, NULL);
1090
+ } else {
1091
+ sel = sel_create(err, alias, name, NULL);
1092
+ }
1093
+ }
1094
+ if (NULL == sel) {
1095
+ return err->code;
1096
+ }
1097
+ if (NULL == parentp) {
1098
+ if (NULL == op->sels) {
1099
+ op->sels = sel;
1100
+ } else {
1101
+ gqlSel s;
1102
+
1103
+ for (s = op->sels; NULL != s->next; s = s->next) {
1104
+ }
1105
+ s->next = sel;
1106
+ }
1107
+ } else {
1108
+ if (NULL == *parentp) {
1109
+ *parentp = sel;
1110
+ } else {
1111
+ gqlSel s;
1112
+
1113
+ for (s = *parentp; NULL != s->next; s = s->next) {
1114
+ }
1115
+ s->next = sel;
1116
+ }
1117
+ }
1118
+ if (NULL == sel->frag && '(' == *doc->cur) {
1119
+ doc->cur++;
1120
+ while (doc->cur < doc->end) {
1121
+ agoo_doc_skip_white(doc);
1122
+ if (')' == *doc->cur) {
1123
+ doc->cur++;
1124
+ break;
1125
+ }
1126
+ if (AGOO_ERR_OK != make_sel_arg(err, doc, gdoc, op, sel)) {
1127
+ return err->code;
1128
+ }
1129
+ }
1130
+ agoo_doc_skip_white(doc);
1131
+ }
1132
+ if (AGOO_ERR_OK != extract_dir_use(err, doc, &sel->dir)) {
1133
+ return err->code;
1134
+ }
1135
+ if (NULL == sel->frag && '{' == *doc->cur) {
1136
+ doc->cur++;
1137
+ while (doc->cur < doc->end) {
1138
+ agoo_doc_skip_white(doc);
1139
+ if ('}' == *doc->cur) {
1140
+ doc->cur++;
1141
+ break;
1142
+ }
1143
+ if (AGOO_ERR_OK != make_sel(err, doc, gdoc, op, &sel->sels)) {
1144
+ return err->code;
1145
+ }
1146
+ }
1147
+ if (doc->end <= doc->cur && '}' != doc->cur[-1]) {
1148
+ return agoo_doc_err(doc, err, "Expected a }");
1149
+ }
1150
+ }
1151
+ return AGOO_ERR_OK;
1152
+ }
1153
+
1154
+
1155
+ static int
1156
+ make_op(agooErr err, agooDoc doc, gqlDoc gdoc) {
1157
+ char name[256];
1158
+ const char *start;
1159
+ gqlOpKind kind;
1160
+ gqlOp op;
1161
+ size_t nlen;
1162
+
1163
+ agoo_doc_skip_white(doc);
1164
+ start = doc->cur;
1165
+ agoo_doc_read_token(doc);
1166
+ if (doc->cur == start ||
1167
+ (5 == (doc->cur - start) && 0 == strncmp(query_str, start, sizeof(query_str) - 1))) {
1168
+ kind = GQL_QUERY;
1169
+ } else if (8 == (doc->cur - start) && 0 == strncmp(mutation_str, start, sizeof(mutation_str) - 1)) {
1170
+ kind = GQL_MUTATION;
1171
+ } else if (12 == (doc->cur - start) && 0 == strncmp(subscription_str, start, sizeof(subscription_str) - 1)) {
1172
+ kind = GQL_SUBSCRIPTION;
1173
+ } else {
1174
+ return agoo_doc_err(doc, err, "Invalid operation type");
1175
+ }
1176
+ agoo_doc_skip_white(doc);
1177
+ start = doc->cur;
1178
+ agoo_doc_read_token(doc);
1179
+ nlen = doc->cur - start;
1180
+ if (sizeof(name) <= nlen) {
1181
+ agoo_doc_err(doc, err, "Name too long");
1182
+ return err->code;
1183
+ }
1184
+ if (0 < nlen) {
1185
+ strncpy(name, start, nlen);
1186
+ }
1187
+ name[nlen] = '\0';
1188
+ if (NULL == (op = gql_op_create(err, name, kind))) {
1189
+ return err->code;
1190
+ }
1191
+ agoo_doc_skip_white(doc);
1192
+ if ('(' == *doc->cur) {
1193
+ doc->cur++;
1194
+ while (doc->cur < doc->end) {
1195
+ agoo_doc_skip_white(doc);
1196
+ if (')' == *doc->cur) {
1197
+ doc->cur++;
1198
+ break;
1199
+ }
1200
+ if (AGOO_ERR_OK != make_op_var(err, doc, op)) {
1201
+ return err->code;
1202
+ }
1203
+ }
1204
+ agoo_doc_skip_white(doc);
1205
+ }
1206
+ if (AGOO_ERR_OK != extract_dir_use(err, doc, &op->dir)) {
1207
+ return err->code;
1208
+ }
1209
+ if ('{' != *doc->cur) {
1210
+ return agoo_doc_err(doc, err, "Expected a {");
1211
+ }
1212
+ doc->cur++;
1213
+ while (doc->cur < doc->end) {
1214
+ agoo_doc_skip_white(doc);
1215
+ if ('}' == *doc->cur) {
1216
+ doc->cur++;
1217
+ break;
1218
+ }
1219
+ if (AGOO_ERR_OK != make_sel(err, doc, gdoc, op, NULL)) {
1220
+ return err->code;
1221
+ }
1222
+ }
1223
+ if (doc->end <= doc->cur && '}' != doc->cur[-1]) {
1224
+ return agoo_doc_err(doc, err, "Expected a }");
1225
+ }
1226
+ if (NULL == gdoc->ops) {
1227
+ gdoc->ops = op;
1228
+ } else {
1229
+ gqlOp o;
1230
+
1231
+ for (o = gdoc->ops; NULL != o->next; o = o->next) {
1232
+ }
1233
+ o->next = op;
1234
+ }
1235
+ return AGOO_ERR_OK;
1236
+ }
1237
+
1238
+ static int
1239
+ make_fragment(agooErr err, agooDoc doc, gqlDoc gdoc) {
1240
+ char name[256];
1241
+ char type_name[256];
1242
+ const char *start;
1243
+ gqlFrag frag;
1244
+
1245
+ agoo_doc_skip_white(doc);
1246
+ start = doc->cur;
1247
+ agoo_doc_read_token(doc);
1248
+ if (8 != (doc->cur - start) || 0 != strncmp("fragment", start, 8)) {
1249
+ agoo_doc_err(doc, err, "Expected the key word 'fragment'.");
1250
+ }
1251
+ agoo_doc_skip_white(doc);
1252
+ if (0 == read_name(err, doc, name, sizeof(name))) {
1253
+ return err->code;
1254
+ }
1255
+ agoo_doc_skip_white(doc);
1256
+ start = doc->cur;
1257
+ agoo_doc_read_token(doc);
1258
+ if (2 != (doc->cur - start) || 0 != strncmp("on", start, 2)) {
1259
+ agoo_doc_err(doc, err, "Expected the key word 'on'.");
1260
+ }
1261
+ agoo_doc_skip_white(doc);
1262
+ if (0 == read_name(err, doc, type_name, sizeof(type_name))) {
1263
+ return err->code;
1264
+ }
1265
+ agoo_doc_skip_white(doc);
1266
+
1267
+ if (NULL == (frag = gql_fragment_create(err, name, gql_assure_type(err, type_name)))) {
1268
+ return err->code;
1269
+ }
1270
+ if (AGOO_ERR_OK != extract_dir_use(err, doc, &frag->dir)) {
1271
+ return err->code;
1272
+ }
1273
+ if ('{' != *doc->cur) {
1274
+ return agoo_doc_err(doc, err, "Expected a {");
1275
+ }
1276
+ doc->cur++;
1277
+
1278
+ while (doc->cur < doc->end) {
1279
+ agoo_doc_skip_white(doc);
1280
+ if ('}' == *doc->cur) {
1281
+ doc->cur++;
1282
+ break;
1283
+ }
1284
+ if (AGOO_ERR_OK != make_sel(err, doc, gdoc, NULL, &frag->sels)) {
1285
+ return err->code;
1286
+ }
1287
+ }
1288
+ if (doc->end <= doc->cur && '}' != doc->cur[-1]) {
1289
+ return agoo_doc_err(doc, err, "Expected a }");
1290
+ }
1291
+ if (NULL == gdoc->frags) {
1292
+ gdoc->frags = frag;
1293
+ } else {
1294
+ gqlFrag f;
1295
+
1296
+ for (f = gdoc->frags; NULL != f->next; f = f->next) {
1297
+ }
1298
+ f->next = frag;
1299
+ }
1300
+ return AGOO_ERR_OK;
1301
+ }
1302
+
1303
+ static gqlType
1304
+ lookup_field_type(gqlType type, const char *field, bool qroot) {
1305
+ gqlType ftype = NULL;
1306
+
1307
+ switch (type->kind) {
1308
+ case GQL_OBJECT:
1309
+ case GQL_INPUT:
1310
+ case GQL_INTERFACE: {
1311
+ gqlField f;
1312
+
1313
+ for (f = type->fields; NULL != f; f = f->next) {
1314
+ if (0 == strcmp(field, f->name)) {
1315
+ ftype = f->type;
1316
+ break;
1317
+ }
1318
+ }
1319
+ if (NULL == ftype) {
1320
+ if (0 == strcmp("__typename", field)) {
1321
+ ftype = &gql_string_type;
1322
+ } else if (qroot) {
1323
+ if (0 == strcmp("__type", field)) {
1324
+ ftype = gql_type_get("__Type");
1325
+ } else if (0 == strcmp("__schema", field)) {
1326
+ ftype = gql_type_get("__Schema");
1327
+ }
1328
+ }
1329
+ }
1330
+ break;
1331
+ }
1332
+ case GQL_LIST:
1333
+ ftype = lookup_field_type(type->base, field, false);
1334
+ break;
1335
+ case GQL_UNION: // Can not be used directly for query type determinations.
1336
+ default:
1337
+ break;
1338
+ }
1339
+ return ftype;
1340
+ }
1341
+
1342
+ static int
1343
+ sel_set_type(agooErr err, gqlType type, gqlSel sels, bool qroot) {
1344
+ gqlSel sel;
1345
+
1346
+ for (sel = sels; NULL != sel; sel = sel->next) {
1347
+ if (NULL == sel->name) { // inline or fragment
1348
+ sel->type = type;
1349
+ if (NULL != sel->inline_frag) {
1350
+ gqlType ftype = type;
1351
+
1352
+ if (NULL != sel->inline_frag->on) {
1353
+ ftype = sel->inline_frag->on;
1354
+ }
1355
+ if (AGOO_ERR_OK != sel_set_type(err, ftype, sel->inline_frag->sels, false)) {
1356
+ return err->code;
1357
+ }
1358
+ }
1359
+ } else {
1360
+ if (NULL == (sel->type = lookup_field_type(type, sel->name, qroot))) {
1361
+ return agoo_err_set(err, AGOO_ERR_EVAL, "Failed to determine the type for %s.", sel->name);
1362
+ }
1363
+ }
1364
+ if (NULL != sel->sels) {
1365
+ if (AGOO_ERR_OK != sel_set_type(err, sel->type, sel->sels, false)) {
1366
+ return err->code;
1367
+ }
1368
+ }
1369
+ }
1370
+ return AGOO_ERR_OK;
1371
+ }
1372
+
1373
+ static int
1374
+ validate_doc(agooErr err, gqlDoc doc) {
1375
+ gqlOp op;
1376
+ gqlType schema;
1377
+ gqlType type = NULL;
1378
+ gqlFrag frag;
1379
+ int cnt;
1380
+
1381
+ if (NULL == (schema = gql_type_get("schema"))) {
1382
+ return agoo_err_set(err, AGOO_ERR_EVAL, "No root (schema) type defined.");
1383
+ }
1384
+ for (frag = doc->frags; NULL != frag; frag = frag->next) {
1385
+ if (AGOO_ERR_OK != sel_set_type(err, frag->on, frag->sels, false)) {
1386
+ return err->code;
1387
+ }
1388
+ }
1389
+ cnt = 0;
1390
+ for (op = doc->ops; NULL != op; op = op->next) {
1391
+ if (NULL == op->name) {
1392
+ cnt++;
1393
+ if (1 < cnt) {
1394
+ return agoo_err_set(err, AGOO_ERR_EVAL, "Multiple un-named operation.");
1395
+ }
1396
+ } else {
1397
+ gqlOp o2 = op->next;
1398
+
1399
+ for (; NULL != o2; o2 = o2->next) {
1400
+ if (NULL == o2->name) {
1401
+ continue;
1402
+ }
1403
+ if (0 == strcmp(o2->name, op->name)) {
1404
+ return agoo_err_set(err, AGOO_ERR_EVAL, "Multiple operation named '%s'.", op->name);
1405
+ }
1406
+ }
1407
+ }
1408
+ }
1409
+ for (op = doc->ops; NULL != op; op = op->next) {
1410
+ switch (op->kind) {
1411
+ case GQL_QUERY:
1412
+ type = lookup_field_type(schema, query_str, false);
1413
+ break;
1414
+ case GQL_MUTATION:
1415
+ type = lookup_field_type(schema, mutation_str, false);
1416
+ break;
1417
+ case GQL_SUBSCRIPTION:
1418
+ type = lookup_field_type(schema, subscription_str, false);
1419
+ break;
1420
+ default:
1421
+ break;
1422
+ }
1423
+ if (NULL == type) {
1424
+ return agoo_err_set(err, AGOO_ERR_EVAL, "Not a supported operation type.");
1425
+ }
1426
+ if (AGOO_ERR_OK != sel_set_type(err, type, op->sels, GQL_QUERY == op->kind)) {
1427
+ return err->code;
1428
+ }
1429
+ }
1430
+ return AGOO_ERR_OK;
1431
+ }
1432
+
1433
+ gqlDoc
1434
+ sdl_parse_doc(agooErr err, const char *str, int len, gqlVar vars) {
1435
+ struct _agooDoc doc;
1436
+ gqlDoc gdoc = NULL;
1437
+
1438
+ agoo_doc_init(&doc, str, len);
1439
+ if (NULL == (gdoc = gql_doc_create(err))) {
1440
+ return NULL;
1441
+ }
1442
+ gdoc->vars = vars;
1443
+ while (doc.cur < doc.end) {
1444
+ agoo_doc_next_token(&doc);
1445
+ if (doc.end <= doc.cur) {
1446
+ break;
1447
+ }
1448
+ switch (*doc.cur) {
1449
+ case '{': // no name query
1450
+ case 'q':
1451
+ case 'm':
1452
+ case 's':
1453
+ if (AGOO_ERR_OK != make_op(err, &doc, gdoc)) {
1454
+ gql_doc_destroy(gdoc);
1455
+ return NULL;
1456
+ }
1457
+ break;
1458
+ case 'f':
1459
+ if (AGOO_ERR_OK != make_fragment(err, &doc, gdoc)) {
1460
+ gql_doc_destroy(gdoc);
1461
+ return NULL;
1462
+ }
1463
+ break;
1464
+ case '\0':
1465
+ goto DONE;
1466
+ default:
1467
+ agoo_doc_err(&doc, err, "unexpected character");
1468
+ gql_doc_destroy(gdoc);
1469
+ return NULL;
1470
+ }
1471
+ }
1472
+ DONE:
1473
+ if (AGOO_ERR_OK != validate_doc(err, gdoc)) {
1474
+ gql_doc_destroy(gdoc);
1475
+ return NULL;
1476
+ }
1477
+ return gdoc;
1478
+ }