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/rresponse.c
CHANGED
@@ -17,20 +17,17 @@ response_free(void *ptr) {
|
|
17
17
|
|
18
18
|
while (NULL != (h = res->headers)) {
|
19
19
|
res->headers = h->next;
|
20
|
-
|
20
|
+
AGOO_FREE(h);
|
21
21
|
xfree(h);
|
22
22
|
}
|
23
|
-
|
24
|
-
|
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 =
|
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 =
|
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 =
|
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
|
-
|
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)
|
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;
|
data/ext/agoo/rserver.c
CHANGED
@@ -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
|
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
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
agoo_server.hooks =
|
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
|
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
|
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
|
762
|
+
agooReq req;
|
760
763
|
|
761
764
|
while (agoo_server.active) {
|
762
|
-
if (NULL != (req = (agooReq)agoo_queue_pop(&agoo_server.eval_queue, 0.
|
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*)
|
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
|
-
|
809
|
-
free(the_rserver.eval_threads);
|
810
|
+
AGOO_FREE(the_rserver.eval_threads);
|
810
811
|
the_rserver.eval_threads = NULL;
|
811
812
|
}
|
812
813
|
}
|
data/ext/agoo/sdl.c
CHANGED
@@ -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
|
-
|
13
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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, ¬_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,
|
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,
|
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
|
-
|
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 (
|
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,
|
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
|
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
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
-
|
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
|
-
|
298
|
+
size_t dlen;
|
128
299
|
size_t nlen;
|
129
300
|
bool required = false;
|
130
301
|
gqlValue dv = NULL;
|
131
|
-
|
132
|
-
|
133
|
-
|
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,
|
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
|
-
|
213
|
-
|
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
|
-
|
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 !=
|
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
|
-
|
263
|
-
|
264
|
-
|
265
|
-
const char
|
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
|
-
|
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
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
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
|
-
|
312
|
-
|
313
|
-
|
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
|
+
}
|