agoo 2.5.4 → 2.5.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of agoo might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/bin/agoo +3 -1
- data/ext/agoo/base64.h +3 -3
- data/ext/agoo/bind.c +1 -1
- data/ext/agoo/bind.h +3 -3
- data/ext/agoo/con.c +4 -2
- data/ext/agoo/con.h +3 -3
- data/ext/agoo/debug.c +7 -0
- data/ext/agoo/debug.h +13 -6
- data/ext/agoo/doc.c +159 -0
- data/ext/agoo/doc.h +34 -0
- data/ext/agoo/dtime.h +3 -3
- data/ext/agoo/err.h +3 -3
- data/ext/agoo/error_stream.h +3 -3
- data/ext/agoo/extconf.rb +1 -1
- data/ext/agoo/gqlintro.c +333 -0
- data/ext/agoo/gqlintro.h +10 -0
- data/ext/agoo/gqlvalue.c +1035 -0
- data/ext/agoo/gqlvalue.h +88 -0
- data/ext/agoo/graphql.c +1078 -0
- data/ext/agoo/graphql.h +198 -0
- data/ext/agoo/hook.c +2 -0
- data/ext/agoo/hook.h +4 -3
- data/ext/agoo/http.c +2 -1
- data/ext/agoo/http.h +3 -3
- data/ext/agoo/kinds.h +3 -3
- data/ext/agoo/log.h +3 -3
- data/ext/agoo/log_queue.h +3 -3
- data/ext/agoo/method.h +3 -3
- data/ext/agoo/page.h +3 -3
- data/ext/agoo/pub.h +3 -3
- data/ext/agoo/queue.h +3 -3
- data/ext/agoo/rack_logger.h +3 -3
- data/ext/agoo/req.c +2 -2
- data/ext/agoo/req.h +3 -3
- data/ext/agoo/request.h +3 -3
- data/ext/agoo/res.h +3 -3
- data/ext/agoo/response.h +3 -3
- data/ext/agoo/rhook.c +2 -2
- data/ext/agoo/rhook.h +4 -3
- data/ext/agoo/rlog.h +3 -3
- data/ext/agoo/rresponse.h +3 -3
- data/ext/agoo/rserver.c +64 -0
- data/ext/agoo/rserver.h +3 -3
- data/ext/agoo/rupgraded.c +1 -1
- data/ext/agoo/rupgraded.h +3 -3
- data/ext/agoo/sdl.c +334 -0
- data/ext/agoo/sdl.h +10 -0
- data/ext/agoo/seg.h +3 -3
- data/ext/agoo/server.c +3 -1
- data/ext/agoo/server.h +5 -4
- data/ext/agoo/sha1.h +3 -3
- data/ext/agoo/sse.h +3 -3
- data/ext/agoo/sub.h +3 -3
- data/ext/agoo/subject.h +3 -3
- data/ext/agoo/text.h +3 -3
- data/ext/agoo/upgraded.h +3 -3
- data/ext/agoo/websocket.h +3 -3
- data/lib/agoo/version.rb +1 -1
- data/lib/rack/handler/agoo.rb +3 -1
- metadata +12 -12
- data/ext/agoo/foo/agoo.c +0 -109
- data/ext/agoo/foo/con.c +0 -1220
- data/ext/agoo/foo/con.h +0 -65
- data/ext/agoo/foo/page.c +0 -699
- data/ext/agoo/foo/pub.c +0 -131
- data/ext/agoo/foo/pub.h +0 -40
- data/ext/agoo/foo/rserver.c +0 -1016
- data/ext/agoo/foo/server.c +0 -303
- data/ext/agoo/foo/server.h +0 -67
- data/ext/agoo/foo/upgraded.c +0 -182
data/ext/agoo/gqlvalue.h
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#ifndef AGOO_GQLVALUE_H
|
4
|
+
#define AGOO_GQLVALUE_H
|
5
|
+
|
6
|
+
#include <stdbool.h>
|
7
|
+
#include <stdint.h>
|
8
|
+
|
9
|
+
#include "err.h"
|
10
|
+
#include "text.h"
|
11
|
+
|
12
|
+
struct _gqlType;
|
13
|
+
|
14
|
+
typedef struct _gqlLink {
|
15
|
+
struct _gqlLink *next;
|
16
|
+
char *key;
|
17
|
+
struct _gqlValue *value;
|
18
|
+
} *gqlLink;
|
19
|
+
|
20
|
+
typedef struct _gqlValue {
|
21
|
+
struct _gqlType *type;
|
22
|
+
union {
|
23
|
+
char *url;
|
24
|
+
int32_t i;
|
25
|
+
int64_t i64;
|
26
|
+
double f;
|
27
|
+
bool b;
|
28
|
+
int64_t time;
|
29
|
+
char *str;
|
30
|
+
char str16[16];
|
31
|
+
struct {
|
32
|
+
uint64_t hi;
|
33
|
+
uint64_t lo;
|
34
|
+
} uuid;
|
35
|
+
struct {
|
36
|
+
struct _gqlLink *members; // linked list for List and Object types
|
37
|
+
struct _gqlType *member_type;
|
38
|
+
};
|
39
|
+
};
|
40
|
+
} *gqlValue;
|
41
|
+
|
42
|
+
extern int gql_value_init(Err err);
|
43
|
+
|
44
|
+
extern void gql_value_destroy(gqlValue value);
|
45
|
+
|
46
|
+
extern gqlValue gql_int_create(Err err, int32_t i);
|
47
|
+
extern gqlValue gql_i64_create(Err err, int64_t i);
|
48
|
+
extern gqlValue gql_string_create(Err err, const char *str, int len);
|
49
|
+
extern gqlValue gql_url_create(Err err, const char *url, int len);
|
50
|
+
extern gqlValue gql_bool_create(Err err, bool b);
|
51
|
+
extern gqlValue gql_float_create(Err err, double f);
|
52
|
+
extern gqlValue gql_time_create(Err err, int64_t t);
|
53
|
+
extern gqlValue gql_time_str_create(Err err, const char *str, int len);
|
54
|
+
extern gqlValue gql_uuid_create(Err err, uint64_t hi, uint64_t lo);
|
55
|
+
extern gqlValue gql_uuid_str_create(Err err, const char *str, int len);
|
56
|
+
extern gqlValue gql_null_create(Err err);
|
57
|
+
extern gqlValue gql_list_create(Err err, struct _gqlType *itemType);
|
58
|
+
extern gqlValue gql_object_create(Err err);
|
59
|
+
|
60
|
+
extern int gql_list_append(Err err, gqlValue list, gqlValue item);
|
61
|
+
extern int gql_list_preend(Err err, gqlValue list, gqlValue item);
|
62
|
+
extern int gql_object_set(Err err, gqlValue obj, const char *key, gqlValue item);
|
63
|
+
|
64
|
+
extern void gql_int_set(gqlValue value, int32_t i);
|
65
|
+
extern void gql_i64_set(gqlValue value, int64_t i);
|
66
|
+
extern void gql_string_set(gqlValue value, const char *str, int len);
|
67
|
+
extern int gql_url_set(Err err, gqlValue value, const char *url, int len);
|
68
|
+
extern void gql_bool_set(gqlValue value, bool b);
|
69
|
+
extern void gql_float_set(gqlValue value, double f);
|
70
|
+
extern void gql_time_set(gqlValue value, int64_t t);
|
71
|
+
extern int gql_time_str_set(Err err, gqlValue value, const char *str, int len);
|
72
|
+
extern void gql_uuid_set(gqlValue value, uint64_t hi, uint64_t lo);
|
73
|
+
extern int gql_uuid_str_set(Err err, gqlValue value, const char *str, int len);
|
74
|
+
extern void gql_null_set(gqlValue value);
|
75
|
+
|
76
|
+
extern Text gql_value_json(Text text, gqlValue value, int indent, int depth);
|
77
|
+
|
78
|
+
extern struct _gqlType gql_null_type;
|
79
|
+
extern struct _gqlType gql_int_type;
|
80
|
+
extern struct _gqlType gql_i64_type;
|
81
|
+
extern struct _gqlType gql_bool_type;
|
82
|
+
extern struct _gqlType gql_float_type;
|
83
|
+
extern struct _gqlType gql_time_type;
|
84
|
+
extern struct _gqlType gql_uuid_type;
|
85
|
+
extern struct _gqlType gql_url_type;
|
86
|
+
extern struct _gqlType gql_string_type;
|
87
|
+
|
88
|
+
#endif // AGOO_GQLVALUE_H
|
data/ext/agoo/graphql.c
ADDED
@@ -0,0 +1,1078 @@
|
|
1
|
+
// Copyright (c) 2018, Peter Ohler, All rights reserved.
|
2
|
+
|
3
|
+
#include <stdio.h>
|
4
|
+
#include <stdlib.h>
|
5
|
+
#include <string.h>
|
6
|
+
|
7
|
+
#include "debug.h"
|
8
|
+
#include "graphql.h"
|
9
|
+
#include "gqlintro.h"
|
10
|
+
#include "gqlvalue.h"
|
11
|
+
#include "req.h"
|
12
|
+
#include "res.h"
|
13
|
+
|
14
|
+
#define BUCKET_SIZE 64
|
15
|
+
#define BUCKET_MASK 63
|
16
|
+
#define BAD_NAME (uint64_t)-1
|
17
|
+
|
18
|
+
typedef struct _Slot {
|
19
|
+
struct _Slot *next;
|
20
|
+
gqlType type;
|
21
|
+
uint64_t hash;
|
22
|
+
} *Slot;
|
23
|
+
|
24
|
+
static Slot buckets[BUCKET_SIZE];
|
25
|
+
|
26
|
+
static uint8_t name_chars[256] = "\
|
27
|
+
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
28
|
+
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
29
|
+
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
30
|
+
\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x00\x00\x00\x00\x00\x00\
|
31
|
+
\x00\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\
|
32
|
+
\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x00\x00\x00\x00\x25\
|
33
|
+
\x00\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\
|
34
|
+
\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x00\x00\x00\x00\x00\
|
35
|
+
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
36
|
+
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
37
|
+
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
38
|
+
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
39
|
+
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
40
|
+
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
41
|
+
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
42
|
+
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
|
43
|
+
";
|
44
|
+
|
45
|
+
static const char spaces[16] = " ";
|
46
|
+
|
47
|
+
static gqlType query_type = NULL;
|
48
|
+
static gqlType mutation_type = NULL;
|
49
|
+
static gqlType subscription_type = NULL;
|
50
|
+
static gqlType schema_type = NULL;
|
51
|
+
static gqlDir directives = NULL;
|
52
|
+
|
53
|
+
static uint64_t
|
54
|
+
calc_hash(const char *name) {
|
55
|
+
uint64_t h = 0;
|
56
|
+
const uint8_t *k = (const uint8_t*)name;
|
57
|
+
uint64_t x;
|
58
|
+
|
59
|
+
for (; '\0' != *k; k++) {
|
60
|
+
if (0 == (x = name_chars[*k])) {
|
61
|
+
return BAD_NAME;
|
62
|
+
}
|
63
|
+
h = 37 * h + x; // fast, just spread it out
|
64
|
+
}
|
65
|
+
return h;
|
66
|
+
}
|
67
|
+
|
68
|
+
static Slot*
|
69
|
+
get_bucketp(uint64_t h) {
|
70
|
+
return buckets + (BUCKET_MASK & (h ^ (h << 5) ^ (h >> 7)));
|
71
|
+
}
|
72
|
+
|
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);
|
101
|
+
}
|
102
|
+
free(type->interfaces);
|
103
|
+
break;
|
104
|
+
}
|
105
|
+
case GQL_UNION: {
|
106
|
+
gqlTypeLink link;
|
107
|
+
|
108
|
+
while (NULL != (link = type->types)) {
|
109
|
+
type->types = link->next;
|
110
|
+
free(link->name);
|
111
|
+
free(link);
|
112
|
+
}
|
113
|
+
break;
|
114
|
+
}
|
115
|
+
case GQL_ENUM: {
|
116
|
+
gqlStrLink link;
|
117
|
+
|
118
|
+
while (NULL != (link = type->choices)) {
|
119
|
+
type->choices = link->next;
|
120
|
+
free(link->str);
|
121
|
+
free(link);
|
122
|
+
}
|
123
|
+
break;
|
124
|
+
}
|
125
|
+
default:
|
126
|
+
break;
|
127
|
+
}
|
128
|
+
DEBUG_FREE(mem_graphql_type, type);
|
129
|
+
free(type);
|
130
|
+
}
|
131
|
+
}
|
132
|
+
|
133
|
+
static void
|
134
|
+
dir_destroy(gqlDir dir) {
|
135
|
+
gqlArg a;
|
136
|
+
gqlStrLink link;
|
137
|
+
|
138
|
+
free((char*)dir->name);
|
139
|
+
free((char*)dir->desc);
|
140
|
+
while (NULL != (a = dir->args)) {
|
141
|
+
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);
|
148
|
+
}
|
149
|
+
while (NULL != (link = dir->locs)) {
|
150
|
+
dir->locs = link->next;
|
151
|
+
free(link->str);
|
152
|
+
free(link);
|
153
|
+
}
|
154
|
+
DEBUG_FREE(mem_graphql_directive, dir);
|
155
|
+
free(dir);
|
156
|
+
}
|
157
|
+
|
158
|
+
gqlType
|
159
|
+
gql_type_get(const char *name) {
|
160
|
+
gqlType type = NULL;
|
161
|
+
uint64_t h = calc_hash(name);
|
162
|
+
|
163
|
+
if (0 < h) {
|
164
|
+
Slot *bucket = get_bucketp(h);
|
165
|
+
Slot s;
|
166
|
+
|
167
|
+
for (s = *bucket; NULL != s; s = s->next) {
|
168
|
+
if (h == s->hash && 0 == strcmp(s->type->name, name)) {
|
169
|
+
type = s->type;
|
170
|
+
break;
|
171
|
+
}
|
172
|
+
}
|
173
|
+
}
|
174
|
+
return type;
|
175
|
+
}
|
176
|
+
|
177
|
+
gqlDir
|
178
|
+
gql_directive_get(const char *name) {
|
179
|
+
gqlDir dir = directives;
|
180
|
+
|
181
|
+
for (; NULL != dir; dir = dir->next) {
|
182
|
+
if (0 == strcmp(name, dir->name)) {
|
183
|
+
break;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
return dir;
|
187
|
+
}
|
188
|
+
|
189
|
+
int
|
190
|
+
gql_type_set(Err err, gqlType type) {
|
191
|
+
uint64_t h = calc_hash(type->name);
|
192
|
+
|
193
|
+
if (h <= 0) {
|
194
|
+
return err_set(err, ERR_ARG, "%s is not a valid GraphQL type name.", type->name);
|
195
|
+
} else {
|
196
|
+
Slot *bucket = get_bucketp(h);
|
197
|
+
Slot s;
|
198
|
+
|
199
|
+
for (s = *bucket; NULL != s; s = s->next) {
|
200
|
+
if (h == s->hash && 0 == strcmp(s->type->name, type->name)) {
|
201
|
+
type_destroy(s->type);
|
202
|
+
s->type = type;
|
203
|
+
return ERR_OK;
|
204
|
+
}
|
205
|
+
}
|
206
|
+
if (NULL == (s = (Slot)malloc(sizeof(struct _Slot)))) {
|
207
|
+
return err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL type.");
|
208
|
+
}
|
209
|
+
DEBUG_ALLOC(mem_graphql_slot, s);
|
210
|
+
s->hash = h;
|
211
|
+
s->type = type;
|
212
|
+
s->next = *bucket;
|
213
|
+
*bucket = s;
|
214
|
+
}
|
215
|
+
return ERR_OK;
|
216
|
+
}
|
217
|
+
|
218
|
+
static void
|
219
|
+
type_remove(gqlType type) {
|
220
|
+
uint64_t h = calc_hash(type->name);
|
221
|
+
|
222
|
+
if (0 < h) {
|
223
|
+
Slot *bucket = get_bucketp(h);
|
224
|
+
Slot s;
|
225
|
+
Slot prev = NULL;
|
226
|
+
|
227
|
+
for (s = *bucket; NULL != s; s = s->next) {
|
228
|
+
if (h == s->hash && 0 == strcmp(s->type->name, type->name)) {
|
229
|
+
if (NULL == prev) {
|
230
|
+
*bucket = s->next;
|
231
|
+
} else {
|
232
|
+
prev->next = s->next;
|
233
|
+
}
|
234
|
+
DEBUG_FREE(mem_graphql_slot, s);
|
235
|
+
free(s);
|
236
|
+
|
237
|
+
break;
|
238
|
+
}
|
239
|
+
prev = s;
|
240
|
+
}
|
241
|
+
}
|
242
|
+
}
|
243
|
+
|
244
|
+
int
|
245
|
+
gql_init(Err err) {
|
246
|
+
memset(buckets, 0, sizeof(buckets));
|
247
|
+
|
248
|
+
if (ERR_OK != gql_value_init(err) ||
|
249
|
+
ERR_OK != gql_intro_init(err)) {
|
250
|
+
|
251
|
+
return err->code;
|
252
|
+
}
|
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)) {
|
261
|
+
|
262
|
+
return err->code;
|
263
|
+
}
|
264
|
+
return ERR_OK;
|
265
|
+
}
|
266
|
+
|
267
|
+
void
|
268
|
+
gql_destroy() {
|
269
|
+
Slot *sp = buckets;
|
270
|
+
Slot s;
|
271
|
+
Slot n;
|
272
|
+
int i;
|
273
|
+
gqlDir dir;
|
274
|
+
|
275
|
+
for (i = BUCKET_SIZE; 0 < i; i--, sp++) {
|
276
|
+
Slot *b = sp;
|
277
|
+
|
278
|
+
*sp = NULL;
|
279
|
+
for (s = *b; NULL != s; s = n) {
|
280
|
+
n = s->next;
|
281
|
+
DEBUG_FREE(mem_graphql_slot, s);
|
282
|
+
gql_type_destroy(s->type);
|
283
|
+
free(s);
|
284
|
+
}
|
285
|
+
*sp = NULL;
|
286
|
+
}
|
287
|
+
while (NULL != (dir = directives)) {
|
288
|
+
directives = dir->next;
|
289
|
+
dir_destroy(dir);
|
290
|
+
}
|
291
|
+
}
|
292
|
+
|
293
|
+
static gqlType
|
294
|
+
type_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
|
295
|
+
gqlType type = (gqlType)malloc(sizeof(struct _gqlType));
|
296
|
+
|
297
|
+
if (NULL == type) {
|
298
|
+
err_set(err, 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
|
+
}
|
311
|
+
type->locked = locked;
|
312
|
+
type->core = false;
|
313
|
+
type->dir = NULL;
|
314
|
+
|
315
|
+
if (ERR_OK != gql_type_set(err, type)) {
|
316
|
+
gql_type_destroy(type);
|
317
|
+
type = NULL;
|
318
|
+
}
|
319
|
+
}
|
320
|
+
return type;
|
321
|
+
}
|
322
|
+
|
323
|
+
Text
|
324
|
+
gql_object_to_json(Text text, gqlValue value, int indent, int depth) {
|
325
|
+
// TBD
|
326
|
+
return text;
|
327
|
+
}
|
328
|
+
|
329
|
+
Text
|
330
|
+
gql_object_to_graphql(Text text, gqlValue value, int indent, int depth) {
|
331
|
+
// TBD
|
332
|
+
return text;
|
333
|
+
}
|
334
|
+
|
335
|
+
gqlType
|
336
|
+
gql_type_create(Err err, const char *name, const char *desc, int dlen, bool locked, gqlType *interfaces) {
|
337
|
+
gqlType type = type_create(err, name, desc, dlen, locked);
|
338
|
+
|
339
|
+
if (NULL != type) {
|
340
|
+
type->kind = GQL_OBJECT;
|
341
|
+
type->to_json = gql_object_to_json;
|
342
|
+
type->fields = NULL;
|
343
|
+
type->interfaces = NULL;
|
344
|
+
if (NULL != interfaces) {
|
345
|
+
gqlType *tp = interfaces;
|
346
|
+
gqlType *np;
|
347
|
+
int cnt = 0;
|
348
|
+
|
349
|
+
for (; NULL != *tp; tp++) {
|
350
|
+
cnt++;
|
351
|
+
}
|
352
|
+
if (0 < cnt) {
|
353
|
+
if (NULL == (type->interfaces = (gqlType*)malloc(sizeof(gqlType) * (cnt + 1)))) {
|
354
|
+
err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL type interfaces.");
|
355
|
+
free(type);
|
356
|
+
return NULL;
|
357
|
+
}
|
358
|
+
for (np = type->interfaces, tp = interfaces; NULL != *tp; np++, tp++) {
|
359
|
+
*np = *tp;
|
360
|
+
}
|
361
|
+
*np = NULL;
|
362
|
+
}
|
363
|
+
}
|
364
|
+
}
|
365
|
+
return type;
|
366
|
+
}
|
367
|
+
|
368
|
+
gqlField
|
369
|
+
gql_type_field(Err err,
|
370
|
+
gqlType type,
|
371
|
+
const char *name,
|
372
|
+
gqlType return_type,
|
373
|
+
const char *desc,
|
374
|
+
bool required,
|
375
|
+
bool list,
|
376
|
+
bool not_empty,
|
377
|
+
gqlResolveFunc resolve) {
|
378
|
+
gqlField f = (gqlField)malloc(sizeof(struct _gqlField));
|
379
|
+
|
380
|
+
if (NULL == f) {
|
381
|
+
err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL field.");
|
382
|
+
} else {
|
383
|
+
DEBUG_ALLOC(mem_graphql_field, f);
|
384
|
+
f->next = NULL;
|
385
|
+
f->name = strdup(name);
|
386
|
+
f->type = return_type;
|
387
|
+
if (NULL == desc) {
|
388
|
+
f->desc = NULL;
|
389
|
+
} else {
|
390
|
+
f->desc = strdup(desc);
|
391
|
+
}
|
392
|
+
f->reason = NULL;
|
393
|
+
f->args = NULL;
|
394
|
+
f->dir = NULL;
|
395
|
+
f->resolve = resolve;
|
396
|
+
f->required = required;
|
397
|
+
f->list = list;
|
398
|
+
f->not_empty = not_empty;
|
399
|
+
f->deprecated = false;
|
400
|
+
if (NULL == type->fields) {
|
401
|
+
type->fields = f;
|
402
|
+
} else {
|
403
|
+
gqlField fend;
|
404
|
+
|
405
|
+
for (fend = type->fields; NULL != fend->next; fend = fend->next) {
|
406
|
+
}
|
407
|
+
fend->next = f;
|
408
|
+
}
|
409
|
+
}
|
410
|
+
return f;
|
411
|
+
}
|
412
|
+
|
413
|
+
gqlArg
|
414
|
+
gql_field_arg(Err err,
|
415
|
+
gqlField field,
|
416
|
+
const char *name,
|
417
|
+
gqlType type,
|
418
|
+
const char *desc,
|
419
|
+
struct _gqlValue *def_value,
|
420
|
+
bool required) {
|
421
|
+
gqlArg a = (gqlArg)malloc(sizeof(struct _gqlArg));
|
422
|
+
|
423
|
+
if (NULL == a) {
|
424
|
+
err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL field argument.");
|
425
|
+
} else {
|
426
|
+
DEBUG_ALLOC(mem_graphql_arg, a);
|
427
|
+
a->next = NULL;
|
428
|
+
a->name = strdup(name);
|
429
|
+
a->type = type;
|
430
|
+
if (NULL == desc) {
|
431
|
+
a->desc = NULL;
|
432
|
+
} else {
|
433
|
+
a->desc = strdup(desc);
|
434
|
+
}
|
435
|
+
a->default_value = def_value;
|
436
|
+
a->dir = NULL;
|
437
|
+
a->required = required;
|
438
|
+
if (NULL == field->args) {
|
439
|
+
field->args = a;
|
440
|
+
} else {
|
441
|
+
gqlArg end;
|
442
|
+
|
443
|
+
for (end = field->args; NULL != end->next; end = end->next) {
|
444
|
+
}
|
445
|
+
end->next = a;
|
446
|
+
}
|
447
|
+
}
|
448
|
+
return a;
|
449
|
+
}
|
450
|
+
|
451
|
+
Text
|
452
|
+
gql_union_to_json(Text text, gqlValue value, int indent, int depth) {
|
453
|
+
// TBD
|
454
|
+
return text;
|
455
|
+
}
|
456
|
+
|
457
|
+
gqlType
|
458
|
+
gql_union_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
|
459
|
+
gqlType type = type_create(err, name, desc, dlen, locked);
|
460
|
+
|
461
|
+
if (NULL != type) {
|
462
|
+
type->kind = GQL_UNION;
|
463
|
+
type->to_json = gql_union_to_json;
|
464
|
+
type->types = NULL;
|
465
|
+
}
|
466
|
+
return type;
|
467
|
+
}
|
468
|
+
|
469
|
+
int
|
470
|
+
gql_union_add(Err err, gqlType type, const char *name, int len) {
|
471
|
+
gqlTypeLink link = (gqlTypeLink)malloc(sizeof(gqlTypeLink));
|
472
|
+
|
473
|
+
if (NULL == link) {
|
474
|
+
err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL Union value.");
|
475
|
+
}
|
476
|
+
if (0 >= len) {
|
477
|
+
len = (int)strlen(name);
|
478
|
+
}
|
479
|
+
link->name = strndup(name, len);
|
480
|
+
if (NULL == type->types) {
|
481
|
+
link->next = type->types;
|
482
|
+
type->types = link;
|
483
|
+
} else {
|
484
|
+
gqlTypeLink last = type->types;
|
485
|
+
|
486
|
+
for (; NULL != last->next; last = last->next) {
|
487
|
+
}
|
488
|
+
link->next = NULL;
|
489
|
+
last->next = link;
|
490
|
+
}
|
491
|
+
return ERR_OK;
|
492
|
+
}
|
493
|
+
|
494
|
+
Text
|
495
|
+
gql_enum_to_json(Text text, gqlValue value, int indent, int depth) {
|
496
|
+
// TBD
|
497
|
+
return text;
|
498
|
+
}
|
499
|
+
|
500
|
+
gqlType
|
501
|
+
gql_enum_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
|
502
|
+
gqlType type = type_create(err, name, desc, dlen, locked);
|
503
|
+
|
504
|
+
if (NULL != type) {
|
505
|
+
type->kind = GQL_ENUM;
|
506
|
+
type->to_json = gql_enum_to_json;
|
507
|
+
type->choices = NULL;
|
508
|
+
}
|
509
|
+
return type;
|
510
|
+
}
|
511
|
+
|
512
|
+
int
|
513
|
+
gql_enum_add(Err err, gqlType type, const char *value, int len) {
|
514
|
+
gqlStrLink link = (gqlStrLink)malloc(sizeof(gqlStrLink));
|
515
|
+
|
516
|
+
if (NULL == link) {
|
517
|
+
err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL Enum value.");
|
518
|
+
}
|
519
|
+
if (0 >= len) {
|
520
|
+
len = (int)strlen(value);
|
521
|
+
}
|
522
|
+
link->next = type->choices;
|
523
|
+
type->choices = link;
|
524
|
+
link->str = strndup(value, len);
|
525
|
+
|
526
|
+
return ERR_OK;
|
527
|
+
}
|
528
|
+
|
529
|
+
int
|
530
|
+
gql_enum_append(Err err, gqlType type, const char *value, int len) {
|
531
|
+
gqlStrLink link = (gqlStrLink)malloc(sizeof(gqlStrLink));
|
532
|
+
|
533
|
+
if (NULL == link) {
|
534
|
+
err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL Enum value.");
|
535
|
+
}
|
536
|
+
if (0 >= len) {
|
537
|
+
len = (int)strlen(value);
|
538
|
+
}
|
539
|
+
link->str = strndup(value, len);
|
540
|
+
if (NULL == type->choices) {
|
541
|
+
link->next = type->choices;
|
542
|
+
type->choices = link;
|
543
|
+
} else {
|
544
|
+
gqlStrLink last = type->choices;
|
545
|
+
|
546
|
+
for (; NULL != last->next; last = last->next) {
|
547
|
+
}
|
548
|
+
link->next = NULL;
|
549
|
+
last->next = link;
|
550
|
+
}
|
551
|
+
return ERR_OK;
|
552
|
+
}
|
553
|
+
|
554
|
+
static Text
|
555
|
+
fragment_to_json(Text text, gqlValue value, int indent, int depth) {
|
556
|
+
// TBD
|
557
|
+
return text;
|
558
|
+
}
|
559
|
+
|
560
|
+
gqlType
|
561
|
+
gql_fragment_create(Err err, const char *name, const char *desc, int dlen, bool locked, gqlType on) {
|
562
|
+
gqlType type = type_create(err, name, desc, dlen, locked);
|
563
|
+
|
564
|
+
if (NULL != type) {
|
565
|
+
type->kind = GQL_FRAG;
|
566
|
+
type->to_json = fragment_to_json;
|
567
|
+
type->fields = NULL;
|
568
|
+
type->on = on;
|
569
|
+
}
|
570
|
+
return type;
|
571
|
+
}
|
572
|
+
|
573
|
+
static Text
|
574
|
+
input_to_json(Text text, gqlValue value, int indent, int depth) {
|
575
|
+
// TBD
|
576
|
+
return text;
|
577
|
+
}
|
578
|
+
|
579
|
+
gqlType
|
580
|
+
gql_input_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
|
581
|
+
gqlType type = type_create(err, name, desc, dlen, locked);
|
582
|
+
|
583
|
+
if (NULL != type) {
|
584
|
+
type->kind = GQL_INPUT;
|
585
|
+
type->to_json = input_to_json;
|
586
|
+
type->fields = NULL;
|
587
|
+
}
|
588
|
+
return type;
|
589
|
+
}
|
590
|
+
|
591
|
+
static Text
|
592
|
+
interface_to_json(Text text, gqlValue value, int indent, int depth) {
|
593
|
+
// TBD
|
594
|
+
return text;
|
595
|
+
}
|
596
|
+
|
597
|
+
gqlType
|
598
|
+
gql_interface_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
|
599
|
+
gqlType type = type_create(err, name, desc, dlen, locked);
|
600
|
+
|
601
|
+
if (NULL != type) {
|
602
|
+
type->kind = GQL_INTERFACE;
|
603
|
+
type->to_json = interface_to_json;
|
604
|
+
type->fields = NULL;
|
605
|
+
}
|
606
|
+
return type;
|
607
|
+
}
|
608
|
+
|
609
|
+
static Text
|
610
|
+
scalar_to_json(Text text, gqlValue value, int indent, int depth) {
|
611
|
+
if (NULL == value->str) {
|
612
|
+
text = text_append(text, "null", 4);
|
613
|
+
} else {
|
614
|
+
text = text_append(text, "\"", 1);
|
615
|
+
text = text_append(text, value->str, -1);
|
616
|
+
text = text_append(text, "\"", 1);
|
617
|
+
}
|
618
|
+
return text;
|
619
|
+
}
|
620
|
+
|
621
|
+
// Create a scalar type that will be represented as a string.
|
622
|
+
gqlType
|
623
|
+
gql_scalar_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
|
624
|
+
gqlType type = type_create(err, name, desc, dlen, locked);
|
625
|
+
|
626
|
+
if (NULL != type) {
|
627
|
+
type->kind = GQL_SCALAR;
|
628
|
+
type->to_json = scalar_to_json;
|
629
|
+
type->coerce = NULL;
|
630
|
+
type->destroy = NULL;
|
631
|
+
}
|
632
|
+
return type;
|
633
|
+
}
|
634
|
+
|
635
|
+
gqlDir
|
636
|
+
gql_directive_create(Err err, const char *name, const char *desc, int dlen, bool locked) {
|
637
|
+
gqlDir dir;
|
638
|
+
|
639
|
+
if (NULL == (dir = (gqlDir)malloc(sizeof(struct _gqlDir)))) {
|
640
|
+
err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL directive.");
|
641
|
+
return NULL;
|
642
|
+
}
|
643
|
+
DEBUG_ALLOC(mem_graphql_directive, dir);
|
644
|
+
dir->next = directives;
|
645
|
+
directives = dir;
|
646
|
+
dir->name = strdup(name);
|
647
|
+
dir->args = NULL;
|
648
|
+
dir->locs = NULL;
|
649
|
+
dir->locked = locked;
|
650
|
+
if (NULL == desc) {
|
651
|
+
dir->desc = NULL;
|
652
|
+
} else {
|
653
|
+
if (0 >= dlen) {
|
654
|
+
dir->desc = strdup(desc);
|
655
|
+
} else {
|
656
|
+
dir->desc = strndup(desc, dlen);
|
657
|
+
}
|
658
|
+
}
|
659
|
+
return dir;
|
660
|
+
}
|
661
|
+
|
662
|
+
gqlArg
|
663
|
+
gql_dir_arg(Err err,
|
664
|
+
gqlDir dir,
|
665
|
+
const char *name,
|
666
|
+
const char *type_name,
|
667
|
+
const char *desc,
|
668
|
+
int dlen,
|
669
|
+
struct _gqlValue *def_value,
|
670
|
+
bool required) {
|
671
|
+
|
672
|
+
gqlArg a = (gqlArg)malloc(sizeof(struct _gqlArg));
|
673
|
+
|
674
|
+
if (NULL == a) {
|
675
|
+
err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL directive argument.");
|
676
|
+
} else {
|
677
|
+
DEBUG_ALLOC(mem_graphql_arg, a);
|
678
|
+
a->next = NULL;
|
679
|
+
a->name = strdup(name);
|
680
|
+
a->type_name = strdup(type_name);
|
681
|
+
a->type = NULL;
|
682
|
+
if (NULL == desc) {
|
683
|
+
a->desc = NULL;
|
684
|
+
} else if (0 < dlen) {
|
685
|
+
a->desc = strdup(desc);
|
686
|
+
} else {
|
687
|
+
a->desc = strndup(desc, dlen);
|
688
|
+
}
|
689
|
+
a->default_value = def_value;
|
690
|
+
a->dir = NULL;
|
691
|
+
a->required = required;
|
692
|
+
if (NULL == dir->args) {
|
693
|
+
dir->args = a;
|
694
|
+
} else {
|
695
|
+
gqlArg end;
|
696
|
+
|
697
|
+
for (end = dir->args; NULL != end->next; end = end->next) {
|
698
|
+
}
|
699
|
+
end->next = a;
|
700
|
+
}
|
701
|
+
}
|
702
|
+
return a;
|
703
|
+
}
|
704
|
+
|
705
|
+
int
|
706
|
+
gql_directive_on(Err err, gqlDir d, const char *on, int len) {
|
707
|
+
gqlStrLink link = (gqlStrLink)malloc(sizeof(gqlStrLink));
|
708
|
+
gqlStrLink loc;
|
709
|
+
|
710
|
+
if (NULL == link) {
|
711
|
+
err_set(err, ERR_MEMORY, "Failed to allocation memory for a GraphQL directive location.");
|
712
|
+
}
|
713
|
+
if (0 >= len) {
|
714
|
+
len = (int)strlen(on);
|
715
|
+
}
|
716
|
+
if (NULL == d->locs) {
|
717
|
+
link->next = d->locs;
|
718
|
+
d->locs = link;
|
719
|
+
} else {
|
720
|
+
link->next = NULL;
|
721
|
+
for (loc = d->locs; NULL != loc->next; loc = loc->next) {
|
722
|
+
}
|
723
|
+
loc->next = link;
|
724
|
+
}
|
725
|
+
link->str = strndup(on, len);
|
726
|
+
|
727
|
+
return ERR_OK;
|
728
|
+
}
|
729
|
+
|
730
|
+
void
|
731
|
+
gql_type_destroy(gqlType type) {
|
732
|
+
type_destroy(type);
|
733
|
+
type_remove(type);
|
734
|
+
}
|
735
|
+
|
736
|
+
// If negative then there are non-simple-string characters.
|
737
|
+
static int
|
738
|
+
desc_len(const char *desc) {
|
739
|
+
const char *d = desc;
|
740
|
+
int special = 1;
|
741
|
+
|
742
|
+
for (; '\0' != *d; d++) {
|
743
|
+
if (*d < ' ' || '"' == *d || '\\' == *d) {
|
744
|
+
special = -1;
|
745
|
+
}
|
746
|
+
}
|
747
|
+
return (int)(d - desc) * special;
|
748
|
+
}
|
749
|
+
|
750
|
+
static Text
|
751
|
+
desc_sdl(Text text, const char *desc, int indent) {
|
752
|
+
if (NULL != desc) {
|
753
|
+
int cnt = desc_len(desc);
|
754
|
+
|
755
|
+
if (0 < indent) {
|
756
|
+
text = text_append(text, spaces, indent);
|
757
|
+
}
|
758
|
+
if (0 <= cnt) {
|
759
|
+
text = text_append(text, "\"", 1);
|
760
|
+
text = text_append(text, desc, cnt);
|
761
|
+
text = text_append(text, "\"\n", 2);
|
762
|
+
} else {
|
763
|
+
text = text_append(text, "\"\"\"\n", 4);
|
764
|
+
if (0 < indent) {
|
765
|
+
const char *start = desc;
|
766
|
+
const char *d = desc;
|
767
|
+
|
768
|
+
for (; '\0' != *d; d++) {
|
769
|
+
if ('\r' == *d) {
|
770
|
+
int len = (int)(d - start);
|
771
|
+
d++;
|
772
|
+
if ('\n' == *d) {
|
773
|
+
d++;
|
774
|
+
}
|
775
|
+
text = text_append(text, spaces, indent);
|
776
|
+
text = text_append(text, start, len);
|
777
|
+
text = text_append(text, "\n", 1);
|
778
|
+
start = d;
|
779
|
+
} else if ('\n' == *d) {
|
780
|
+
text = text_append(text, spaces, indent);
|
781
|
+
text = text_append(text, start, (int)(d - start));
|
782
|
+
text = text_append(text, "\n", 1);
|
783
|
+
d++;
|
784
|
+
start = d;
|
785
|
+
}
|
786
|
+
}
|
787
|
+
text = text_append(text, spaces, indent);
|
788
|
+
text = text_append(text, start, (int)(d - start));
|
789
|
+
} else {
|
790
|
+
text = text_append(text, desc, -1);
|
791
|
+
}
|
792
|
+
if (0 < indent) {
|
793
|
+
text = text_append(text, "\n", 1);
|
794
|
+
text = text_append(text, spaces, indent);
|
795
|
+
text = text_append(text, "\"\"\"\n", 4);
|
796
|
+
} else {
|
797
|
+
text = text_append(text, "\n\"\"\"\n", 5);
|
798
|
+
}
|
799
|
+
}
|
800
|
+
}
|
801
|
+
return text;
|
802
|
+
}
|
803
|
+
|
804
|
+
static Text
|
805
|
+
arg_sdl(Text text, gqlArg a, bool with_desc, bool last) {
|
806
|
+
if (with_desc) {
|
807
|
+
text = desc_sdl(text, a->desc, 4);
|
808
|
+
}
|
809
|
+
text = text_append(text, a->name, -1);
|
810
|
+
if (a->required) {
|
811
|
+
text = text_append(text, "!: ", 3);
|
812
|
+
} else {
|
813
|
+
text = text_append(text, ": ", 2);
|
814
|
+
}
|
815
|
+
text = text_append(text, a->type_name, -1);
|
816
|
+
if (NULL != a->default_value) {
|
817
|
+
text = text_append(text, " = ", 3);
|
818
|
+
text = gql_value_json(text, a->default_value, 0, 0);
|
819
|
+
}
|
820
|
+
if (!last) {
|
821
|
+
text = text_append(text, ", ", 2);
|
822
|
+
}
|
823
|
+
return text;
|
824
|
+
}
|
825
|
+
|
826
|
+
static Text
|
827
|
+
field_sdl(Text text, gqlField f, bool with_desc) {
|
828
|
+
if (with_desc) {
|
829
|
+
text = desc_sdl(text, f->desc, 2);
|
830
|
+
}
|
831
|
+
text = text_append(text, " ", 2);
|
832
|
+
text = text_append(text, f->name, -1);
|
833
|
+
if (NULL != f->args) {
|
834
|
+
gqlArg a;
|
835
|
+
|
836
|
+
text = text_append(text, "(", 1);
|
837
|
+
for (a = f->args; NULL != a; a = a->next) {
|
838
|
+
text = arg_sdl(text, a, with_desc, NULL == a->next);
|
839
|
+
}
|
840
|
+
text = text_append(text, ")", 1);
|
841
|
+
}
|
842
|
+
text = text_append(text, ": ", 2);
|
843
|
+
if (f->list) {
|
844
|
+
text = text_append(text, "[", 1);
|
845
|
+
text = text_append(text, f->type->name, -1);
|
846
|
+
if (f->not_empty) {
|
847
|
+
text = text_append(text, "!", 1);
|
848
|
+
}
|
849
|
+
text = text_append(text, "]", 1);
|
850
|
+
} else {
|
851
|
+
text = text_append(text, f->type->name, -1);
|
852
|
+
}
|
853
|
+
if (f->required) {
|
854
|
+
text = text_append(text, "!", 1);
|
855
|
+
}
|
856
|
+
text = text_append(text, "\n", 1);
|
857
|
+
|
858
|
+
return text;
|
859
|
+
}
|
860
|
+
|
861
|
+
Text
|
862
|
+
gql_type_sdl(Text text, gqlType type, bool with_desc) {
|
863
|
+
if (with_desc) {
|
864
|
+
desc_sdl(text, type->desc, 0);
|
865
|
+
}
|
866
|
+
switch (type->kind) {
|
867
|
+
case GQL_OBJECT:
|
868
|
+
case GQL_FRAG: {
|
869
|
+
gqlField f;
|
870
|
+
|
871
|
+
text = text_append(text, "type ", 5);
|
872
|
+
text = text_append(text, type->name, -1);
|
873
|
+
if (NULL != type->interfaces) {
|
874
|
+
gqlType *tp = type->interfaces;
|
875
|
+
|
876
|
+
text = text_append(text, " implements ", 12);
|
877
|
+
for (; NULL != *tp; tp++) {
|
878
|
+
text = text_append(text, (*tp)->name, -1);
|
879
|
+
if (NULL != *(tp + 1)) {
|
880
|
+
text = text_append(text, ", ", 2);
|
881
|
+
}
|
882
|
+
}
|
883
|
+
}
|
884
|
+
text = text_append(text, " {\n", 3);
|
885
|
+
for (f = type->fields; NULL != f; f = f->next) {
|
886
|
+
text = field_sdl(text, f, with_desc);
|
887
|
+
}
|
888
|
+
text = text_append(text, "}\n", 2);
|
889
|
+
break;
|
890
|
+
}
|
891
|
+
case GQL_UNION: {
|
892
|
+
gqlTypeLink link;
|
893
|
+
|
894
|
+
text = text_append(text, "union ", 6);
|
895
|
+
text = text_append(text, type->name, -1);
|
896
|
+
text = text_append(text, " = ", 3);
|
897
|
+
for (link = type->types; NULL != link; link = link->next) {
|
898
|
+
text = text_append(text, link->name, -1);
|
899
|
+
if (NULL != link->next) {
|
900
|
+
text = text_append(text, " | ", 3);
|
901
|
+
}
|
902
|
+
}
|
903
|
+
break;
|
904
|
+
}
|
905
|
+
case GQL_ENUM: {
|
906
|
+
gqlStrLink link;;
|
907
|
+
|
908
|
+
text = text_append(text, "enum ", 5);
|
909
|
+
text = text_append(text, type->name, -1);
|
910
|
+
text = text_append(text, " {\n", 3);
|
911
|
+
for (link = type->choices; NULL != link; link = link->next) {
|
912
|
+
text = text_append(text, " ", 2);
|
913
|
+
text = text_append(text, link->str, -1);
|
914
|
+
text = text_append(text, "\n", 1);
|
915
|
+
}
|
916
|
+
text = text_append(text, "}\n", 2);
|
917
|
+
break;
|
918
|
+
}
|
919
|
+
case GQL_SCALAR:
|
920
|
+
text = text_append(text, "scalar ", 7);
|
921
|
+
text = text_append(text, type->name, -1);
|
922
|
+
text = text_append(text, "\n", 1);
|
923
|
+
break;
|
924
|
+
case GQL_INTERFACE: {
|
925
|
+
gqlField f;
|
926
|
+
|
927
|
+
text = text_append(text, "interface ", 10);
|
928
|
+
text = text_append(text, type->name, -1);
|
929
|
+
text = text_append(text, " {\n", 3);
|
930
|
+
|
931
|
+
for (f = type->fields; NULL != f; f = f->next) {
|
932
|
+
text = field_sdl(text, f, with_desc);
|
933
|
+
}
|
934
|
+
text = text_append(text, "}\n", 2);
|
935
|
+
break;
|
936
|
+
}
|
937
|
+
case GQL_INPUT: {
|
938
|
+
gqlField f;
|
939
|
+
|
940
|
+
text = text_append(text, "input ", 6);
|
941
|
+
text = text_append(text, type->name, -1);
|
942
|
+
text = text_append(text, " {\n", 3);
|
943
|
+
for (f = type->fields; NULL != f; f = f->next) {
|
944
|
+
text = field_sdl(text, f, with_desc);
|
945
|
+
}
|
946
|
+
text = text_append(text, "}\n", 2);
|
947
|
+
break;
|
948
|
+
}
|
949
|
+
default:
|
950
|
+
break;
|
951
|
+
}
|
952
|
+
return text;
|
953
|
+
}
|
954
|
+
|
955
|
+
Text
|
956
|
+
gql_directive_sdl(Text text, gqlDir d, bool with_desc) {
|
957
|
+
gqlStrLink link;
|
958
|
+
|
959
|
+
if (with_desc) {
|
960
|
+
text = desc_sdl(text, d->desc, 0);
|
961
|
+
}
|
962
|
+
text = text_append(text, "directive @", 11);
|
963
|
+
text = text_append(text, d->name, -1);
|
964
|
+
if (NULL != d->args) {
|
965
|
+
gqlArg a;
|
966
|
+
|
967
|
+
text = text_append(text, "(", 1);
|
968
|
+
for (a = d->args; NULL != a; a = a->next) {
|
969
|
+
text = arg_sdl(text, a, with_desc, NULL == a->next);
|
970
|
+
}
|
971
|
+
text = text_append(text, ")", 1);
|
972
|
+
}
|
973
|
+
text = text_append(text, " on ", 4);
|
974
|
+
for (link = d->locs; NULL != link; link = link->next) {
|
975
|
+
text = text_append(text, link->str, -1);
|
976
|
+
if (NULL != link->next) {
|
977
|
+
text = text_append(text, " | ", 3);
|
978
|
+
}
|
979
|
+
}
|
980
|
+
return text;
|
981
|
+
}
|
982
|
+
|
983
|
+
static int
|
984
|
+
type_cmp(const void *v0, const void *v1) {
|
985
|
+
gqlType t0 = *(gqlType*)v0;
|
986
|
+
gqlType t1 = *(gqlType*)v1;
|
987
|
+
|
988
|
+
if (t0->kind == t1->kind) {
|
989
|
+
if (0 == strcmp("schema", t0->name)) {
|
990
|
+
return -1;
|
991
|
+
}
|
992
|
+
if (0 == strcmp("schema", t1->name)) {
|
993
|
+
return 1;
|
994
|
+
}
|
995
|
+
return strcmp(t0->name, t1->name);
|
996
|
+
}
|
997
|
+
return t0->kind - t1->kind;
|
998
|
+
}
|
999
|
+
|
1000
|
+
Text
|
1001
|
+
gql_schema_sdl(Text text, bool with_desc, bool all) {
|
1002
|
+
Slot *bucket;
|
1003
|
+
Slot s;
|
1004
|
+
gqlType type;
|
1005
|
+
gqlDir d;
|
1006
|
+
int i;
|
1007
|
+
int cnt = 0;
|
1008
|
+
|
1009
|
+
for (bucket = buckets, i = 0; i < BUCKET_SIZE; bucket++, i++) {
|
1010
|
+
for (s = *bucket; NULL != s; s = s->next) {
|
1011
|
+
type = s->type;
|
1012
|
+
if (!all && type->core) {
|
1013
|
+
continue;
|
1014
|
+
}
|
1015
|
+
cnt++;
|
1016
|
+
}
|
1017
|
+
}
|
1018
|
+
if (0 < cnt) {
|
1019
|
+
gqlType types[cnt];
|
1020
|
+
gqlType *tp = types;
|
1021
|
+
|
1022
|
+
for (bucket = buckets, i = 0; i < BUCKET_SIZE; bucket++, i++) {
|
1023
|
+
for (s = *bucket; NULL != s; s = s->next) {
|
1024
|
+
type = s->type;
|
1025
|
+
if (!all && type->core) {
|
1026
|
+
continue;
|
1027
|
+
}
|
1028
|
+
*tp++ = type;
|
1029
|
+
}
|
1030
|
+
}
|
1031
|
+
qsort(types, cnt, sizeof(gqlType), type_cmp);
|
1032
|
+
for (i = 0, tp = types; i < cnt; i++, tp++) {
|
1033
|
+
text = gql_type_sdl(text, *tp, with_desc);
|
1034
|
+
if (i < cnt - 1) {
|
1035
|
+
text = text_append(text, "\n", 1);
|
1036
|
+
}
|
1037
|
+
}
|
1038
|
+
}
|
1039
|
+
for (d = directives; NULL != d; d = d->next) {
|
1040
|
+
text = text_append(text, "\n", 1);
|
1041
|
+
text = gql_directive_sdl(text, d, with_desc);
|
1042
|
+
text = text_append(text, "\n", 1);
|
1043
|
+
}
|
1044
|
+
return text;
|
1045
|
+
}
|
1046
|
+
|
1047
|
+
void
|
1048
|
+
gql_dump_hook(Req req) {
|
1049
|
+
char buf[256];
|
1050
|
+
int cnt;
|
1051
|
+
Text text = text_allocate(4094);
|
1052
|
+
bool all = false;
|
1053
|
+
bool with_desc = true;
|
1054
|
+
int vlen;
|
1055
|
+
const char *s = req_query_value(req, "all", 3, &vlen);
|
1056
|
+
|
1057
|
+
if (NULL != s && 4 == vlen && 0 == strncasecmp("true", s, 4)) {
|
1058
|
+
all = true;
|
1059
|
+
}
|
1060
|
+
s = req_query_value(req, "with_desc", 9, &vlen);
|
1061
|
+
|
1062
|
+
if (NULL != s && 5 == vlen && 0 == strncasecmp("false", s, 5)) {
|
1063
|
+
with_desc = false;
|
1064
|
+
}
|
1065
|
+
text = gql_schema_sdl(text, with_desc, all);
|
1066
|
+
cnt = snprintf(buf, sizeof(buf), "HTTP/1.1 200 Okay\r\nContent-Type: application/graphql\r\nContent-Length: %ld\r\n\r\n", text->len);
|
1067
|
+
text = text_prepend(text, buf, cnt);
|
1068
|
+
res_set_message(req->res, text);
|
1069
|
+
}
|
1070
|
+
|
1071
|
+
void
|
1072
|
+
gql_eval_hook(Req req) {
|
1073
|
+
// TBD detect introspection
|
1074
|
+
// start resolving by callout to some global handler as needed
|
1075
|
+
// pass target, field, args
|
1076
|
+
// return json or gqlValue
|
1077
|
+
// for handler, if introspection then handler here else global
|
1078
|
+
}
|