Skip to content

Commit

Permalink
Post structure (ohler55#119)
Browse files Browse the repository at this point in the history
* Support POST with named op

* Handle JSON POSTs that use the apollo layout
  • Loading branch information
ohler55 authored Jul 7, 2023
1 parent 1897c5f commit a385b51
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 51 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

All changes to the Agoo gem are documented here. Releases follow semantic versioning.

## [2.15.6] - 2023-07-07

### Fixed

- Support for Apollo style POST requests fixed.

## [2.15.5] - 2023-04-07

### Changed
Expand Down
2 changes: 0 additions & 2 deletions ext/agoo/con.c
Original file line number Diff line number Diff line change
Expand Up @@ -991,7 +991,6 @@ static void
publish_pub(agooPub pub, agooConLoop loop) {
agooUpgraded up;
const char *sub = pub->subject->pattern;
int cnt = 0;

for (up = agoo_server.up_list; NULL != up; up = up->next) {
if (NULL != up->con && up->con->loop == loop && agoo_upgraded_match(up, sub)) {
Expand All @@ -1001,7 +1000,6 @@ publish_pub(agooPub pub, agooConLoop loop) {
agoo_con_res_append(up->con, res);
res->con_kind = AGOO_CON_ANY;
agoo_res_message_push(res, agoo_text_dup(pub->msg));
cnt++;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions ext/agoo/early_hints.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,6 @@ void
early_hints_init(VALUE mod) {
eh_class = rb_define_class_under(mod, "EarlyHints", rb_cObject);

rb_undef_alloc_func(eh_class);
rb_define_method(eh_class, "call", eh_call, 1);
}
1 change: 1 addition & 0 deletions ext/agoo/error_stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ void
error_stream_init(VALUE mod) {
es_class = rb_define_class_under(mod, "ErrorStream", rb_cObject);

rb_undef_alloc_func(es_class);
rb_define_method(es_class, "puts", es_puts, 1);
rb_define_method(es_class, "write", es_write, 1);
rb_define_method(es_class, "flush", es_flush, 0);
Expand Down
1 change: 1 addition & 0 deletions ext/agoo/request.c
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,7 @@ void
request_init(VALUE mod) {
req_class = rb_define_class_under(mod, "Request", rb_cObject);

rb_undef_alloc_func(req_class);
rb_define_method(req_class, "to_s", to_s, 0);
rb_define_method(req_class, "to_h", to_h, 0);
rb_define_method(req_class, "environment", to_h, 0);
Expand Down
2 changes: 2 additions & 0 deletions ext/agoo/rgraphql.c
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,8 @@ void
graphql_init(VALUE mod) {
graphql_class = rb_define_class_under(mod, "GraphQL", rb_cObject);

rb_undef_alloc_func(graphql_class);

rb_define_module_function(graphql_class, "schema", graphql_schema, 1);

rb_define_module_function(graphql_class, "load", graphql_load, 1);
Expand Down
1 change: 1 addition & 0 deletions ext/agoo/rresponse.c
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ void
response_init(VALUE mod) {
res_class = rb_define_class_under(mod, "Response", rb_cObject);

rb_undef_alloc_func(res_class);
rb_define_method(res_class, "to_s", to_s, 0);
rb_define_method(res_class, "body", body_get, 0);
rb_define_method(res_class, "body=", body_set, 1);
Expand Down
13 changes: 7 additions & 6 deletions ext/agoo/rupgraded.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ get_upgraded(VALUE self) {
const char*
extract_subject(VALUE subject, int *slen) {
const char *subj;

switch (rb_type(subject)) {
case T_STRING:
subj = StringValuePtr(subject);
Expand Down Expand Up @@ -84,7 +84,7 @@ rup_write(VALUE self, VALUE msg) {
}
} else {
volatile VALUE rs = rb_funcall(msg, to_s_id, 0);

message = StringValuePtr(rs);
mlen = RSTRING_LEN(rs);
}
Expand Down Expand Up @@ -165,7 +165,7 @@ static VALUE
rup_pending(VALUE self) {
agooUpgraded up = get_upgraded(self);
int pending = -1;

if (NULL != up) {
pending = agoo_upgraded_pending(up);
atomic_fetch_sub(&up->ref_cnt, 1);
Expand All @@ -183,7 +183,7 @@ static VALUE
rup_open(VALUE self) {
agooUpgraded up = get_upgraded(self);
int pending = -1;

if (NULL != up) {
pending = (int)(long)atomic_load(&up->pending);
atomic_fetch_sub(&up->ref_cnt, 1);
Expand All @@ -204,7 +204,7 @@ rup_protocol(VALUE self) {

if (agoo_server.active) {
agooUpgraded up;

pthread_mutex_lock(&agoo_server.up_lock);
if (NULL != (up = DATA_PTR(self)) && NULL != up->con) {
switch (up->con->bind->kind) {
Expand Down Expand Up @@ -249,7 +249,7 @@ rupgraded_create(agooCon c, VALUE obj, VALUE env) {
up->wrap = (void*)Data_Wrap_Struct(upgraded_class, NULL, NULL, up);

agoo_server_add_upgraded(up);

if (rb_respond_to(obj, on_open_id)) {
rb_funcall(obj, on_open_id, 1, (VALUE)up->wrap);
}
Expand Down Expand Up @@ -285,6 +285,7 @@ void
upgraded_init(VALUE mod) {
upgraded_class = rb_define_class_under(mod, "Upgraded", rb_cObject);

rb_undef_alloc_func(upgraded_class);
rb_define_method(upgraded_class, "write", rup_write, 1);
rb_define_method(upgraded_class, "subscribe", rup_subscribe, 1);
rb_define_method(upgraded_class, "unsubscribe", rup_unsubscribe, -1);
Expand Down
133 changes: 91 additions & 42 deletions ext/agoo/sdl.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,38 @@ static const char union_str[] = "union";

static int make_sel(agooErr err, agooDoc doc, gqlDoc gdoc, gqlOp op, gqlSel *parentp);

/*
static void
debug_sels(gqlSel sel, int indent) {
char pad[100];
memset(pad, ' ', sizeof(pad));
pad[indent] = '\0';
printf("%s{%s %s", pad, sel->name, (NULL == sel->type) ? "NULL" : sel->type->name);
if (NULL == sel->sels) {
printf("}\n");
} else {
printf("\n");
for (gqlSel sc = sel->sels; NULL != sc; sc = sc->next) {
debug_sels(sc, indent + 2);
}
printf("%s}\n", pad);
}
}
static void
debug_doc(gqlDoc doc) {
printf("*** doc: {\n");
for (gqlOp op = doc->ops; NULL != op; op = op->next) {
printf(" {%s\n", op->name);
debug_sels(op->sels, 4);
}
printf(" }\n");
printf("}\n");
}
*/

static int
extract_desc(agooErr err, agooDoc doc, const char **descp, size_t *lenp) {
agoo_doc_skip_white(doc);
Expand Down Expand Up @@ -1312,24 +1344,67 @@ make_sel(agooErr err, agooDoc doc, gqlDoc gdoc, gqlOp op, gqlSel *parentp) {
return AGOO_ERR_OK;
}

static gqlType
lookup_field_type(gqlType type, const char *field, bool qroot) {
gqlType ftype = NULL;

switch (type->kind) {
case GQL_SCHEMA:
case GQL_OBJECT:
case GQL_INPUT:
case GQL_INTERFACE: {
gqlField f;

for (f = type->fields; NULL != f; f = f->next) {
if (0 == strcmp(field, f->name)) {
ftype = f->type;
break;
}
}
if (NULL == ftype) {
if (0 == strcmp("__typename", field)) {
ftype = &gql_string_type;
} else if (qroot) {
if (0 == strcmp("__type", field)) {
ftype = gql_type_get("__Type");
} else if (0 == strcmp("__schema", field)) {
ftype = gql_type_get("__Schema");
}
}
}
break;
}
case GQL_LIST:
case GQL_NON_NULL:
ftype = lookup_field_type(type->base, field, false);
break;
case GQL_UNION: // Can not be used directly for query type determinations.
default:
break;
}
return ftype;
}

static int
make_op(agooErr err, agooDoc doc, gqlDoc gdoc, gqlOpKind kind) {
char name[256];
const char *kind_str = query_str;
const char *start;
gqlOp op;
size_t nlen;

agoo_doc_skip_white(doc);
start = doc->cur;
agoo_doc_read_token(doc);
if ( start < doc->cur) {
if (start < doc->cur) {
if (5 == (doc->cur - start) && 0 == strncmp(query_str, start, sizeof(query_str) - 1)) {
kind = GQL_QUERY;
} else if (8 == (doc->cur - start) && 0 == strncmp(mutation_str, start, sizeof(mutation_str) - 1)) {
kind = GQL_MUTATION;
kind_str = mutation_str;
} else if (12 == (doc->cur - start) && 0 == strncmp(subscription_str, start, sizeof(subscription_str) - 1)) {
kind = GQL_SUBSCRIPTION;
kind_str = subscription_str;
} else {
return agoo_doc_err(doc, err, "Invalid operation type");
}
Expand Down Expand Up @@ -1384,6 +1459,21 @@ make_op(agooErr err, agooDoc doc, gqlDoc gdoc, gqlOpKind kind) {
if (doc->end <= doc->cur && '}' != doc->cur[-1]) {
return agoo_doc_err(doc, err, "Expected a }");
}
if (0 < nlen) {
gqlType schema = gql_root_type();
gqlType type = lookup_field_type(schema, kind_str, false);

if ((NULL == lookup_field_type(type, op->sels->name, false)) &&
(NULL != lookup_field_type(type, name, false))) {
gqlSel wrap = sel_create(err, NULL, name, NULL);

if (NULL == wrap) {
return err->code;
}
wrap->sels = op->sels;
op->sels = wrap;
}
}
if (NULL == gdoc->ops) {
gdoc->ops = op;
} else {
Expand Down Expand Up @@ -1461,47 +1551,6 @@ make_fragment(agooErr err, agooDoc doc, gqlDoc gdoc) {
return AGOO_ERR_OK;
}

static gqlType
lookup_field_type(gqlType type, const char *field, bool qroot) {
gqlType ftype = NULL;

switch (type->kind) {
case GQL_SCHEMA:
case GQL_OBJECT:
case GQL_INPUT:
case GQL_INTERFACE: {
gqlField f;

for (f = type->fields; NULL != f; f = f->next) {
if (0 == strcmp(field, f->name)) {
ftype = f->type;
break;
}
}
if (NULL == ftype) {
if (0 == strcmp("__typename", field)) {
ftype = &gql_string_type;
} else if (qroot) {
if (0 == strcmp("__type", field)) {
ftype = gql_type_get("__Type");
} else if (0 == strcmp("__schema", field)) {
ftype = gql_type_get("__Schema");
}
}
}
break;
}
case GQL_LIST:
case GQL_NON_NULL:
ftype = lookup_field_type(type->base, field, false);
break;
case GQL_UNION: // Can not be used directly for query type determinations.
default:
break;
}
return ftype;
}

static int
sel_set_type(agooErr err, gqlType type, gqlSel sels, bool qroot) {
gqlSel sel;
Expand Down
2 changes: 1 addition & 1 deletion lib/agoo/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

module Agoo
# Agoo version.
VERSION = '2.15.5'
VERSION = '2.15.6'
end
18 changes: 18 additions & 0 deletions test/graphql_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,24 @@ def test_post_json
post_test(uri, body, 'application/json', expect)
end

def test_post_json_op
uri = URI('http://localhost:6472/graphql?indent=2')
body = %^{
"query":"query artists{name}"
}^
expect = %^{
"data":{
"artists":[
{
"name":"Fazerdaze"
}
]
}
}^

post_test(uri, body, 'application/json', expect)
end

def test_post_fragment
uri = URI('http://localhost:6472/graphql?indent=2')
body = %^
Expand Down

0 comments on commit a385b51

Please sign in to comment.