agoo 2.11.7 → 2.13.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.
@@ -424,6 +424,7 @@ parse_value(agooErr err, agooDoc doc) {
424
424
  if (4 <= doc->end - doc->cur &&
425
425
  'r' == doc->cur[1] && 'u' == doc->cur[2] && 'e' == doc->cur[3]) {
426
426
  value = gql_bool_create(err, true);
427
+ doc->cur += 4;
427
428
  } else {
428
429
  agoo_doc_err(doc, err, "invalid token");
429
430
  }
@@ -432,6 +433,7 @@ parse_value(agooErr err, agooDoc doc) {
432
433
  if (5 <= doc->end - doc->cur &&
433
434
  'a' == doc->cur[1] && 'l' == doc->cur[2] && 's' == doc->cur[3] && 'e' == doc->cur[4]) {
434
435
  value = gql_bool_create(err, false);
436
+ doc->cur += 5;
435
437
  } else {
436
438
  agoo_doc_err(doc, err, "invalid token");
437
439
  }
@@ -440,6 +442,7 @@ parse_value(agooErr err, agooDoc doc) {
440
442
  if (4 <= doc->end - doc->cur &&
441
443
  'u' == doc->cur[1] && 'l' == doc->cur[2] && 'l' == doc->cur[3]) {
442
444
  value = gql_null_create(err);
445
+ doc->cur += 4;
443
446
  } else {
444
447
  agoo_doc_err(doc, err, "invalid token");
445
448
  }
@@ -590,8 +590,7 @@ list_destroy(gqlValue value) {
590
590
 
591
591
  while (NULL != (link = value->members)) {
592
592
  value->members = link->next;
593
- gql_value_destroy(link->value);
594
- AGOO_FREE(link);
593
+ gql_link_destroy(link);
595
594
  }
596
595
  }
597
596
 
@@ -679,9 +678,7 @@ object_destroy(gqlValue value) {
679
678
 
680
679
  while (NULL != (link = value->members)) {
681
680
  value->members = link->next;
682
- gql_value_destroy(link->value);
683
- AGOO_FREE(link->key);
684
- AGOO_FREE(link);
681
+ gql_link_destroy(link);
685
682
  }
686
683
  }
687
684
 
@@ -1572,6 +1569,9 @@ int
1572
1569
  gql_value_convert(agooErr err, gqlValue value, gqlType type) {
1573
1570
  int code = AGOO_ERR_OK;
1574
1571
 
1572
+ if (GQL_NON_NULL == type->kind) {
1573
+ type = type->base;
1574
+ }
1575
1575
  if (type != value->type) {
1576
1576
  switch (type->scalar_kind) {
1577
1577
  case GQL_SCALAR_BOOL:
@@ -517,8 +517,7 @@ gql_type_field(agooErr err,
517
517
  gqlType return_type,
518
518
  gqlValue default_value,
519
519
  const char *desc,
520
- size_t dlen,
521
- bool required) {
520
+ size_t dlen) {
522
521
  gqlField f = (gqlField)AGOO_MALLOC(sizeof(struct _gqlField));
523
522
 
524
523
  if (NULL == f) {
@@ -538,7 +537,6 @@ gql_type_field(agooErr err,
538
537
  f->args = NULL;
539
538
  f->dir = NULL;
540
539
  f->default_value = default_value;
541
- f->required = required;
542
540
  if (NULL == type->fields) {
543
541
  type->fields = f;
544
542
  } else {
@@ -559,8 +557,7 @@ gql_field_arg(agooErr err,
559
557
  gqlType type,
560
558
  const char *desc,
561
559
  size_t dlen,
562
- struct _gqlValue *def_value,
563
- bool required) {
560
+ struct _gqlValue *def_value) {
564
561
  gqlArg a = (gqlArg)AGOO_MALLOC(sizeof(struct _gqlArg));
565
562
 
566
563
  if (NULL == a) {
@@ -579,7 +576,6 @@ gql_field_arg(agooErr err,
579
576
  }
580
577
  a->default_value = def_value;
581
578
  a->dir = NULL;
582
- a->required = required;
583
579
  if (NULL == field->args) {
584
580
  field->args = a;
585
581
  } else {
@@ -600,8 +596,7 @@ gql_input_arg(agooErr err,
600
596
  gqlType type,
601
597
  const char *desc,
602
598
  size_t dlen,
603
- struct _gqlValue *def_value,
604
- bool required) {
599
+ struct _gqlValue *def_value) {
605
600
  gqlArg a = (gqlArg)AGOO_MALLOC(sizeof(struct _gqlArg));
606
601
 
607
602
  if (NULL == a) {
@@ -620,7 +615,6 @@ gql_input_arg(agooErr err,
620
615
  }
621
616
  a->default_value = def_value;
622
617
  a->dir = NULL;
623
- a->required = required;
624
618
  if (NULL == input->args) {
625
619
  input->args = a;
626
620
  } else {
@@ -801,8 +795,7 @@ gql_dir_arg(agooErr err,
801
795
  gqlType type,
802
796
  const char *desc,
803
797
  size_t dlen,
804
- struct _gqlValue *def_value,
805
- bool required) {
798
+ struct _gqlValue *def_value) {
806
799
 
807
800
  gqlArg a = (gqlArg)AGOO_MALLOC(sizeof(struct _gqlArg));
808
801
 
@@ -822,7 +815,6 @@ gql_dir_arg(agooErr err,
822
815
  }
823
816
  a->default_value = def_value;
824
817
  a->dir = NULL;
825
- a->required = required;
826
818
  if (NULL == dir->args) {
827
819
  dir->args = a;
828
820
  } else {
@@ -863,11 +855,11 @@ gql_directive_on(agooErr err, gqlDir d, const char *on, int len) {
863
855
  }
864
856
 
865
857
  gqlType
866
- gql_assure_list(agooErr err, gqlType base, bool not_empty) {
858
+ gql_assure_list(agooErr err, gqlType base) {
867
859
  char name[256];
868
860
  gqlType type;
869
861
 
870
- if ((int)sizeof(name) <= snprintf(name, sizeof(name), "[%s%s]", base->name, not_empty ? "!" : "")) {
862
+ if ((int)sizeof(name) <= snprintf(name, sizeof(name), "[%s]", base->name)) {
871
863
  agoo_err_set(err, AGOO_ERR_ARG, "Name too long");
872
864
  return NULL;
873
865
  }
@@ -876,7 +868,24 @@ gql_assure_list(agooErr err, gqlType base, bool not_empty) {
876
868
  return NULL;
877
869
  }
878
870
  type->base = base;
879
- type->not_empty = not_empty;
871
+ }
872
+ return type;
873
+ }
874
+
875
+ gqlType
876
+ gql_assure_nonnull(agooErr err, gqlType base) {
877
+ char name[256];
878
+ gqlType type;
879
+
880
+ if ((int)sizeof(name) <= snprintf(name, sizeof(name), "%s!", base->name)) {
881
+ agoo_err_set(err, AGOO_ERR_ARG, "Name too long");
882
+ return NULL;
883
+ }
884
+ if (NULL == (type = gql_type_get(name))) {
885
+ if (NULL == (type = type_create(err, GQL_NON_NULL, name, NULL, 0))) {
886
+ return NULL;
887
+ }
888
+ type->base = base;
880
889
  }
881
890
  return type;
882
891
  }
@@ -988,9 +997,6 @@ arg_sdl(agooText text, gqlArg a, bool with_desc, bool same_line, bool last) {
988
997
  if (NULL != a->type) { // should always be true
989
998
  text = agoo_text_append(text, a->type->name, -1);
990
999
  }
991
- if (a->required) {
992
- text = agoo_text_append(text, "!", 1);
993
- }
994
1000
  if (NULL != a->default_value) {
995
1001
  text = agoo_text_append(text, " = ", 3);
996
1002
  text = gql_value_sdl(text, a->default_value, 0, 0);
@@ -1024,9 +1030,6 @@ field_sdl(agooText text, gqlField f, bool with_desc) {
1024
1030
  }
1025
1031
  text = agoo_text_append(text, ": ", 2);
1026
1032
  text = agoo_text_append(text, f->type->name, -1);
1027
- if (f->required) {
1028
- text = agoo_text_append(text, "!", 1);
1029
- }
1030
1033
  if (NULL != f->default_value) {
1031
1034
  text = agoo_text_append(text, " = ", 3);
1032
1035
  text = gql_value_sdl(text, f->default_value, 0, 0);
@@ -1204,7 +1207,7 @@ gql_schema_sdl(agooText text, bool with_desc, bool all) {
1204
1207
  for (bucket = buckets, i = 0; i < BUCKET_SIZE; bucket++, i++) {
1205
1208
  for (s = *bucket; NULL != s; s = s->next) {
1206
1209
  type = s->type;
1207
- if (GQL_LIST == type->kind) {
1210
+ if (GQL_LIST == type->kind || GQL_NON_NULL == type->kind) {
1208
1211
  continue;
1209
1212
  }
1210
1213
  if (!all && type->core) {
@@ -1216,11 +1219,12 @@ gql_schema_sdl(agooText text, bool with_desc, bool all) {
1216
1219
  if (0 < cnt) {
1217
1220
  gqlType types[cnt];
1218
1221
  gqlType *tp = types;
1222
+ long len;
1219
1223
 
1220
1224
  for (bucket = buckets, i = 0; i < BUCKET_SIZE; bucket++, i++) {
1221
1225
  for (s = *bucket; NULL != s; s = s->next) {
1222
1226
  type = s->type;
1223
- if (GQL_LIST == type->kind) {
1227
+ if (GQL_LIST == type->kind || GQL_NON_NULL == type->kind) {
1224
1228
  continue;
1225
1229
  }
1226
1230
  if (!all && type->core) {
@@ -1231,8 +1235,9 @@ gql_schema_sdl(agooText text, bool with_desc, bool all) {
1231
1235
  }
1232
1236
  qsort(types, cnt, sizeof(gqlType), type_cmp);
1233
1237
  for (i = 0, tp = types; i < cnt; i++, tp++) {
1238
+ len = text->len;
1234
1239
  text = gql_type_sdl(text, *tp, with_desc);
1235
- if (i < cnt - 1) {
1240
+ if (i < cnt - 1 && len < text->len) {
1236
1241
  text = agoo_text_append(text, "\n", 1);
1237
1242
  }
1238
1243
  }
@@ -1355,6 +1360,9 @@ gql_doc_create(agooErr err) {
1355
1360
  doc->ops = NULL;
1356
1361
  doc->vars = NULL;
1357
1362
  doc->frags = NULL;
1363
+ doc->req = NULL;
1364
+ doc->ctx = NULL;
1365
+ doc->ctx_free = NULL;
1358
1366
  }
1359
1367
  return doc;
1360
1368
  }
@@ -1474,6 +1482,9 @@ gql_doc_destroy(gqlDoc doc) {
1474
1482
  doc->frags = frag->next;
1475
1483
  gql_frag_destroy(frag);
1476
1484
  }
1485
+ if (NULL != doc->ctx_free) {
1486
+ doc->ctx_free(doc->ctx);
1487
+ }
1477
1488
  AGOO_FREE(doc);
1478
1489
  }
1479
1490
 
@@ -88,7 +88,6 @@ typedef struct _gqlArg {
88
88
  struct _gqlType *type;
89
89
  struct _gqlValue *default_value;
90
90
  struct _gqlDirUse *dir;
91
- bool required;
92
91
  } *gqlArg;
93
92
 
94
93
  typedef struct _gqlField {
@@ -99,7 +98,6 @@ typedef struct _gqlField {
99
98
  gqlArg args;
100
99
  struct _gqlDirUse *dir;
101
100
  struct _gqlValue *default_value;
102
- bool required;
103
101
  } *gqlField;
104
102
 
105
103
  typedef struct _gqlDir {
@@ -133,15 +131,12 @@ typedef struct _gqlType {
133
131
  gqlTypeLink types; // Union
134
132
  gqlEnumVal choices; // Enums
135
133
  gqlArg args; // InputObject
134
+ struct _gqlType *base; // List and NonNull types
136
135
  struct { // scalar
137
136
  agooText (*to_sdl)(agooText text, struct _gqlValue *value, int indent, int depth);
138
137
  agooText (*to_json)(agooText text, struct _gqlValue *value, int indent, int depth);
139
138
  void (*destroy)(struct _gqlValue *value);
140
139
  };
141
- struct { // List types
142
- struct _gqlType *base;
143
- bool not_empty;
144
- };
145
140
  };
146
141
  } *gqlType;
147
142
 
@@ -202,6 +197,9 @@ typedef struct _gqlDoc {
202
197
  gqlFrag frags;
203
198
  gqlOp op; // the op to execute
204
199
  struct _gqlFuncs funcs;
200
+ struct _agooReq *req;
201
+ void *ctx;
202
+ void (*ctx_free)(void*);
205
203
  } *gqlDoc;
206
204
 
207
205
  extern int gql_init(agooErr err);
@@ -222,8 +220,7 @@ extern gqlField gql_type_field(agooErr err,
222
220
  gqlType return_type,
223
221
  struct _gqlValue *default_value,
224
222
  const char *desc,
225
- size_t dlen,
226
- bool required);
223
+ size_t dlen);
227
224
 
228
225
  extern gqlArg gql_field_arg(agooErr err,
229
226
  gqlField field,
@@ -231,8 +228,7 @@ extern gqlArg gql_field_arg(agooErr err,
231
228
  gqlType type,
232
229
  const char *desc,
233
230
  size_t dlen,
234
- struct _gqlValue *def_value,
235
- bool required);
231
+ struct _gqlValue *def_value);
236
232
 
237
233
  extern gqlArg gql_input_arg(agooErr err,
238
234
  gqlType input,
@@ -240,8 +236,7 @@ extern gqlArg gql_input_arg(agooErr err,
240
236
  gqlType type,
241
237
  const char *desc,
242
238
  size_t dlen,
243
- struct _gqlValue *def_value,
244
- bool required);
239
+ struct _gqlValue *def_value);
245
240
 
246
241
  extern gqlType gql_scalar_create(agooErr err, const char *name, const char *desc, size_t dlen);
247
242
 
@@ -252,8 +247,7 @@ extern gqlArg gql_dir_arg(agooErr err,
252
247
  gqlType type,
253
248
  const char *desc,
254
249
  size_t dlen,
255
- struct _gqlValue *def_value,
256
- bool required);
250
+ struct _gqlValue *def_value);
257
251
  extern int gql_directive_on(agooErr err, gqlDir d, const char *on, int len);
258
252
  extern gqlDir gql_directive_get(const char *name);
259
253
 
@@ -266,7 +260,8 @@ extern int gql_union_add(agooErr err, gqlType type, gqlType member);
266
260
  extern gqlType gql_enum_create(agooErr err, const char *name, const char *desc, size_t dlen);
267
261
  extern gqlEnumVal gql_enum_append(agooErr err, gqlType type, const char *value, size_t len, const char *desc, size_t dlen);
268
262
 
269
- extern gqlType gql_assure_list(agooErr err, gqlType base, bool not_empty);
263
+ extern gqlType gql_assure_list(agooErr err, gqlType base);
264
+ extern gqlType gql_assure_nonnull(agooErr err, gqlType base);
270
265
 
271
266
  extern int gql_type_set(agooErr err, gqlType type);
272
267
  extern gqlType gql_type_get(const char *name);
@@ -291,6 +286,7 @@ extern agooText gql_doc_sdl(gqlDoc doc, agooText text);
291
286
  extern void gql_dump_hook(struct _agooReq *req);
292
287
  extern void gql_eval_get_hook(struct _agooReq *req);
293
288
  extern void gql_eval_post_hook(struct _agooReq *req);
289
+ extern void gql_eval_options_hook(struct _agooReq *req);
294
290
 
295
291
  extern int gql_validate(agooErr err);
296
292
 
@@ -14,7 +14,7 @@ agooReq
14
14
  agoo_req_create(size_t mlen) {
15
15
  size_t size = mlen + sizeof(struct _agooReq) - 7;
16
16
  agooReq req = (agooReq)AGOO_MALLOC(size);
17
-
17
+
18
18
  if (NULL != req) {
19
19
  memset(req, 0, size);
20
20
  req->env = agoo_server.env_nil_value;
@@ -56,7 +56,7 @@ agoo_req_port(agooReq r) {
56
56
  int len;
57
57
  const char *host;
58
58
  const char *colon;
59
-
59
+
60
60
  if (NULL == (host = agoo_con_header_value(r->header.start, r->header.len, "Host", &len))) {
61
61
  return 0;
62
62
  }
@@ -94,7 +94,7 @@ agoo_req_query_value(agooReq r, const char *key, int klen, int *vlenp) {
94
94
  static int
95
95
  hexVal(int c) {
96
96
  int h = -1;
97
-
97
+
98
98
  if ('0' <= c && c <= '9') {
99
99
  h = c - '0';
100
100
  } else if ('a' <= c && c <= 'f') {
@@ -110,12 +110,12 @@ agoo_req_query_decode(char *s, int len) {
110
110
  char *sn = s;
111
111
  char *so = s;
112
112
  char *end = s + len;
113
-
113
+
114
114
  while (so < end) {
115
115
  if ('%' == *so) {
116
116
  int n;
117
117
  int c = 0;
118
-
118
+
119
119
  so++;
120
120
  if (0 > (c = hexVal(*so))) {
121
121
  *sn++ = '%';
@@ -133,7 +133,7 @@ agoo_req_query_decode(char *s, int len) {
133
133
  }
134
134
  }
135
135
  *sn = '\0';
136
-
136
+
137
137
  return (int)(sn - s);
138
138
  }
139
139
 
@@ -145,9 +145,9 @@ agoo_req_header_value(agooReq req, const char *key, int *vlen) {
145
145
  const char *hend = h + req->header.len;
146
146
  const char *value;
147
147
  int klen = (int)strlen(key);
148
-
148
+
149
149
  while (h < hend) {
150
- if (0 == strncmp(key, h, klen) && ':' == h[klen]) {
150
+ if (0 == strncasecmp(key, h, klen) && ':' == h[klen]) {
151
151
  h += klen + 1;
152
152
  for (; ' ' == *h; h++) {
153
153
  }
@@ -155,7 +155,7 @@ agoo_req_header_value(agooReq req, const char *key, int *vlen) {
155
155
  for (; '\r' != *h && '\0' != *h; h++) {
156
156
  }
157
157
  *vlen = (int)(h - value);
158
-
158
+
159
159
  return value;
160
160
  }
161
161
  for (; h < hend; h++) {
@@ -167,4 +167,3 @@ agoo_req_header_value(agooReq req, const char *key, int *vlen) {
167
167
  }
168
168
  return NULL;
169
169
  }
170
-
@@ -13,6 +13,7 @@
13
13
  #include "gqlvalue.h"
14
14
  #include "graphql.h"
15
15
  #include "pub.h"
16
+ #include "request.h"
16
17
  #include "sdl.h"
17
18
  #include "server.h"
18
19
 
@@ -27,13 +28,31 @@ typedef struct _typeClass {
27
28
  const char *classname;
28
29
  } *TypeClass;
29
30
 
31
+ typedef struct _bhArgs {
32
+ agooErr err;
33
+ agooReq req;
34
+ agooText headers;
35
+ } *bhArgs;
36
+
30
37
  static VALUE graphql_class = Qundef;
31
38
  static VALUE vroot = Qnil;
39
+ static VALUE build_headers_func = Qnil;
40
+ static VALUE arg_clas = Qnil;
41
+ static VALUE field_clas = Qnil;
42
+ static VALUE type_clas = Qnil;
43
+
44
+ static ID call_id;
32
45
 
33
46
  static TypeClass type_class_map = NULL;
34
47
 
35
48
  static int
36
- make_ruby_use(agooErr err, VALUE root, const char *method, const char *type_name, bool fresh, gqlType schema_type, const char *desc) {
49
+ make_ruby_use(agooErr err,
50
+ VALUE root,
51
+ const char *method,
52
+ const char *type_name,
53
+ bool fresh,
54
+ gqlType schema_type,
55
+ const char *desc) {
37
56
  gqlType type;
38
57
  gqlDirUse use;
39
58
  volatile VALUE v;
@@ -47,7 +66,7 @@ make_ruby_use(agooErr err, VALUE root, const char *method, const char *type_name
47
66
  return agoo_err_set(err, AGOO_ERR_ARG, "Failed to find the '%s' type.", type_name);
48
67
  }
49
68
  if (fresh) {
50
- if (NULL == gql_type_field(err, schema_type, method, type, NULL, desc, 0, false)) {
69
+ if (NULL == gql_type_field(err, schema_type, method, type, NULL, desc, 0)) {
51
70
  return err->code;
52
71
  }
53
72
  }
@@ -85,7 +104,7 @@ call_eval(VALUE x) {
85
104
 
86
105
  static void*
87
106
  protect_eval(void *x) {
88
- rb_rescue2(call_eval, (VALUE)x, rescue_error, (VALUE)x, rb_eException, 0);
107
+ rb_rescue2(call_eval, (VALUE)x, rescue_error, (VALUE)x, rb_eException, (VALUE)0);
89
108
 
90
109
  return NULL;
91
110
  }
@@ -225,6 +244,9 @@ coerce(agooErr err, gqlRef ref, gqlType type) {
225
244
  if (Qnil == (VALUE)ref) {
226
245
  return gql_null_create(err);
227
246
  }
247
+ if (NULL != type && GQL_NON_NULL == type->kind) {
248
+ type = type->base;
249
+ }
228
250
  if (NULL == type) {
229
251
  // This is really an error but make a best effort anyway.
230
252
  switch (rb_type((VALUE)ref)) {
@@ -372,12 +394,25 @@ ref_type(gqlRef ref) {
372
394
  return type;
373
395
  }
374
396
 
397
+ static VALUE
398
+ make_plan(gqlSel sel) {
399
+
400
+ // TBD create a plan object that wraps the sel, create children as needed
401
+ // plan is not valid outside of method
402
+ // should be able to create SDL from it
403
+
404
+ return Qnil;
405
+ }
406
+
375
407
  static int
376
408
  resolve(agooErr err, gqlDoc doc, gqlRef target, gqlField field, gqlSel sel, gqlValue result, int depth) {
377
409
  volatile VALUE child;
410
+ volatile VALUE rreq = Qnil;
378
411
  VALUE obj = (VALUE)target;
379
412
  int d2 = depth + 1;
380
413
  const char *key = sel->name;
414
+ ID method;
415
+ int arity;
381
416
 
382
417
  if ('_' == *sel->name && '_' == sel->name[1]) {
383
418
  if (0 == strcmp("__typename", sel->name)) {
@@ -397,8 +432,10 @@ resolve(agooErr err, gqlDoc doc, gqlRef target, gqlField field, gqlSel sel, gqlV
397
432
  return agoo_err_set(err, AGOO_ERR_EVAL, "Not a valid operation on the root object.");
398
433
  }
399
434
  }
400
- if (NULL == sel->args) {
401
- child = rb_funcall(obj, rb_intern(sel->name), 0);
435
+ method = rb_intern(sel->name);
436
+ arity = rb_obj_method_arity(obj, method);
437
+ if (0 == arity) {
438
+ child = rb_funcall(obj, method, 0);
402
439
  } else {
403
440
  volatile VALUE rargs = rb_hash_new();
404
441
  gqlSelArg sa;
@@ -429,7 +466,21 @@ resolve(agooErr err, gqlDoc doc, gqlRef target, gqlField field, gqlSel sel, gqlV
429
466
  }
430
467
  rb_hash_aset(rargs, rb_str_new_cstr(sa->name), gval_to_ruby(v));
431
468
  }
432
- child = rb_funcall(obj, rb_intern(sel->name), 1, rargs);
469
+ if (-1 == arity || 1 == arity) {
470
+ child = rb_funcall(obj, method, 1, rargs);
471
+ } else {
472
+ if (NULL == doc->ctx) {
473
+ rreq = request_wrap(doc->req);
474
+ doc->ctx = (void*)rreq;
475
+ } else {
476
+ rreq = (VALUE)doc->ctx;
477
+ }
478
+ if (-2 == arity || 2 == arity) {
479
+ child = rb_funcall(obj, method, 2, rargs, rreq);
480
+ } else {
481
+ child = rb_funcall(obj, method, 3, rargs, rreq, make_plan(sel));
482
+ }
483
+ }
433
484
  }
434
485
  if (GQL_SUBSCRIPTION == doc->op->kind && RUBY_T_STRING == rb_type(child)) {
435
486
  gqlValue c;
@@ -591,6 +642,7 @@ graphql_schema(VALUE self, VALUE root) {
591
642
  gqlDirUse use;
592
643
  gqlType schema_type;
593
644
  bool fresh = false;
645
+ gqlType string_nn;
594
646
 
595
647
  if (!rb_block_given_p()) {
596
648
  rb_raise(rb_eStandardError, "A block is required.");
@@ -603,7 +655,8 @@ graphql_schema(VALUE self, VALUE root) {
603
655
  printf("*-*-* %s\n", err.msg);
604
656
  exit(2);
605
657
  }
606
- if (NULL == gql_dir_arg(&err, dir, "class", &gql_string_type, NULL, 0, NULL, true)) {
658
+ if (NULL == (string_nn = gql_assure_nonnull(&err, &gql_string_type)) ||
659
+ NULL == gql_dir_arg(&err, dir, "class", string_nn, NULL, 0, NULL)) {
607
660
  printf("*-*-* %s\n", err.msg);
608
661
  exit(3);
609
662
  }
@@ -629,7 +682,7 @@ graphql_schema(VALUE self, VALUE root) {
629
682
  printf("*-*-* %s\n", err.msg);
630
683
  exit(6);
631
684
  }
632
- rb_rescue2(rescue_yield, Qnil, rescue_yield_error, (VALUE)&err, rb_eException, 0);
685
+ rb_rescue2(rescue_yield, Qnil, rescue_yield_error, (VALUE)&err, rb_eException, (VALUE)0);
633
686
  if (AGOO_ERR_OK != err.code) {
634
687
  printf("*-*-* %s\n", err.msg);
635
688
  exit(7);
@@ -724,16 +777,16 @@ graphql_load_file(VALUE self, VALUE path) {
724
777
  return Qnil;
725
778
  }
726
779
 
727
- /* Document-method: dump_sdl
780
+ /* Document-method: sdl_dump
728
781
  *
729
- * call-seq: dump_sdl()
782
+ * call-seq: sdl_dump()
730
783
  *
731
784
  * The preferred method of inspecting a GraphQL schema is to use introspection
732
785
  * queries but for debugging and for reviewing the schema a dump of the schema
733
- * as SDL can be helpful. The _#dump_sdl_ method returns the schema as an SDL
786
+ * as SDL can be helpful. The _#sdl_dump_ method returns the schema as an SDL
734
787
  * string.
735
788
  *
736
- * - *options* [_Hash_] server options
789
+ * - *options* [_Hash_] options
737
790
  *
738
791
  * - *:with_description* [_true_|_false_] if true the description strings are included. If false they are not included.
739
792
  *
@@ -767,6 +820,79 @@ graphql_sdl_dump(VALUE self, VALUE options) {
767
820
  return dump;
768
821
  }
769
822
 
823
+ static void
824
+ type_cb(gqlType type, void *ctx) {
825
+ VALUE rtypes = (VALUE)ctx;
826
+ VALUE t;
827
+
828
+ if (GQL_OBJECT != type->kind || type->core) {
829
+ return;
830
+ }
831
+ t = rb_obj_alloc(type_clas);
832
+ rb_ivar_set(t, rb_intern("@name"), rb_str_new_cstr(type->name));
833
+ if (NULL != type->desc) {
834
+ rb_ivar_set(t, rb_intern("@description"), rb_str_new_cstr(type->desc));
835
+ }
836
+ if (NULL != type->fields) {
837
+ VALUE fields = rb_ary_new();
838
+ VALUE field;
839
+ gqlField f;
840
+
841
+ rb_ivar_set(t, rb_intern("@fields"), fields);
842
+ for (f = type->fields; NULL != f; f = f->next) {
843
+ field = rb_obj_alloc(field_clas);
844
+ rb_ary_push(fields, field);
845
+ rb_ivar_set(field, rb_intern("@name"), rb_str_new_cstr(f->name));
846
+ if (NULL != f->desc) {
847
+ rb_ivar_set(field, rb_intern("@description"), rb_str_new_cstr(f->desc));
848
+ }
849
+ if (NULL != f->type) {
850
+ rb_ivar_set(field, rb_intern("@type_name"), rb_str_new_cstr(f->type->name));
851
+ }
852
+ if (NULL != f->default_value) {
853
+ rb_ivar_set(field, rb_intern("@default_value"), gval_to_ruby(f->default_value));
854
+ }
855
+ if (NULL != f->args) {
856
+ VALUE args = rb_ary_new();
857
+ VALUE arg;
858
+ gqlArg a;
859
+
860
+ rb_ivar_set(field, rb_intern("@args"), args);
861
+ for (a = f->args; NULL != a; a = a->next) {
862
+ arg = rb_obj_alloc(arg_clas);
863
+ rb_ary_push(args, arg);
864
+ rb_ivar_set(arg, rb_intern("@name"), rb_str_new_cstr(a->name));
865
+ if (NULL != a->desc) {
866
+ rb_ivar_set(arg, rb_intern("@description"), rb_str_new_cstr(a->desc));
867
+ }
868
+ if (NULL != a->type) {
869
+ rb_ivar_set(arg, rb_intern("@type_name"), rb_str_new_cstr(a->type->name));
870
+ }
871
+ if (NULL != a->default_value) {
872
+ rb_ivar_set(arg, rb_intern("@default_value"), gval_to_ruby(a->default_value));
873
+ }
874
+ }
875
+ }
876
+ }
877
+ }
878
+ rb_ary_push(rtypes, t);
879
+ }
880
+
881
+ /* Document-method: sdl_types
882
+ *
883
+ * call-seq: sdl_types()
884
+ *
885
+ * Returns an array of all SDL types as Ruby objects.
886
+ */
887
+ static VALUE
888
+ graphql_sdl_types(VALUE self) {
889
+ VALUE rtypes = rb_ary_new();
890
+
891
+ gql_type_iterate(type_cb, (void*)rtypes);
892
+
893
+ return rtypes;
894
+ }
895
+
770
896
  /* Document-method: publish
771
897
  *
772
898
  * call-seq: publish(subject, event)
@@ -791,6 +917,105 @@ graphql_publish(VALUE self, VALUE subject, VALUE event) {
791
917
  return Qnil;
792
918
  }
793
919
 
920
+ static VALUE
921
+ rescue_build_header(VALUE x, VALUE ignore) {
922
+ bhArgs args = (bhArgs)x;
923
+ volatile VALUE info = rb_errinfo();
924
+ volatile VALUE msg = rb_funcall(info, rb_intern("message"), 0);
925
+
926
+ agoo_err_set(args->err, AGOO_ERR_EVAL, "%s", rb_string_value_ptr(&msg));
927
+
928
+ return Qnil;
929
+ }
930
+
931
+ static int
932
+ build_headers_cb(VALUE key, VALUE value, VALUE x) {
933
+ bhArgs args = (bhArgs)x;
934
+ const char *ks = rb_string_value_ptr((VALUE*)&key);
935
+ volatile VALUE vs = rb_obj_as_string(value);
936
+ const char *s = rb_string_value_ptr((VALUE*)&vs);
937
+
938
+ if (NULL == (args->headers = gql_add_header(args->err, args->headers, ks, s))) {
939
+ rb_raise(rb_eStandardError, "%s", args->err->msg);
940
+ }
941
+ return ST_CONTINUE;
942
+ }
943
+
944
+ static VALUE
945
+ inner_build_headers(VALUE x) {
946
+ volatile VALUE hh = rb_funcall(build_headers_func, call_id, 1, request_wrap(((bhArgs)x)->req));
947
+
948
+ rb_hash_foreach(hh, build_headers_cb, x);
949
+
950
+ return Qnil;
951
+ }
952
+
953
+ static void*
954
+ protected_build_headers(void *x) {
955
+ return (void*)rb_rescue2(inner_build_headers, (VALUE)x, rescue_build_header, (VALUE)x, rb_eException, (VALUE)0);
956
+ }
957
+
958
+ static agooText
959
+ build_headers(agooErr err, agooReq req, agooText headers) {
960
+ struct _bhArgs args = {
961
+ .err = err,
962
+ .req = req,
963
+ .headers = headers,
964
+ };
965
+ rb_thread_call_with_gvl(protected_build_headers, &args);
966
+
967
+ return args.headers;
968
+ }
969
+
970
+ /* Document-method: build_headers=
971
+ *
972
+ * call-seq: build_headers=(func)
973
+ *
974
+ * Provide a function to call that builds headers for GraphQL responses. The
975
+ * function should expect a single request and should return a Hash of the
976
+ * headers to add. Content-Type and Content-Length should not be set.
977
+ */
978
+ static VALUE
979
+ graphql_build_headers(VALUE self, VALUE func) {
980
+ gql_build_headers = build_headers;
981
+ build_headers_func = func;
982
+ rb_gc_register_address(&build_headers_func);
983
+
984
+ return Qnil;
985
+ }
986
+
987
+ static int
988
+ headers_cb(VALUE key, VALUE value, VALUE x) {
989
+ agooText *tp = (agooText*)x;
990
+ const char *ks = rb_string_value_ptr((VALUE*)&key);
991
+ volatile VALUE vs = rb_obj_as_string(value);
992
+ const char *s = rb_string_value_ptr((VALUE*)&vs);
993
+ struct _agooErr err = AGOO_ERR_INIT;
994
+
995
+ if (NULL == (*tp = gql_add_header(&err, *tp, ks, s))) {
996
+ rb_raise(rb_eStandardError, "%s", err.msg);
997
+ }
998
+ return ST_CONTINUE;
999
+ }
1000
+
1001
+
1002
+ /* Document-method: headers
1003
+ *
1004
+ * call-seq: headers(header_hash)
1005
+ *
1006
+ * Provide a Hash to be used as the headers for GraphQL responses.
1007
+ * Content-Type and Content-Length should not be set.
1008
+ */
1009
+ static VALUE
1010
+ graphql_headers(VALUE self, VALUE map) {
1011
+ agooText t = agoo_text_allocate(1024);
1012
+
1013
+ rb_hash_foreach(map, headers_cb, (VALUE)&t);
1014
+ gql_headers = t;
1015
+
1016
+ return Qnil;
1017
+ }
1018
+
794
1019
  /* Document-class: Agoo::Graphql
795
1020
  *
796
1021
  * The Agoo::GraphQL class provides support for the GraphQL API as defined in
@@ -814,5 +1039,16 @@ graphql_init(VALUE mod) {
814
1039
 
815
1040
  rb_define_module_function(graphql_class, "sdl_dump", graphql_sdl_dump, 1);
816
1041
 
1042
+ rb_define_module_function(graphql_class, "sdl_types", graphql_sdl_types, 0);
1043
+
817
1044
  rb_define_module_function(graphql_class, "publish", graphql_publish, 2);
1045
+
1046
+ rb_define_module_function(graphql_class, "build_headers=", graphql_build_headers, 1);
1047
+ rb_define_module_function(graphql_class, "headers", graphql_headers, 1);
1048
+
1049
+ arg_clas = rb_const_get_at(graphql_class, rb_intern("Arg"));
1050
+ field_clas = rb_const_get_at(graphql_class, rb_intern("Field"));
1051
+ type_clas = rb_const_get_at(graphql_class, rb_intern("Type"));
1052
+
1053
+ call_id = rb_intern("call");
818
1054
  }