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
@@ -20,14 +20,22 @@ typedef struct _gqlLink {
20
20
  typedef struct _gqlValue {
21
21
  struct _gqlType *type;
22
22
  union {
23
- char *url;
24
23
  int32_t i;
25
24
  int64_t i64;
26
25
  double f;
27
26
  bool b;
28
27
  int64_t time;
29
- char *str;
30
- char str16[16];
28
+ union {
29
+ struct {
30
+ char alloced;
31
+ char a[15];
32
+ };
33
+ struct {
34
+ char x; // same memory location as kind
35
+ char pad[7];
36
+ const char *ptr;
37
+ };
38
+ } str;
31
39
  struct {
32
40
  uint64_t hi;
33
41
  uint64_t lo;
@@ -42,11 +50,16 @@ typedef struct _gqlValue {
42
50
  extern int gql_value_init(agooErr err);
43
51
 
44
52
  extern void gql_value_destroy(gqlValue value);
53
+ extern gqlValue gql_value_dup(agooErr err, gqlValue value);
54
+
55
+ extern gqlLink gql_link_create(agooErr err, const char *key, gqlValue value);
56
+ extern void gql_link_destroy(gqlLink link);
45
57
 
46
58
  extern gqlValue gql_int_create(agooErr err, int32_t i);
47
59
  extern gqlValue gql_i64_create(agooErr err, int64_t i);
48
60
  extern gqlValue gql_string_create(agooErr err, const char *str, int len);
49
- extern gqlValue gql_url_create(agooErr err, const char *url, int len);
61
+ extern gqlValue gql_token_create(agooErr err, const char *str, int len, struct _gqlType *type);
62
+ extern gqlValue gql_var_create(agooErr err, const char *str, int len);
50
63
  extern gqlValue gql_bool_create(agooErr err, bool b);
51
64
  extern gqlValue gql_float_create(agooErr err, double f);
52
65
  extern gqlValue gql_time_create(agooErr err, int64_t t);
@@ -54,7 +67,7 @@ extern gqlValue gql_time_str_create(agooErr err, const char *str, int len);
54
67
  extern gqlValue gql_uuid_create(agooErr err, uint64_t hi, uint64_t lo);
55
68
  extern gqlValue gql_uuid_str_create(agooErr err, const char *str, int len);
56
69
  extern gqlValue gql_null_create(agooErr err);
57
- extern gqlValue gql_list_create(agooErr err, struct _gqlType *itemType);
70
+ extern gqlValue gql_list_create(agooErr err, struct _gqlType *item_type);
58
71
  extern gqlValue gql_object_create(agooErr err);
59
72
 
60
73
  extern int gql_list_append(agooErr err, gqlValue list, gqlValue item);
@@ -64,7 +77,7 @@ extern int gql_object_set(agooErr err, gqlValue obj, const char *key, gqlValue i
64
77
  extern void gql_int_set(gqlValue value, int32_t i);
65
78
  extern void gql_i64_set(gqlValue value, int64_t i);
66
79
  extern int gql_string_set(agooErr err, gqlValue value, const char *str, int len);
67
- extern int gql_url_set(agooErr err, gqlValue value, const char *url, int len);
80
+ extern int gql_token_set(agooErr err, gqlValue value, const char *str, int len);
68
81
  extern void gql_bool_set(gqlValue value, bool b);
69
82
  extern void gql_float_set(gqlValue value, double f);
70
83
  extern void gql_time_set(gqlValue value, int64_t t);
@@ -73,7 +86,15 @@ extern void gql_uuid_set(gqlValue value, uint64_t hi, uint64_t lo);
73
86
  extern int gql_uuid_str_set(agooErr err, gqlValue value, const char *str, int len);
74
87
  extern void gql_null_set(gqlValue value);
75
88
 
89
+ extern const char* gql_string_get(gqlValue value);
90
+
76
91
  extern agooText gql_value_json(agooText text, gqlValue value, int indent, int depth);
92
+ extern agooText gql_value_sdl(agooText text, gqlValue value, int indent, int depth);
93
+
94
+ //extern agooText gql_object_to_json(agooText text, gqlValue value, int indent, int depth);
95
+ extern agooText gql_object_to_sdl(agooText text, gqlValue value, int indent, int depth);
96
+
97
+ extern int gql_value_convert(agooErr err, gqlValue value, struct _gqlType *type);
77
98
 
78
99
  extern struct _gqlType gql_null_type;
79
100
  extern struct _gqlType gql_int_type;
@@ -82,7 +103,8 @@ extern struct _gqlType gql_bool_type;
82
103
  extern struct _gqlType gql_float_type;
83
104
  extern struct _gqlType gql_time_type;
84
105
  extern struct _gqlType gql_uuid_type;
85
- extern struct _gqlType gql_url_type;
86
106
  extern struct _gqlType gql_string_type;
107
+ extern struct _gqlType gql_token_type; // used for enum values
108
+ extern struct _gqlType gql_var_type; // used for variable keys
87
109
 
88
110
  #endif // AGOO_GQLVALUE_H
@@ -44,11 +44,12 @@ static uint8_t name_chars[256] = "\
44
44
 
45
45
  static const char spaces[16] = " ";
46
46
 
47
- static gqlType query_type = NULL;
48
- static gqlType mutation_type = NULL;
49
- static gqlType subscription_type = NULL;
47
+ gqlDir gql_directives = NULL;
48
+
50
49
  static gqlType schema_type = NULL;
51
- static gqlDir directives = NULL;
50
+ static bool inited = false;
51
+
52
+ static void gql_frag_destroy(gqlFrag frag);
52
53
 
53
54
  static uint64_t
54
55
  calc_hash(const char *name) {
@@ -70,89 +71,184 @@ get_bucketp(uint64_t h) {
70
71
  return buckets + (BUCKET_MASK & (h ^ (h << 5) ^ (h >> 7)));
71
72
  }
72
73
 
73
- static void
74
- type_destroy(gqlType type) {
75
- if (!type->core) {
76
- free((char*)type->name);
77
- free((char*)type->desc);
78
- switch (type->kind) {
79
- case GQL_OBJECT:
80
- case GQL_FRAG:
81
- case GQL_INTERFACE:
82
- case GQL_INPUT: {
83
- gqlField f;
84
- gqlArg a;
85
-
86
- while (NULL != (f = type->fields)) {
87
- type->fields = f->next;
88
- while (NULL != (a = f->args)) {
89
- f->args = a->next;
90
- free((char*)a->name);
91
- free((char*)a->type_name);
92
- free((char*)a->desc);
93
- gql_value_destroy(a->default_value);
94
- DEBUG_FREE(mem_graphql_arg, a);
95
- free(a);
96
- }
97
- free((char*)f->name);
98
- free((char*)f->desc);
99
- DEBUG_FREE(mem_graphql_field, f);
100
- free(f);
74
+ static const char*
75
+ kind_string(gqlKind kind) {
76
+ switch (kind) {
77
+ case GQL_UNDEF: return "undefined";
78
+ case GQL_OBJECT: return "object";
79
+ case GQL_INPUT: return "input";
80
+ case GQL_UNION: return "union";
81
+ case GQL_INTERFACE: return "interface";
82
+ case GQL_ENUM: return "enum";
83
+ case GQL_SCALAR: return "scalar";
84
+ case GQL_LIST: return "list";
85
+ default: break;
86
+ }
87
+ return "unknown";
88
+ }
89
+
90
+ static char*
91
+ alloc_string(agooErr err, const char *s, size_t len) {
92
+ char *a = NULL;
93
+
94
+ if (NULL != s) {
95
+ if (0 >= len) {
96
+ if (NULL == (a = AGOO_STRDUP(s))) {
97
+ agoo_err_set(err, AGOO_ERR_MEMORY, "strdup() failed.");
98
+ }
99
+ len = strlen(a);
100
+ } else {
101
+ if (NULL == (a = AGOO_STRNDUP(s, len))) {
102
+ agoo_err_set(err, AGOO_ERR_MEMORY, "strndup() of length %d failed.", len);
101
103
  }
102
- free(type->interfaces);
103
- break;
104
104
  }
105
- case GQL_UNION: {
106
- gqlTypeLink link;
105
+ }
106
+ return a;
107
+ }
107
108
 
108
- while (NULL != (link = type->types)) {
109
- type->types = link->next;
110
- free(link->name);
111
- free(link);
112
- }
113
- break;
109
+ static void
110
+ free_dir_uses(gqlDirUse uses) {
111
+ gqlDirUse u;
112
+ gqlLink link;
113
+
114
+ while (NULL != (u = uses)) {
115
+ uses = u->next;
116
+ while (NULL != (link = u->args)) {
117
+ u->args = link->next;
118
+ gql_link_destroy(link);
114
119
  }
115
- case GQL_ENUM: {
116
- gqlStrLink link;
120
+ AGOO_FREE(u);
121
+ }
122
+ }
117
123
 
118
- while (NULL != (link = type->choices)) {
119
- type->choices = link->next;
120
- free(link->str);
121
- free(link);
122
- }
123
- break;
124
+ static void
125
+ arg_destroy(gqlArg a) {
126
+ AGOO_FREE((char*)a->name);
127
+ AGOO_FREE((char*)a->desc);
128
+ if (NULL != a->default_value) {
129
+ gql_value_destroy(a->default_value);
130
+ }
131
+ free_dir_uses(a->dir);
132
+
133
+ AGOO_FREE(a);
134
+ }
135
+
136
+ static void
137
+ field_destroy(gqlField f) {
138
+ gqlArg a;
139
+
140
+ AGOO_FREE((char*)f->name);
141
+ AGOO_FREE((char*)f->desc);
142
+ while (NULL != (a = f->args)) {
143
+ f->args = a->next;
144
+ arg_destroy(a);
145
+ }
146
+ free_dir_uses(f->dir);
147
+ if (NULL != f->default_value) {
148
+ gql_value_destroy(f->default_value);
149
+ }
150
+ AGOO_FREE(f);
151
+ }
152
+
153
+ static void
154
+ type_clean(gqlType type) {
155
+ AGOO_FREE((char*)type->desc);
156
+ type->desc = NULL;
157
+ free_dir_uses(type->dir);
158
+ type->dir = NULL;
159
+
160
+ switch (type->kind) {
161
+ case GQL_OBJECT:
162
+ case GQL_INTERFACE: {
163
+ gqlField f;
164
+ gqlTypeLink link;
165
+
166
+ while (NULL != (f = type->fields)) {
167
+ type->fields = f->next;
168
+ field_destroy(f);
124
169
  }
125
- default:
126
- break;
170
+ while (NULL != (link = type->interfaces)) {
171
+ type->interfaces = link->next;
172
+ AGOO_FREE(link);
127
173
  }
128
- DEBUG_FREE(mem_graphql_type, type);
129
- free(type);
174
+ break;
175
+ }
176
+ case GQL_INPUT: {
177
+ gqlArg a;
178
+
179
+ while (NULL != (a = type->args)) {
180
+ type->args = a->next;
181
+ arg_destroy(a);
182
+ }
183
+ break;
184
+ }
185
+ case GQL_UNION: {
186
+ gqlTypeLink link;
187
+
188
+ while (NULL != (link = type->types)) {
189
+ type->types = link->next;
190
+ AGOO_FREE(link);
191
+ }
192
+ break;
193
+ }
194
+ case GQL_ENUM: {
195
+ gqlEnumVal ev;
196
+
197
+ while (NULL != (ev = type->choices)) {
198
+ type->choices = ev->next;
199
+ AGOO_FREE((char*)ev->value);
200
+ AGOO_FREE((char*)ev->desc);
201
+ free_dir_uses(ev->dir);
202
+ AGOO_FREE(ev);
203
+ }
204
+ break;
205
+ }
206
+ default:
207
+ break;
130
208
  }
131
209
  }
132
210
 
133
211
  static void
134
- dir_destroy(gqlDir dir) {
212
+ type_destroy(gqlType type) {
213
+ if (&gql_int_type == type ||
214
+ &gql_i64_type == type ||
215
+ &gql_bool_type == type ||
216
+ &gql_float_type == type ||
217
+ &gql_time_type == type ||
218
+ &gql_uuid_type == type ||
219
+ &gql_token_type == type ||
220
+ &gql_var_type == type ||
221
+ &gql_string_type == type) {
222
+
223
+ return;
224
+ }
225
+ type_clean(type);
226
+ AGOO_FREE((char*)type->name);
227
+ AGOO_FREE(type);
228
+ }
229
+
230
+ static void
231
+ dir_clean(gqlDir dir) {
135
232
  gqlArg a;
136
233
  gqlStrLink link;
137
234
 
138
- free((char*)dir->name);
139
- free((char*)dir->desc);
235
+ AGOO_FREE((char*)dir->desc);
140
236
  while (NULL != (a = dir->args)) {
141
237
  dir->args = a->next;
142
- free((char*)a->name);
143
- free((char*)a->type_name);
144
- free((char*)a->desc);
145
- gql_value_destroy(a->default_value);
146
- DEBUG_FREE(mem_graphql_arg, a);
147
- free(a);
238
+ arg_destroy(a);
148
239
  }
149
240
  while (NULL != (link = dir->locs)) {
150
241
  dir->locs = link->next;
151
- free(link->str);
152
- free(link);
242
+ AGOO_FREE(link->str);
243
+ AGOO_FREE(link);
153
244
  }
154
- DEBUG_FREE(mem_graphql_directive, dir);
155
- free(dir);
245
+ }
246
+
247
+ static void
248
+ dir_destroy(gqlDir dir) {
249
+ AGOO_FREE((char*)dir->name);
250
+ dir_clean(dir);
251
+ AGOO_FREE(dir);
156
252
  }
157
253
 
158
254
  gqlType
@@ -174,9 +270,22 @@ gql_type_get(const char *name) {
174
270
  return type;
175
271
  }
176
272
 
273
+ void
274
+ gql_type_iterate(void (*fun)(gqlType type, void *ctx), void *ctx) {
275
+ Slot *sp = buckets;
276
+ Slot s;
277
+ int i;
278
+
279
+ for (i = BUCKET_SIZE; 0 < i; i--, sp++) {
280
+ for (s = *sp; NULL != s; s = s->next) {
281
+ fun(s->type, ctx);
282
+ }
283
+ }
284
+ }
285
+
177
286
  gqlDir
178
287
  gql_directive_get(const char *name) {
179
- gqlDir dir = directives;
288
+ gqlDir dir = gql_directives;
180
289
 
181
290
  for (; NULL != dir; dir = dir->next) {
182
291
  if (0 == strcmp(name, dir->name)) {
@@ -203,10 +312,9 @@ gql_type_set(agooErr err, gqlType type) {
203
312
  return AGOO_ERR_OK;
204
313
  }
205
314
  }
206
- if (NULL == (s = (Slot)malloc(sizeof(struct _slot)))) {
207
- return agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a GraphQL type.");
315
+ if (NULL == (s = (Slot)AGOO_MALLOC(sizeof(struct _slot)))) {
316
+ return agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a GraphQL type.");
208
317
  }
209
- DEBUG_ALLOC(mem_graphql_slot, s);
210
318
  s->hash = h;
211
319
  s->type = type;
212
320
  s->next = *bucket;
@@ -231,8 +339,7 @@ type_remove(gqlType type) {
231
339
  } else {
232
340
  prev->next = s->next;
233
341
  }
234
- DEBUG_FREE(mem_graphql_slot, s);
235
- free(s);
342
+ AGOO_FREE(s);
236
343
 
237
344
  break;
238
345
  }
@@ -243,24 +350,32 @@ type_remove(gqlType type) {
243
350
 
244
351
  int
245
352
  gql_init(agooErr err) {
246
- memset(buckets, 0, sizeof(buckets));
353
+ gqlType query_type;
354
+ gqlType mutation_type;
355
+ gqlType subscription_type;
247
356
 
357
+ if (inited) {
358
+ return AGOO_ERR_OK;
359
+ }
360
+ memset(buckets, 0, sizeof(buckets));
248
361
  if (AGOO_ERR_OK != gql_value_init(err) ||
249
362
  AGOO_ERR_OK != gql_intro_init(err)) {
250
363
 
251
364
  return err->code;
252
365
  }
253
- if (
254
- NULL == (query_type = gql_type_create(err, "Query", "The GraphQL root Query.", -1, false, NULL)) ||
255
- NULL == (mutation_type = gql_type_create(err, "Mutation", "The GraphQL root Mutation.", -1, false, NULL)) ||
256
- NULL == (subscription_type = gql_type_create(err, "Subscription", "The GraphQL root Subscription.", -1, false, NULL)) ||
257
- NULL == (schema_type = gql_type_create(err, "schema", "The GraphQL root Object.", -1, false, NULL)) ||
258
- NULL == gql_type_field(err, schema_type, "query", query_type, "Root level query.", false, false, false, NULL) ||
259
- NULL == gql_type_field(err, schema_type, "mutation", mutation_type, "Root level mutation.", false, false, false, NULL) ||
260
- NULL == gql_type_field(err, schema_type, "subscription", subscription_type, "Root level subscription.", false, false, false, NULL)) {
366
+ if (NULL == (query_type = gql_type_create(err, "Query", "The GraphQL root Query.", 0, NULL)) ||
367
+ NULL == (mutation_type = gql_type_create(err, "Mutation", "The GraphQL root Mutation.", 0, NULL)) ||
368
+ NULL == (subscription_type = gql_type_create(err, "Subscription", "The GraphQL root Subscription.", 0, NULL)) ||
369
+ NULL == (schema_type = gql_type_create(err, "schema", "The GraphQL root Object.", 0, NULL)) ||
370
+
371
+ NULL == gql_type_field(err, schema_type, "query", query_type, NULL, "Root level query.", 0, false) ||
372
+ NULL == gql_type_field(err, schema_type, "mutation", mutation_type, NULL, "Root level mutation.", 0, false) ||
373
+ NULL == gql_type_field(err, schema_type, "subscription", subscription_type, NULL, "Root level subscription.", 0, false)) {
261
374
 
262
375
  return err->code;
263
376
  }
377
+ inited = true;
378
+
264
379
  return AGOO_ERR_OK;
265
380
  }
266
381
 
@@ -271,63 +386,69 @@ gql_destroy() {
271
386
  Slot n;
272
387
  int i;
273
388
  gqlDir dir;
274
-
275
- for (i = BUCKET_SIZE; 0 < i; i--, sp++) {
276
- Slot *b = sp;
277
389
 
390
+ for (i = BUCKET_SIZE; 0 < i; i--, sp++) {
391
+ s = *sp;
392
+
278
393
  *sp = NULL;
279
- for (s = *b; NULL != s; s = n) {
394
+ for (; NULL != s; s = n) {
280
395
  n = s->next;
281
- DEBUG_FREE(mem_graphql_slot, s);
282
- gql_type_destroy(s->type);
283
- free(s);
396
+ type_destroy(s->type);
397
+ AGOO_FREE(s);
284
398
  }
285
- *sp = NULL;
286
399
  }
287
- while (NULL != (dir = directives)) {
288
- directives = dir->next;
400
+ while (NULL != (dir = gql_directives)) {
401
+ gql_directives = dir->next;
289
402
  dir_destroy(dir);
290
403
  }
404
+ inited = false;
291
405
  }
292
406
 
293
407
  static gqlType
294
- type_create(agooErr err, const char *name, const char *desc, int dlen, bool locked) {
295
- gqlType type = (gqlType)malloc(sizeof(struct _gqlType));
408
+ type_create(agooErr err, gqlKind kind, const char *name, const char *desc, size_t dlen) {
409
+ gqlType type = gql_type_get(name);
296
410
 
297
411
  if (NULL == type) {
298
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a GraphQL Type.");
299
- } else {
300
- DEBUG_ALLOC(mem_graphql_type, type);
301
- type->name = strdup(name);
302
- if (NULL == desc) {
303
- type->desc = NULL;
304
- } else {
305
- if (0 >= dlen) {
306
- type->desc = strdup(desc);
307
- } else {
308
- type->desc = strndup(desc, dlen);
309
- }
310
- if (NULL == type->desc) {
311
- agoo_err_set(err, AGOO_ERR_MEMORY, "strdup or strndup of length %d failed.", dlen);
312
- return NULL;
313
- }
412
+ if (NULL == (type = (gqlType)AGOO_MALLOC(sizeof(struct _gqlType)))) {
413
+ agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a GraphQL Type.");
414
+ return NULL;
415
+ }
416
+ if (NULL == (type->name = AGOO_STRDUP(name))) {
417
+ agoo_err_set(err, AGOO_ERR_MEMORY, "strdup of type name failed. %s:%d", __FILE__, __LINE__);
418
+ return NULL;
419
+ }
420
+ if (NULL == (type->desc = alloc_string(err, desc, dlen)) && AGOO_ERR_OK != err->code) {
421
+ return NULL;
314
422
  }
315
- type->locked = locked;
316
- type->core = false;
317
423
  type->dir = NULL;
424
+ type->kind = kind;
425
+ type->scalar_kind = GQL_SCALAR_UNDEF;
426
+ type->core = false;
318
427
 
319
428
  if (AGOO_ERR_OK != gql_type_set(err, type)) {
320
429
  gql_type_destroy(type);
321
430
  type = NULL;
322
431
  }
432
+ } else if (GQL_UNDEF == type->kind) {
433
+ if (NULL == (type->desc = alloc_string(err, desc, dlen)) && AGOO_ERR_OK != err->code) {
434
+ return NULL;
435
+ }
436
+ type->kind = kind;
437
+ } else if (type->core) {
438
+ agoo_err_set(err, AGOO_ERR_LOCK, "%d can not be modified.", name);
439
+ return NULL;
440
+ } else if (kind == type->kind) { // looks like it is being modified so remove all the old stuff
441
+ type_clean(type);
442
+ } else {
443
+ agoo_err_set(err, AGOO_ERR_LOCK, "%d already exists as a %s.", name, kind_string(type->kind));
444
+ return NULL;
323
445
  }
324
446
  return type;
325
447
  }
326
448
 
327
- agooText
328
- gql_object_to_json(agooText text, gqlValue value, int indent, int depth) {
329
- // TBD
330
- return text;
449
+ static gqlType
450
+ type_undef_create(agooErr err, const char *name) {
451
+ return type_create(err, GQL_UNDEF, name, NULL, 0);
331
452
  }
332
453
 
333
454
  agooText
@@ -337,70 +458,76 @@ gql_object_to_graphql(agooText text, gqlValue value, int indent, int depth) {
337
458
  }
338
459
 
339
460
  gqlType
340
- gql_type_create(agooErr err, const char *name, const char *desc, int dlen, bool locked, gqlType *interfaces) {
341
- gqlType type = type_create(err, name, desc, dlen, locked);
342
-
343
- if (NULL != type) {
344
- type->kind = GQL_OBJECT;
345
- type->to_json = gql_object_to_json;
346
- type->fields = NULL;
347
- type->interfaces = NULL;
348
- if (NULL != interfaces) {
349
- gqlType *tp = interfaces;
350
- gqlType *np;
351
- int cnt = 0;
461
+ gql_assure_type(agooErr err, const char *name) {
462
+ gqlType type = NULL;
463
+
464
+ if (NULL != name) {
465
+ type = gql_type_get(name);
352
466
 
353
- for (; NULL != *tp; tp++) {
354
- cnt++;
355
- }
356
- if (0 < cnt) {
357
- if (NULL == (type->interfaces = (gqlType*)malloc(sizeof(gqlType) * (cnt + 1)))) {
358
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a GraphQL type interfaces.");
359
- free(type);
360
- return NULL;
361
- }
362
- for (np = type->interfaces, tp = interfaces; NULL != *tp; np++, tp++) {
363
- *np = *tp;
364
- }
365
- *np = NULL;
467
+ if (NULL == type) {
468
+ if (NULL == (type = type_undef_create(err, name))) {
469
+ return NULL;
366
470
  }
367
471
  }
368
472
  }
369
473
  return type;
370
474
  }
371
475
 
476
+ static gqlDir
477
+ assure_directive(agooErr err, const char *name) {
478
+ gqlDir dir = gql_directive_get(name);
479
+
480
+ if (NULL == dir) {
481
+ if (NULL == (dir = (gqlDir)AGOO_MALLOC(sizeof(struct _gqlDir)))) {
482
+ agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a GraphQL directive.");
483
+ return NULL;
484
+ }
485
+ dir->next = gql_directives;
486
+ gql_directives = dir;
487
+ dir->name = AGOO_STRDUP(name);
488
+ dir->args = NULL;
489
+ dir->locs = NULL;
490
+ dir->defined = false;
491
+ dir->desc = NULL;
492
+ }
493
+ return dir;
494
+ }
495
+
496
+ gqlType
497
+ gql_type_create(agooErr err, const char *name, const char *desc, size_t dlen, gqlTypeLink interfaces) {
498
+ gqlType type = type_create(err, GQL_OBJECT, name, desc, dlen);
499
+
500
+ if (NULL != type) {
501
+ type->fields = NULL;
502
+ type->interfaces = interfaces;
503
+ }
504
+ return type;
505
+ }
506
+
372
507
  gqlField
373
- gql_type_field(agooErr err,
374
- gqlType type,
375
- const char *name,
376
- gqlType return_type,
377
- const char *desc,
378
- bool required,
379
- bool list,
380
- bool not_empty,
381
- gqlResolveFunc resolve) {
382
- gqlField f = (gqlField)malloc(sizeof(struct _gqlField));
508
+ gql_type_field(agooErr err,
509
+ gqlType type,
510
+ const char *name,
511
+ gqlType return_type,
512
+ gqlValue default_value,
513
+ const char *desc,
514
+ size_t dlen,
515
+ bool required) {
516
+ gqlField f = (gqlField)AGOO_MALLOC(sizeof(struct _gqlField));
383
517
 
384
518
  if (NULL == f) {
385
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a GraphQL field.");
519
+ agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a GraphQL field.");
386
520
  } else {
387
- DEBUG_ALLOC(mem_graphql_field, f);
388
521
  f->next = NULL;
389
- f->name = strdup(name);
522
+ f->name = AGOO_STRDUP(name);
390
523
  f->type = return_type;
391
- if (NULL == desc) {
392
- f->desc = NULL;
393
- } else {
394
- f->desc = strdup(desc);
524
+ if (NULL == (f->desc = alloc_string(err, desc, dlen)) && AGOO_ERR_OK != err->code) {
525
+ return NULL;
395
526
  }
396
- f->reason = NULL;
397
527
  f->args = NULL;
398
528
  f->dir = NULL;
399
- f->resolve = resolve;
529
+ f->default_value = default_value;
400
530
  f->required = required;
401
- f->list = list;
402
- f->not_empty = not_empty;
403
- f->deprecated = false;
404
531
  if (NULL == type->fields) {
405
532
  type->fields = f;
406
533
  } else {
@@ -420,21 +547,19 @@ gql_field_arg(agooErr err,
420
547
  const char *name,
421
548
  gqlType type,
422
549
  const char *desc,
550
+ size_t dlen,
423
551
  struct _gqlValue *def_value,
424
552
  bool required) {
425
- gqlArg a = (gqlArg)malloc(sizeof(struct _gqlArg));
553
+ gqlArg a = (gqlArg)AGOO_MALLOC(sizeof(struct _gqlArg));
426
554
 
427
555
  if (NULL == a) {
428
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a GraphQL field argument.");
556
+ agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a GraphQL field argument.");
429
557
  } else {
430
- DEBUG_ALLOC(mem_graphql_arg, a);
431
558
  a->next = NULL;
432
- a->name = strdup(name);
559
+ a->name = AGOO_STRDUP(name);
433
560
  a->type = type;
434
- if (NULL == desc) {
435
- a->desc = NULL;
436
- } else {
437
- a->desc = strdup(desc);
561
+ if (NULL == (a->desc = alloc_string(err, desc, dlen)) && AGOO_ERR_OK != err->code) {
562
+ return NULL;
438
563
  }
439
564
  a->default_value = def_value;
440
565
  a->dir = NULL;
@@ -452,37 +577,24 @@ gql_field_arg(agooErr err,
452
577
  return a;
453
578
  }
454
579
 
455
- agooText
456
- gql_union_to_json(agooText text, gqlValue value, int indent, int depth) {
457
- // TBD
458
- return text;
459
- }
460
-
461
580
  gqlType
462
- gql_union_create(agooErr err, const char *name, const char *desc, int dlen, bool locked) {
463
- gqlType type = type_create(err, name, desc, dlen, locked);
581
+ gql_union_create(agooErr err, const char *name, const char *desc, size_t dlen) {
582
+ gqlType type = type_create(err, GQL_UNION, name, desc, dlen);
464
583
 
465
584
  if (NULL != type) {
466
- type->kind = GQL_UNION;
467
- type->to_json = gql_union_to_json;
468
585
  type->types = NULL;
469
586
  }
470
587
  return type;
471
588
  }
472
589
 
473
590
  int
474
- gql_union_add(agooErr err, gqlType type, const char *name, int len) {
475
- gqlTypeLink link = (gqlTypeLink)malloc(sizeof(gqlTypeLink));
591
+ gql_union_add(agooErr err, gqlType type, gqlType member) {
592
+ gqlTypeLink link = (gqlTypeLink)AGOO_MALLOC(sizeof(struct _gqlTypeLink));
476
593
 
477
594
  if (NULL == link) {
478
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a GraphQL Union value.");
479
- }
480
- if (0 >= len) {
481
- len = (int)strlen(name);
482
- }
483
- if (NULL == (link->name = strndup(name, len))) {
484
- return agoo_err_set(err, AGOO_ERR_MEMORY, "strndup of length %d failed.", len);
595
+ return agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a GraphQL Union value.");
485
596
  }
597
+ link->type = member;
486
598
  if (NULL == type->types) {
487
599
  link->next = type->types;
488
600
  type->types = link;
@@ -497,175 +609,122 @@ gql_union_add(agooErr err, gqlType type, const char *name, int len) {
497
609
  return AGOO_ERR_OK;
498
610
  }
499
611
 
500
- agooText
501
- gql_enum_to_json(agooText text, gqlValue value, int indent, int depth) {
502
- // TBD
503
- return text;
504
- }
505
-
506
612
  gqlType
507
- gql_enum_create(agooErr err, const char *name, const char *desc, int dlen, bool locked) {
508
- gqlType type = type_create(err, name, desc, dlen, locked);
613
+ gql_enum_create(agooErr err, const char *name, const char *desc, size_t dlen) {
614
+ gqlType type = type_create(err, GQL_ENUM, name, desc, dlen);
509
615
 
510
616
  if (NULL != type) {
511
- type->kind = GQL_ENUM;
512
- type->to_json = gql_enum_to_json;
513
617
  type->choices = NULL;
514
618
  }
515
619
  return type;
516
620
  }
517
621
 
518
- int
519
- gql_enum_add(agooErr err, gqlType type, const char *value, int len) {
520
- gqlStrLink link = (gqlStrLink)malloc(sizeof(gqlStrLink));
622
+ gqlEnumVal
623
+ gql_enum_append(agooErr err, gqlType type, const char *value, size_t len, const char *desc, size_t dlen) {
624
+ gqlEnumVal ev = (gqlEnumVal)AGOO_MALLOC(sizeof(struct _gqlEnumVal));
521
625
 
522
- if (NULL == link) {
523
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a GraphQL Enum value.");
626
+ if (NULL == ev) {
627
+ agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a GraphQL Enum value.");
628
+ return NULL;
524
629
  }
525
630
  if (0 >= len) {
526
- len = (int)strlen(value);
527
- }
528
- link->next = type->choices;
529
- type->choices = link;
530
- if (NULL == (link->str = strndup(value, len))) {
531
- return agoo_err_set(err, AGOO_ERR_MEMORY, "strdup or strndup of length %d failed.", len);
532
- }
533
- return AGOO_ERR_OK;
534
- }
535
-
536
- int
537
- gql_enum_append(agooErr err, gqlType type, const char *value, int len) {
538
- gqlStrLink link = (gqlStrLink)malloc(sizeof(gqlStrLink));
539
-
540
- if (NULL == link) {
541
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a GraphQL Enum value.");
631
+ len = strlen(value);
542
632
  }
543
- if (0 >= len) {
544
- len = (int)strlen(value);
633
+ if (NULL == (ev->value = AGOO_STRNDUP(value, len))) {
634
+ agoo_err_set(err, AGOO_ERR_MEMORY, "strndup of length %d failed. %s:%d", len, __FILE__, __LINE__);
635
+ return NULL;
545
636
  }
546
- if (NULL == (link->str = strndup(value, len))) {
547
- return agoo_err_set(err, AGOO_ERR_MEMORY, "strdup or strndup of length %d failed.", len);
637
+ ev->dir = NULL;
638
+ if (NULL == (ev->desc = alloc_string(err, desc, dlen)) && AGOO_ERR_OK != err->code) {
639
+ return NULL;
548
640
  }
549
641
  if (NULL == type->choices) {
550
- link->next = type->choices;
551
- type->choices = link;
642
+ ev->next = type->choices;
643
+ type->choices = ev;
552
644
  } else {
553
- gqlStrLink last = type->choices;
645
+ gqlEnumVal last = type->choices;
554
646
 
555
647
  for (; NULL != last->next; last = last->next) {
556
648
  }
557
- link->next = NULL;
558
- last->next = link;
559
- }
560
- return AGOO_ERR_OK;
561
- }
562
-
563
- static agooText
564
- fragment_to_json(agooText text, gqlValue value, int indent, int depth) {
565
- // TBD
566
- return text;
567
- }
568
-
569
- gqlType
570
- gql_fragment_create(agooErr err, const char *name, const char *desc, int dlen, bool locked, gqlType on) {
571
- gqlType type = type_create(err, name, desc, dlen, locked);
572
-
573
- if (NULL != type) {
574
- type->kind = GQL_FRAG;
575
- type->to_json = fragment_to_json;
576
- type->fields = NULL;
577
- type->on = on;
649
+ ev->next = NULL;
650
+ last->next = ev;
578
651
  }
579
- return type;
580
- }
581
-
582
- static agooText
583
- input_to_json(agooText text, gqlValue value, int indent, int depth) {
584
- // TBD
585
- return text;
652
+ return ev;
586
653
  }
587
654
 
588
655
  gqlType
589
- gql_input_create(agooErr err, const char *name, const char *desc, int dlen, bool locked) {
590
- gqlType type = type_create(err, name, desc, dlen, locked);
656
+ gql_input_create(agooErr err, const char *name, const char *desc, size_t dlen) {
657
+ gqlType type = type_create(err, GQL_INPUT, name, desc, dlen);
591
658
 
592
659
  if (NULL != type) {
593
- type->kind = GQL_INPUT;
594
- type->to_json = input_to_json;
595
- type->fields = NULL;
660
+ type->args = NULL;
596
661
  }
597
662
  return type;
598
663
  }
599
664
 
600
- static agooText
601
- interface_to_json(agooText text, gqlValue value, int indent, int depth) {
602
- // TBD
603
- return text;
604
- }
605
-
606
665
  gqlType
607
- gql_interface_create(agooErr err, const char *name, const char *desc, int dlen, bool locked) {
608
- gqlType type = type_create(err, name, desc, dlen, locked);
666
+ gql_interface_create(agooErr err, const char *name, const char *desc, size_t dlen) {
667
+ gqlType type = type_create(err, GQL_INTERFACE, name, desc, dlen);
609
668
 
610
669
  if (NULL != type) {
611
- type->kind = GQL_INTERFACE;
612
- type->to_json = interface_to_json;
613
670
  type->fields = NULL;
671
+ type->interfaces = NULL;
614
672
  }
615
673
  return type;
616
674
  }
617
675
 
676
+ // TBD this is not correct. Is it used?
618
677
  static agooText
619
- scalar_to_json(agooText text, gqlValue value, int indent, int depth) {
620
- if (NULL == value->str) {
621
- text = agoo_text_append(text, "null", 4);
622
- } else {
623
- text = agoo_text_append(text, "\"", 1);
624
- text = agoo_text_append(text, value->str, -1);
625
- text = agoo_text_append(text, "\"", 1);
626
- }
678
+ scalar_to_text(agooText text, gqlValue value, int indent, int depth) {
679
+ printf("********** scalar_to_text\n");
627
680
  return text;
628
681
  }
629
682
 
630
683
  // Create a scalar type that will be represented as a string.
631
684
  gqlType
632
- gql_scalar_create(agooErr err, const char *name, const char *desc, int dlen, bool locked) {
633
- gqlType type = type_create(err, name, desc, dlen, locked);
685
+ gql_scalar_create(agooErr err, const char *name, const char *desc, size_t dlen) {
686
+ gqlType type = type_create(err, GQL_SCALAR, name, desc, dlen);
634
687
 
635
688
  if (NULL != type) {
636
- type->kind = GQL_SCALAR;
637
- type->to_json = scalar_to_json;
638
- type->coerce = NULL;
689
+ type->to_json = scalar_to_text;
690
+ type->to_sdl = scalar_to_text;
639
691
  type->destroy = NULL;
640
692
  }
641
693
  return type;
642
694
  }
643
695
 
644
696
  gqlDir
645
- gql_directive_create(agooErr err, const char *name, const char *desc, int dlen, bool locked) {
646
- gqlDir dir;
647
-
648
- if (NULL == (dir = (gqlDir)malloc(sizeof(struct _gqlDir)))) {
649
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a GraphQL directive.");
650
- return NULL;
651
- }
652
- DEBUG_ALLOC(mem_graphql_directive, dir);
653
- dir->next = directives;
654
- directives = dir;
655
- dir->name = strdup(name);
656
- dir->args = NULL;
657
- dir->locs = NULL;
658
- dir->locked = locked;
659
- if (NULL == desc) {
660
- dir->desc = NULL;
661
- } else {
662
- if (0 >= dlen) {
663
- dir->desc = strdup(desc);
697
+ gql_directive_create(agooErr err, const char *name, const char *desc, size_t dlen) {
698
+ gqlDir dir = gql_directive_get(name);
699
+
700
+ if (NULL != dir) {
701
+ if (!dir->defined) {
702
+ dir->defined = true;
703
+ if (NULL == (dir->desc = alloc_string(err, desc, dlen)) && AGOO_ERR_OK != err->code) {
704
+ return NULL;
705
+ }
664
706
  } else {
665
- dir->desc = strndup(desc, dlen);
707
+ dir_clean(dir);
708
+ dir->args = NULL;
709
+ dir->locs = NULL;
710
+ dir->defined = true;
711
+ if (NULL == (dir->desc = alloc_string(err, desc, dlen)) && AGOO_ERR_OK != err->code) {
712
+ return NULL;
713
+ }
666
714
  }
667
- if (NULL == dir->desc) {
668
- agoo_err_set(err, AGOO_ERR_MEMORY, "strdup or strndup of length %d failed.", dlen);
715
+ } else {
716
+ if (NULL == (dir = (gqlDir)AGOO_MALLOC(sizeof(struct _gqlDir)))) {
717
+ agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a GraphQL directive.");
718
+ return NULL;
719
+ }
720
+ dir->next = gql_directives;
721
+ gql_directives = dir;
722
+ dir->name = AGOO_STRDUP(name);
723
+ dir->args = NULL;
724
+ dir->locs = NULL;
725
+ dir->defined = true;
726
+ dir->core = false;
727
+ if (NULL == (dir->desc = alloc_string(err, desc, dlen)) && AGOO_ERR_OK != err->code) {
669
728
  return NULL;
670
729
  }
671
730
  }
@@ -676,31 +735,21 @@ gqlArg
676
735
  gql_dir_arg(agooErr err,
677
736
  gqlDir dir,
678
737
  const char *name,
679
- const char *type_name,
738
+ gqlType type,
680
739
  const char *desc,
681
- int dlen,
740
+ size_t dlen,
682
741
  struct _gqlValue *def_value,
683
742
  bool required) {
684
743
 
685
- gqlArg a = (gqlArg)malloc(sizeof(struct _gqlArg));
744
+ gqlArg a = (gqlArg)AGOO_MALLOC(sizeof(struct _gqlArg));
686
745
 
687
746
  if (NULL == a) {
688
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a GraphQL directive argument.");
747
+ agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a GraphQL directive argument.");
689
748
  } else {
690
- DEBUG_ALLOC(mem_graphql_arg, a);
691
749
  a->next = NULL;
692
- a->name = strdup(name);
693
- a->type_name = strdup(type_name);
694
- a->type = NULL;
695
- if (NULL == desc) {
696
- a->desc = NULL;
697
- } else if (0 < dlen) {
698
- a->desc = strdup(desc);
699
- } else {
700
- a->desc = strndup(desc, dlen);
701
- }
702
- if (NULL == a->desc) {
703
- agoo_err_set(err, AGOO_ERR_MEMORY, "strdup or strndup of length %d failed.", dlen);
750
+ a->name = AGOO_STRDUP(name);
751
+ a->type = type;
752
+ if (NULL == (a->desc = alloc_string(err, desc, dlen)) && AGOO_ERR_OK != err->code) {
704
753
  return NULL;
705
754
  }
706
755
  a->default_value = def_value;
@@ -721,11 +770,11 @@ gql_dir_arg(agooErr err,
721
770
 
722
771
  int
723
772
  gql_directive_on(agooErr err, gqlDir d, const char *on, int len) {
724
- gqlStrLink link = (gqlStrLink)malloc(sizeof(gqlStrLink));
773
+ gqlStrLink link = (gqlStrLink)AGOO_MALLOC(sizeof(struct _gqlStrLink));
725
774
  gqlStrLink loc;
726
775
 
727
776
  if (NULL == link) {
728
- agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocation memory for a GraphQL directive location.");
777
+ agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a GraphQL directive location.");
729
778
  }
730
779
  if (0 >= len) {
731
780
  len = (int)strlen(on);
@@ -739,12 +788,31 @@ gql_directive_on(agooErr err, gqlDir d, const char *on, int len) {
739
788
  }
740
789
  loc->next = link;
741
790
  }
742
- if (NULL == (link->str = strndup(on, len))) {
743
- return agoo_err_set(err, AGOO_ERR_MEMORY, "strdup or strndup of length %d failed.", len);
791
+ if (NULL == (link->str = AGOO_STRNDUP(on, len))) {
792
+ return agoo_err_set(err, AGOO_ERR_MEMORY, "strndup of length %d failed. %s:%d", len, __FILE__, __LINE__);
744
793
  }
745
794
  return AGOO_ERR_OK;
746
795
  }
747
796
 
797
+ gqlType
798
+ gql_assure_list(agooErr err, gqlType base, bool not_empty) {
799
+ char name[256];
800
+ gqlType type;
801
+
802
+ if ((int)sizeof(name) <= snprintf(name, sizeof(name), "[%s%s]", base->name, not_empty ? "!" : "")) {
803
+ agoo_err_set(err, AGOO_ERR_ARG, "Name too long");
804
+ return NULL;
805
+ }
806
+ if (NULL == (type = gql_type_get(name))) {
807
+ if (NULL == (type = type_create(err, GQL_LIST, name, NULL, 0))) {
808
+ return NULL;
809
+ }
810
+ type->base = base;
811
+ type->not_empty = not_empty;
812
+ }
813
+ return type;
814
+ }
815
+
748
816
  void
749
817
  gql_type_destroy(gqlType type) {
750
818
  type_destroy(type);
@@ -825,15 +893,14 @@ arg_sdl(agooText text, gqlArg a, bool with_desc, bool last) {
825
893
  text = desc_sdl(text, a->desc, 4);
826
894
  }
827
895
  text = agoo_text_append(text, a->name, -1);
896
+ text = agoo_text_append(text, ": ", 2);
897
+ text = agoo_text_append(text, a->type->name, -1);
828
898
  if (a->required) {
829
- text = agoo_text_append(text, "!: ", 3);
830
- } else {
831
- text = agoo_text_append(text, ": ", 2);
899
+ text = agoo_text_append(text, "!", 1);
832
900
  }
833
- text = agoo_text_append(text, a->type_name, -1);
834
901
  if (NULL != a->default_value) {
835
902
  text = agoo_text_append(text, " = ", 3);
836
- text = gql_value_json(text, a->default_value, 0, 0);
903
+ text = gql_value_sdl(text, a->default_value, 0, 0);
837
904
  }
838
905
  if (!last) {
839
906
  text = agoo_text_append(text, ", ", 2);
@@ -841,6 +908,26 @@ arg_sdl(agooText text, gqlArg a, bool with_desc, bool last) {
841
908
  return text;
842
909
  }
843
910
 
911
+ static agooText
912
+ append_dir_use(agooText text, gqlDirUse use) {
913
+ for (; NULL != use; use = use->next) {
914
+ text = agoo_text_append(text, " @", 2);
915
+ text = agoo_text_append(text, use->dir->name, -1);
916
+ if (NULL != use->args) {
917
+ gqlLink link;
918
+
919
+ text = agoo_text_append(text, "(", 1);
920
+ for (link = use->args; NULL != link; link = link->next) {
921
+ text = agoo_text_append(text, link->key, -1);
922
+ text = agoo_text_append(text, ": ", 2);
923
+ text = gql_value_sdl(text, link->value, 0, 0);
924
+ }
925
+ text = agoo_text_append(text, ")", 1);
926
+ }
927
+ }
928
+ return text;
929
+ }
930
+
844
931
  static agooText
845
932
  field_sdl(agooText text, gqlField f, bool with_desc) {
846
933
  if (with_desc) {
@@ -858,19 +945,15 @@ field_sdl(agooText text, gqlField f, bool with_desc) {
858
945
  text = agoo_text_append(text, ")", 1);
859
946
  }
860
947
  text = agoo_text_append(text, ": ", 2);
861
- if (f->list) {
862
- text = agoo_text_append(text, "[", 1);
863
- text = agoo_text_append(text, f->type->name, -1);
864
- if (f->not_empty) {
865
- text = agoo_text_append(text, "!", 1);
866
- }
867
- text = agoo_text_append(text, "]", 1);
868
- } else {
869
- text = agoo_text_append(text, f->type->name, -1);
870
- }
948
+ text = agoo_text_append(text, f->type->name, -1);
871
949
  if (f->required) {
872
950
  text = agoo_text_append(text, "!", 1);
873
951
  }
952
+ if (NULL != f->default_value) {
953
+ text = agoo_text_append(text, " = ", 3);
954
+ text = gql_value_sdl(text, f->default_value, 0, 0);
955
+ }
956
+ text = append_dir_use(text, f->dir);
874
957
  text = agoo_text_append(text, "\n", 1);
875
958
 
876
959
  return text;
@@ -889,16 +972,17 @@ gql_type_sdl(agooText text, gqlType type, bool with_desc) {
889
972
  text = agoo_text_append(text, "type ", 5);
890
973
  text = agoo_text_append(text, type->name, -1);
891
974
  if (NULL != type->interfaces) {
892
- gqlType *tp = type->interfaces;
975
+ gqlTypeLink tp = type->interfaces;
893
976
 
894
977
  text = agoo_text_append(text, " implements ", 12);
895
- for (; NULL != *tp; tp++) {
896
- text = agoo_text_append(text, (*tp)->name, -1);
897
- if (NULL != *(tp + 1)) {
898
- text = agoo_text_append(text, ", ", 2);
978
+ for (; NULL != tp; tp = tp->next) {
979
+ text = agoo_text_append(text, tp->type->name, -1);
980
+ if (NULL != tp->next) {
981
+ text = agoo_text_append(text, " & ", 3);
899
982
  }
900
983
  }
901
984
  }
985
+ text = append_dir_use(text, type->dir);
902
986
  text = agoo_text_append(text, " {\n", 3);
903
987
  for (f = type->fields; NULL != f; f = f->next) {
904
988
  text = field_sdl(text, f, with_desc);
@@ -911,9 +995,10 @@ gql_type_sdl(agooText text, gqlType type, bool with_desc) {
911
995
 
912
996
  text = agoo_text_append(text, "union ", 6);
913
997
  text = agoo_text_append(text, type->name, -1);
998
+ text = append_dir_use(text, type->dir);
914
999
  text = agoo_text_append(text, " = ", 3);
915
1000
  for (link = type->types; NULL != link; link = link->next) {
916
- text = agoo_text_append(text, link->name, -1);
1001
+ text = agoo_text_append(text, link->type->name, -1);
917
1002
  if (NULL != link->next) {
918
1003
  text = agoo_text_append(text, " | ", 3);
919
1004
  }
@@ -921,14 +1006,19 @@ gql_type_sdl(agooText text, gqlType type, bool with_desc) {
921
1006
  break;
922
1007
  }
923
1008
  case GQL_ENUM: {
924
- gqlStrLink link;;
1009
+ gqlEnumVal ev;
925
1010
 
926
1011
  text = agoo_text_append(text, "enum ", 5);
927
1012
  text = agoo_text_append(text, type->name, -1);
1013
+ text = append_dir_use(text, type->dir);
928
1014
  text = agoo_text_append(text, " {\n", 3);
929
- for (link = type->choices; NULL != link; link = link->next) {
1015
+ for (ev = type->choices; NULL != ev; ev = ev->next) {
930
1016
  text = agoo_text_append(text, " ", 2);
931
- text = agoo_text_append(text, link->str, -1);
1017
+ if (with_desc) {
1018
+ text = desc_sdl(text, ev->desc, 2);
1019
+ }
1020
+ text = agoo_text_append(text, ev->value, -1);
1021
+ text = append_dir_use(text, ev->dir);
932
1022
  text = agoo_text_append(text, "\n", 1);
933
1023
  }
934
1024
  text = agoo_text_append(text, "}\n", 2);
@@ -937,6 +1027,7 @@ gql_type_sdl(agooText text, gqlType type, bool with_desc) {
937
1027
  case GQL_SCALAR:
938
1028
  text = agoo_text_append(text, "scalar ", 7);
939
1029
  text = agoo_text_append(text, type->name, -1);
1030
+ text = append_dir_use(text, type->dir);
940
1031
  text = agoo_text_append(text, "\n", 1);
941
1032
  break;
942
1033
  case GQL_INTERFACE: {
@@ -944,6 +1035,7 @@ gql_type_sdl(agooText text, gqlType type, bool with_desc) {
944
1035
 
945
1036
  text = agoo_text_append(text, "interface ", 10);
946
1037
  text = agoo_text_append(text, type->name, -1);
1038
+ text = append_dir_use(text, type->dir);
947
1039
  text = agoo_text_append(text, " {\n", 3);
948
1040
 
949
1041
  for (f = type->fields; NULL != f; f = f->next) {
@@ -953,13 +1045,14 @@ gql_type_sdl(agooText text, gqlType type, bool with_desc) {
953
1045
  break;
954
1046
  }
955
1047
  case GQL_INPUT: {
956
- gqlField f;
1048
+ gqlArg a;
957
1049
 
958
1050
  text = agoo_text_append(text, "input ", 6);
959
1051
  text = agoo_text_append(text, type->name, -1);
1052
+ text = append_dir_use(text, type->dir);
960
1053
  text = agoo_text_append(text, " {\n", 3);
961
- for (f = type->fields; NULL != f; f = f->next) {
962
- text = field_sdl(text, f, with_desc);
1054
+ for (a = type->args; NULL != a; a = a->next) {
1055
+ text = arg_sdl(text, a, with_desc, NULL == a->next);
963
1056
  }
964
1057
  text = agoo_text_append(text, "}\n", 2);
965
1058
  break;
@@ -1027,6 +1120,9 @@ gql_schema_sdl(agooText text, bool with_desc, bool all) {
1027
1120
  for (bucket = buckets, i = 0; i < BUCKET_SIZE; bucket++, i++) {
1028
1121
  for (s = *bucket; NULL != s; s = s->next) {
1029
1122
  type = s->type;
1123
+ if (GQL_LIST == type->kind) {
1124
+ continue;
1125
+ }
1030
1126
  if (!all && type->core) {
1031
1127
  continue;
1032
1128
  }
@@ -1040,6 +1136,9 @@ gql_schema_sdl(agooText text, bool with_desc, bool all) {
1040
1136
  for (bucket = buckets, i = 0; i < BUCKET_SIZE; bucket++, i++) {
1041
1137
  for (s = *bucket; NULL != s; s = s->next) {
1042
1138
  type = s->type;
1139
+ if (GQL_LIST == type->kind) {
1140
+ continue;
1141
+ }
1043
1142
  if (!all && type->core) {
1044
1143
  continue;
1045
1144
  }
@@ -1054,7 +1153,10 @@ gql_schema_sdl(agooText text, bool with_desc, bool all) {
1054
1153
  }
1055
1154
  }
1056
1155
  }
1057
- for (d = directives; NULL != d; d = d->next) {
1156
+ for (d = gql_directives; NULL != d; d = d->next) {
1157
+ if (!all && d->core) {
1158
+ continue;
1159
+ }
1058
1160
  text = agoo_text_append(text, "\n", 1);
1059
1161
  text = gql_directive_sdl(text, d, with_desc);
1060
1162
  text = agoo_text_append(text, "\n", 1);
@@ -1062,6 +1164,362 @@ gql_schema_sdl(agooText text, bool with_desc, bool all) {
1062
1164
  return text;
1063
1165
  }
1064
1166
 
1167
+ gqlDirUse
1168
+ gql_dir_use_create(agooErr err, const char *name) {
1169
+ gqlDirUse use;
1170
+
1171
+ if (NULL == (use = (gqlDirUse)AGOO_MALLOC(sizeof(struct _gqlDirUse)))) {
1172
+ agoo_err_set(err, AGOO_ERR_MEMORY, "Failed to allocate memory for a directive useage.");
1173
+ } else {
1174
+ if (NULL == (use->dir = assure_directive(err, name))) {
1175
+ return NULL;
1176
+ }
1177
+ use->next = NULL;
1178
+ use->args = NULL;
1179
+ }
1180
+ return use;
1181
+ }
1182
+
1183
+ int
1184
+ gql_dir_use_arg(agooErr err, gqlDirUse use, const char *key, gqlValue value) {
1185
+ gqlLink link = gql_link_create(err, key, value);
1186
+
1187
+ if (NULL == link) {
1188
+ return err->code;
1189
+ }
1190
+ link->next = use->args;
1191
+ use->args = link;
1192
+
1193
+ return AGOO_ERR_OK;
1194
+ }
1195
+
1196
+ void
1197
+ gql_type_directive_use(gqlType type, gqlDirUse use) {
1198
+ if (NULL == type->dir) {
1199
+ type->dir = use;
1200
+ } else {
1201
+ gqlDirUse u = type->dir;
1202
+
1203
+ for (; NULL != u->next; u = u->next) {
1204
+ }
1205
+ u->next = use;
1206
+ }
1207
+ }
1208
+
1209
+ gqlFrag
1210
+ gql_fragment_create(agooErr err, const char *name, gqlType on) {
1211
+ gqlFrag frag = (gqlFrag)AGOO_MALLOC(sizeof(struct _gqlFrag));
1212
+
1213
+ if (NULL == frag) {
1214
+ agoo_err_set(err, AGOO_ERR_MEMORY, "fragment creation failed. %s:%d", __FILE__, __LINE__);
1215
+ } else {
1216
+ frag->next = NULL;
1217
+ if (NULL != name) {
1218
+ if (NULL == (frag->name = AGOO_STRDUP(name))) {
1219
+ agoo_err_set(err, AGOO_ERR_MEMORY, "strdup of fragment name failed. %s:%d", __FILE__, __LINE__);
1220
+ return NULL;
1221
+ }
1222
+ } else {
1223
+ frag->name = NULL;
1224
+ }
1225
+ frag->dir = NULL;
1226
+ frag->on = on;
1227
+ frag->sels = NULL;
1228
+ }
1229
+ return frag;
1230
+ }
1231
+
1232
+ int
1233
+ gql_validate(agooErr err) {
1234
+
1235
+ // TBD
1236
+ // list members all the same or attempt to coerce
1237
+ // check all validation rules in doc that are not covered in the parsing
1238
+ // check the directive us in specified locations only
1239
+ // set types if they are not already set or error out
1240
+
1241
+ return AGOO_ERR_OK;
1242
+ }
1243
+
1244
+ gqlDoc
1245
+ gql_doc_create(agooErr err) {
1246
+ gqlDoc doc = (gqlDoc)AGOO_MALLOC(sizeof(struct _gqlDoc));
1247
+
1248
+ if (NULL == doc) {
1249
+ agoo_err_set(err, AGOO_ERR_MEMORY, "GraphQL Document creation failed. %s:%d", __FILE__, __LINE__);
1250
+ } else {
1251
+ doc->ops = NULL;
1252
+ doc->vars = NULL;
1253
+ doc->frags = NULL;
1254
+ }
1255
+ return doc;
1256
+ }
1257
+
1258
+ gqlOp
1259
+ gql_op_create(agooErr err, const char *name, gqlOpKind kind) {
1260
+ gqlOp op = (gqlOp)AGOO_MALLOC(sizeof(struct _gqlOp));
1261
+
1262
+ if (NULL == op) {
1263
+ agoo_err_set(err, AGOO_ERR_MEMORY, "GraphQL Operation creation failed. %s:%d", __FILE__, __LINE__);
1264
+ } else {
1265
+ op->next = NULL;
1266
+ if (NULL != name && '\0' != *name) {
1267
+ if (NULL == (op->name = AGOO_STRDUP(name))) {
1268
+ agoo_err_set(err, AGOO_ERR_MEMORY, "strdup of operation name failed. %s:%d", __FILE__, __LINE__);
1269
+ return NULL;
1270
+ }
1271
+ } else {
1272
+ op->name = NULL;
1273
+ }
1274
+ op->vars = NULL;
1275
+ op->dir = NULL;
1276
+ op->sels = NULL;
1277
+ op->kind = kind;
1278
+ }
1279
+ return op;
1280
+ }
1281
+
1282
+ static void
1283
+ var_destroy(gqlVar var) {
1284
+ AGOO_FREE((char*)var->name);
1285
+ if (NULL != var->value) {
1286
+ gql_value_destroy(var->value);
1287
+ }
1288
+ AGOO_FREE(var);
1289
+ }
1290
+
1291
+ static void
1292
+ sel_arg_destroy(gqlSelArg arg) {
1293
+ AGOO_FREE((char*)arg->name);
1294
+ if (NULL != arg->value) {
1295
+ gql_value_destroy(arg->value);
1296
+ }
1297
+ AGOO_FREE(arg);
1298
+ }
1299
+
1300
+ static void
1301
+ sel_destroy(gqlSel sel) {
1302
+ gqlSelArg arg;
1303
+ gqlSel s;
1304
+
1305
+ AGOO_FREE((char*)sel->alias);
1306
+ AGOO_FREE((char*)sel->name);
1307
+ while (NULL != (arg = sel->args)) {
1308
+ sel->args = arg->next;
1309
+ sel_arg_destroy(arg);
1310
+ }
1311
+ free_dir_uses(sel->dir);
1312
+ while (NULL != (s = sel->sels)) {
1313
+ sel->sels = s->next;
1314
+ sel_destroy(s);
1315
+ }
1316
+ AGOO_FREE((char*)sel->frag);
1317
+ if (NULL != sel->inline_frag) {
1318
+ gql_frag_destroy(sel->inline_frag);
1319
+ }
1320
+ AGOO_FREE(sel);
1321
+ }
1322
+
1323
+ void
1324
+ gql_op_destroy(gqlOp op) {
1325
+ gqlSel sel;
1326
+ gqlVar var;
1327
+
1328
+ AGOO_FREE((char*)op->name);
1329
+ while (NULL != (var = op->vars)) {
1330
+ op->vars = var->next;
1331
+ var_destroy(var);
1332
+ }
1333
+ free_dir_uses(op->dir);
1334
+ while (NULL != (sel = op->sels)) {
1335
+ op->sels = sel->next;
1336
+ sel_destroy(sel);
1337
+ }
1338
+ AGOO_FREE(op);
1339
+ }
1340
+
1341
+ void
1342
+ gql_frag_destroy(gqlFrag frag) {
1343
+ gqlSel sel;
1344
+
1345
+ AGOO_FREE((char*)frag->name);
1346
+ free_dir_uses(frag->dir);
1347
+ while (NULL != (sel = frag->sels)) {
1348
+ frag->sels = sel->next;
1349
+ sel_destroy(sel);
1350
+ }
1351
+ AGOO_FREE(frag);
1352
+ }
1353
+
1354
+ void
1355
+ gql_doc_destroy(gqlDoc doc) {
1356
+ gqlOp op;
1357
+ gqlFrag frag;
1358
+ gqlVar var;
1359
+
1360
+ while (NULL != (op = doc->ops)) {
1361
+ doc->ops = op->next;
1362
+ gql_op_destroy(op);
1363
+ }
1364
+ while (NULL != (var = doc->vars)) {
1365
+ doc->vars = var->next;
1366
+ var_destroy(var);
1367
+ }
1368
+ while (NULL != (frag = doc->frags)) {
1369
+ doc->frags = frag->next;
1370
+ gql_frag_destroy(frag);
1371
+ }
1372
+ AGOO_FREE(doc);
1373
+ }
1374
+
1375
+ static agooText
1376
+ sel_sdl(agooText text, gqlSel sel, int depth) {
1377
+ int indent = depth * 2;
1378
+
1379
+ if ((int)sizeof(spaces) <= indent) {
1380
+ indent = sizeof(spaces) - 1;
1381
+ }
1382
+ text = agoo_text_append(text, spaces, indent);
1383
+ if (NULL != sel->frag) {
1384
+ text = agoo_text_append(text, "...", 3);
1385
+ text = agoo_text_append(text, sel->frag, -1);
1386
+ text = append_dir_use(text, sel->dir);
1387
+ } else if (NULL != sel->inline_frag) {
1388
+ if (NULL == sel->inline_frag->on) {
1389
+ text = agoo_text_append(text, "...", 3);
1390
+ } else {
1391
+ text = agoo_text_append(text, "... on ", 7);
1392
+ text = agoo_text_append(text, sel->inline_frag->on->name, -1);
1393
+ }
1394
+ text = append_dir_use(text, sel->dir);
1395
+ if (NULL != sel->inline_frag->sels) {
1396
+ gqlSel s;
1397
+ int d2 = depth + 1;
1398
+
1399
+ text = agoo_text_append(text, " {\n", 3);
1400
+ for (s = sel->inline_frag->sels; NULL != s; s = s->next) {
1401
+ text = sel_sdl(text, s, d2);
1402
+ }
1403
+ text = agoo_text_append(text, spaces, indent);
1404
+ text = agoo_text_append(text, "}", 1);
1405
+ }
1406
+ } else {
1407
+ if (NULL != sel->alias) {
1408
+ text = agoo_text_append(text, sel->alias, -1);
1409
+ text = agoo_text_append(text, ": ", 2);
1410
+ }
1411
+ text = agoo_text_append(text, sel->name, -1);
1412
+ if (NULL != sel->args) {
1413
+ gqlSelArg arg;
1414
+
1415
+ text = agoo_text_append(text, "(", 1);
1416
+ for (arg = sel->args; NULL != arg; arg = arg->next) {
1417
+ text = agoo_text_append(text, arg->name, -1);
1418
+ text = agoo_text_append(text, ": ", 2);
1419
+ if (NULL != arg->var) {
1420
+ text = agoo_text_append(text, "$", 1);
1421
+ text = agoo_text_append(text, arg->var->name, -1);
1422
+ } else if (NULL != arg->value) {
1423
+ text = gql_value_sdl(text, arg->value, 0, 0);
1424
+ } else {
1425
+ text = agoo_text_append(text, "null", 4);
1426
+ }
1427
+ if (NULL != arg->next) {
1428
+ text = agoo_text_append(text, ", ", 2);
1429
+ }
1430
+ }
1431
+ text = agoo_text_append(text, ")", 1);
1432
+ }
1433
+ text = append_dir_use(text, sel->dir);
1434
+ if (NULL != sel->sels) {
1435
+ gqlSel s;
1436
+ int d2 = depth + 1;
1437
+
1438
+ text = agoo_text_append(text, " {\n", 3);
1439
+ for (s = sel->sels; NULL != s; s = s->next) {
1440
+ text = sel_sdl(text, s, d2);
1441
+ }
1442
+ text = agoo_text_append(text, spaces, indent);
1443
+ text = agoo_text_append(text, "}", 1);
1444
+ }
1445
+ }
1446
+ text = agoo_text_append(text, "\n", 1);
1447
+
1448
+ return text;
1449
+ }
1450
+
1451
+ static agooText
1452
+ op_sdl(agooText text, gqlOp op) {
1453
+ gqlSel sel;
1454
+
1455
+ text = agoo_text_append(text, "query", 5);
1456
+ if (NULL != op->name) {
1457
+ text = agoo_text_append(text, " ", 1);
1458
+ text = agoo_text_append(text, op->name, -1);
1459
+ }
1460
+ if (NULL != op->vars) {
1461
+ gqlVar var;
1462
+
1463
+ text = agoo_text_append(text, "(", 1);
1464
+ for (var = op->vars; NULL != var; var = var->next) {
1465
+ text = agoo_text_append(text, "$", 1);
1466
+ text = agoo_text_append(text, var->name, -1);
1467
+ text = agoo_text_append(text, ": ", 2);
1468
+
1469
+ text = agoo_text_append(text, var->type->name, -1);
1470
+ if (NULL != var->value) {
1471
+ text = agoo_text_append(text, " = ", 3);
1472
+ text = gql_value_sdl(text, var->value, 0, 0);
1473
+ }
1474
+ if (NULL != var->next) {
1475
+ text = agoo_text_append(text, ", ", 2);
1476
+ }
1477
+ }
1478
+ text = agoo_text_append(text, ")", 1);
1479
+ }
1480
+ text = append_dir_use(text, op->dir);
1481
+
1482
+ text = agoo_text_append(text, " {\n", 3);
1483
+ for (sel = op->sels; NULL != sel; sel = sel->next) {
1484
+ text = sel_sdl(text, sel, 1);
1485
+ }
1486
+ text = agoo_text_append(text, "}\n", 2);
1487
+
1488
+ return text;
1489
+ }
1490
+
1491
+ static agooText
1492
+ frag_sdl(agooText text, gqlFrag frag) {
1493
+ gqlSel sel;
1494
+
1495
+ text = agoo_text_append(text, "fragment ", 9);
1496
+ text = agoo_text_append(text, frag->name, -1);
1497
+ text = agoo_text_append(text, " on ", 4);
1498
+ text = agoo_text_append(text, frag->on->name, -1);
1499
+ text = append_dir_use(text, frag->dir);
1500
+ text = agoo_text_append(text, " {\n", 3);
1501
+ for (sel = frag->sels; NULL != sel; sel = sel->next) {
1502
+ text = sel_sdl(text, sel, 1);
1503
+ }
1504
+ text = agoo_text_append(text, "}\n", 2);
1505
+
1506
+ return text;
1507
+ }
1508
+
1509
+ agooText
1510
+ gql_doc_sdl(gqlDoc doc, agooText text) {
1511
+ gqlOp op;
1512
+ gqlFrag frag;
1513
+
1514
+ for (op = doc->ops; NULL != op; op = op->next) {
1515
+ op_sdl(text, op);
1516
+ }
1517
+ for (frag = doc->frags; NULL != frag; frag = frag->next) {
1518
+ text = frag_sdl(text, frag);
1519
+ }
1520
+ return text;
1521
+ }
1522
+
1065
1523
  void
1066
1524
  gql_dump_hook(agooReq req) {
1067
1525
  char buf[256];
@@ -1086,11 +1544,32 @@ gql_dump_hook(agooReq req) {
1086
1544
  agoo_res_set_message(req->res, text);
1087
1545
  }
1088
1546
 
1089
- void
1090
- gql_eval_hook(agooReq req) {
1091
- // TBD detect introspection
1092
- // start resolving by callout to some global handler as needed
1093
- // pass target, field, args
1094
- // return json or gqlValue
1095
- // for handler, if introspection then handler here else global
1547
+ gqlField
1548
+ gql_type_get_field(gqlType type, const char *field) {
1549
+ gqlField f;
1550
+
1551
+ if (NULL == type) {
1552
+ return NULL;
1553
+ }
1554
+ switch (type->kind) {
1555
+ case GQL_OBJECT:
1556
+ case GQL_INTERFACE:
1557
+ for (f = type->fields; NULL != f; f = f->next) {
1558
+ if (0 == strcmp(field, f->name)) {
1559
+ return f;
1560
+ }
1561
+ }
1562
+ break;
1563
+ case GQL_UNION:
1564
+ // TBD
1565
+ break;
1566
+ case GQL_LIST:
1567
+ return gql_type_get_field(type->base, field);
1568
+ break;
1569
+ default:
1570
+ // TBD
1571
+ break;
1572
+ }
1573
+ return NULL;
1096
1574
  }
1575
+