Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.

Commit 61bee44

Browse files
committed
Return result in standard GraphQL format
Success case: {data = ...} Error case: {errors = {message = ..., ...}} compiled_query:execute(...) becomes exception-safe (performs pcall internally), but graphql.new(...):compile() and graphql.new(...).execute(...) still can throw an exception. Enabled 5_2 test in common.test.lua, it fails before fix for #135 ( PR #178). Prerequisite for #71. Prerequisite for #134.
1 parent 13489d6 commit 61bee44

28 files changed

+207
-216
lines changed

graphql/impl.lua

+9-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,15 @@ local function gql_execute(qstate, variables, operation_name)
4141

4242
local root_value = {}
4343

44-
return execute(state.schema, qstate.ast, root_value, variables,
45-
operation_name)
44+
local ok, data = pcall(execute, state.schema, qstate.ast, root_value,
45+
variables, operation_name)
46+
if not ok then
47+
local err = utils.serialize_error(data)
48+
return {errors = {err}}
49+
end
50+
return {
51+
data = data,
52+
}
4653
end
4754

4855
--- Compile a query and execute an operation.

graphql/server/server.lua

+1-9
Original file line numberDiff line numberDiff line change
@@ -114,16 +114,8 @@ function server.init(graphql, host, port)
114114
operation_name = nil
115115
end
116116

117-
local ok, result = pcall(compiled_query.execute, compiled_query,
118-
variables, operation_name)
119-
if not ok then
120-
return {
121-
status = 200,
122-
body = json.encode({error = {{message = result}}})
123-
}
124-
end
117+
local result = compiled_query:execute(variables, operation_name)
125118

126-
result = {data = result}
127119
return {
128120
status = 200,
129121
headers = {

graphql/utils.lua

+23
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
--- Various utility function used across the graphql module sources and tests.
22

3+
local json = require('json')
34
local log = require('log')
5+
local ffi = require('ffi')
46

57
local utils = {}
68

@@ -217,4 +219,25 @@ function utils.optional_require_rex()
217219
return rex, is_pcre2
218220
end
219221

222+
function utils.serialize_error(err)
223+
if type(err) == 'string' then
224+
return {message = err}
225+
elseif type(err) == 'cdata' and
226+
tostring(ffi.typeof(err)) == 'ctype<const struct error &>' then
227+
return {message = tostring(err)}
228+
elseif type(err) == 'table' and type(err.message) == 'string' then
229+
return err
230+
end
231+
232+
local message = 'internal error: unknown error format'
233+
local encode_use_tostring_orig = json.cfg.encode_use_tostring
234+
json.cfg({encode_use_tostring = true})
235+
local orig_error = json.encode(err)
236+
json.cfg({encode_use_tostring = encode_use_tostring_orig})
237+
return {
238+
message = message,
239+
orig_error = orig_error,
240+
}
241+
end
242+
220243
return utils

test/bench/bench.lua

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,13 @@ local function workload(shard, bench_prepare, bench_iter, opts)
4949

5050
-- first iteration; print result and update checksum
5151
local result = bench_iter(state)
52-
local result_str = yaml.encode(result)
52+
local result_str = yaml.encode(result.data)
5353
checksum:update(result_str .. '1')
5454

5555
-- the rest iterations; just update checksum
5656
for i = 2, iterations do
5757
local result = bench_iter(state)
58-
local result_str = yaml.encode(result)
58+
local result_str = yaml.encode(result.data)
5959
checksum:update(result_str .. tostring(i))
6060
if i % 100 == 0 then
6161
fiber.yield()

test/common/directives.test.lua

+4-4
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ local function run_queries(gql_wrapper)
5959
first_name: Ivan
6060
]]):strip())
6161

62-
test:is_deeply(result_1_1, exp_result_1_1, '1_1')
62+
test:is_deeply(result_1_1.data, exp_result_1_1, '1_1')
6363

6464
-- }}}
6565
-- {{{ 1_2
@@ -81,7 +81,7 @@ local function run_queries(gql_wrapper)
8181
description: first order of Ivan
8282
]]):strip())
8383

84-
test:is_deeply(result_1_2, exp_result_1_2, '1_2')
84+
test:is_deeply(result_1_2.data, exp_result_1_2, '1_2')
8585

8686
-- }}}
8787

@@ -122,7 +122,7 @@ local function run_queries(gql_wrapper)
122122
description: first order of Ivan
123123
]]):strip())
124124

125-
test:is_deeply(result_2_1, exp_result_2_1, '2_1')
125+
test:is_deeply(result_2_1.data, exp_result_2_1, '2_1')
126126

127127
-- }}}
128128
-- {{{ 2_2
@@ -148,7 +148,7 @@ local function run_queries(gql_wrapper)
148148
first_name: Ivan
149149
]]):strip())
150150

151-
test:is_deeply(result_2_2, exp_result_2_2, '2_2')
151+
test:is_deeply(result_2_2.data, exp_result_2_2, '2_2')
152152

153153
-- }}}
154154

test/common/introspection.test.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -3113,7 +3113,7 @@ local function run_queries(gql_wrapper)
31133113
test_utils.show_trace(function()
31143114
local gql_query = gql_wrapper:compile(query)
31153115
local result = gql_query:execute({})
3116-
test:is_deeply(result, exp_result, 'introspection query')
3116+
test:is_deeply(result.data, exp_result, 'introspection query')
31173117
end)
31183118

31193119
assert(test:check(), 'check plan')

test/common/limit_result.test.lua

+15-10
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,25 @@ local function run_queries(gql_wrapper)
3333
local variables = {
3434
user_id = 5,
3535
}
36-
local ok, result = pcall(gql_query.execute, gql_query, variables)
37-
assert(ok == false, "this test should fail")
38-
test:like(result,
39-
'count%[4%] exceeds limit%[3%] %(`resulting_object_cnt_max`',
40-
'resulting_object_cnt_max test')
36+
local result = gql_query:execute(variables)
37+
assert(result.data == nil, "this test should fail")
38+
assert(result.errors ~= nil, "this test should fail")
39+
local err = test_utils.strip_error(result.errors[1].message)
40+
test:like(err,
41+
'count%[4%] exceeds limit%[3%] %(`resulting_object_cnt_max`',
42+
'resulting_object_cnt_max test')
43+
4144
variables = {
4245
user_id = 5,
4346
description = "no such description"
4447
}
45-
ok, result = pcall(gql_query.execute, gql_query, variables)
46-
assert(ok == false, "this test should fail")
47-
test:like(result,
48-
'count%[6%] exceeds limit%[5%] %(`fetched_object_cnt_max`',
49-
'resulting_object_cnt_max test')
48+
local result = gql_query:execute(variables)
49+
assert(result.data == nil, "this test should fail")
50+
assert(result.errors ~= nil, "this test should fail")
51+
local err = test_utils.strip_error(result.errors[1].message)
52+
test:like(err,
53+
'count%[6%] exceeds limit%[5%] %(`fetched_object_cnt_max`',
54+
'resulting_object_cnt_max test')
5055

5156
assert(test:check(), 'check plan')
5257
end

test/common/mutation.test.lua

+20-20
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ local function check_insert(test, gql_wrapper, virtbox, mutation_insert,
9292
-- check mutation result from graphql
9393
local result = gql_mutation_insert:execute(dont_pass_variables and {} or
9494
variables_insert)
95-
test:is_deeply(result, exp_result_insert, 'insert result')
95+
test:is_deeply(result.data, exp_result_insert, 'insert result')
9696
-- check inserted user
9797
local tuple = get_tuple(virtbox, 'user_collection', {user_id})
9898
test:ok(tuple ~= nil, 'tuple was inserted')
@@ -134,7 +134,7 @@ local function check_insert_order_metainfo(test, gql_wrapper, virtbox,
134134
-- check mutation result
135135
local gql_mutation_insert = gql_wrapper:compile(mutation_insert)
136136
local result = gql_mutation_insert:execute(variables)
137-
test:is_deeply(result, exp_result_insert, 'insert result')
137+
test:is_deeply(result.data, exp_result_insert, 'insert result')
138138

139139
-- check inserted tuple
140140
local EXTERNAL_ID_STRING = 1 -- 0 is for int
@@ -218,7 +218,7 @@ local function check_update(test, gql_wrapper, virtbox, mutation_update,
218218
-- check mutation result from graphql
219219
local result = gql_mutation_update:execute(dont_pass_variables and {} or
220220
variables_update)
221-
test:is_deeply(result, exp_result_update, 'update result')
221+
test:is_deeply(result.data, exp_result_update, 'update result')
222222
-- check updated user
223223
local tuple = get_tuple(virtbox, 'user_collection', {user_id})
224224
test:ok(tuple ~= nil, 'updated tuple exists')
@@ -282,7 +282,7 @@ local function check_update_order_metainfo(test, gql_wrapper, virtbox,
282282
-- check mutation result
283283
local gql_mutation_update = gql_wrapper:compile(mutation_update)
284284
local result = gql_mutation_update:execute(variables)
285-
test:is_deeply(result, exp_result_update, 'update result')
285+
test:is_deeply(result.data, exp_result_update, 'update result')
286286

287287
-- check updated tuple
288288
local tuple = get_tuple(virtbox, 'order_metainfo_collection',
@@ -334,7 +334,7 @@ local function check_delete(test, gql_wrapper, virtbox, mutation_delete,
334334
-- check mutation result from graphql
335335
local result = gql_mutation_delete:execute(dont_pass_variables and {} or
336336
variables_delete)
337-
test:is_deeply(result, exp_result_delete, 'delete result')
337+
test:is_deeply(result.data, exp_result_delete, 'delete result')
338338

339339
-- check the user was deleted
340340
local tuple = get_tuple(virtbox, 'user_collection', {user_id})
@@ -468,10 +468,10 @@ local function run_queries(gql_wrapper, virtbox, meta)
468468
}
469469
]]
470470
local gql_mutation_insert_3i = gql_wrapper:compile(mutation_insert_3i)
471-
local ok, err = pcall(gql_mutation_insert_3i.execute,
472-
gql_mutation_insert_3i, {})
471+
local result = gql_mutation_insert_3i:execute({})
472+
local err = test_utils.strip_error(result.errors[1].message)
473473
local err_exp = '"insert" must be the only argument when it is present'
474-
test:is_deeply({ok, test_utils.strip_error(err)}, {false, err_exp},
474+
test:is(err, err_exp,
475475
'"insert" argument is forbidden with other filters (object arguments)')
476476

477477
-- test "insert" argument is forbidden with list arguments
@@ -489,10 +489,10 @@ local function run_queries(gql_wrapper, virtbox, meta)
489489
}
490490
]]
491491
local gql_mutation_insert_4i = gql_wrapper:compile(mutation_insert_4i)
492-
local ok, err = pcall(gql_mutation_insert_4i.execute,
493-
gql_mutation_insert_4i, {})
492+
local result = gql_mutation_insert_4i:execute({})
493+
local err = test_utils.strip_error(result.errors[1].message)
494494
local err_exp = '"insert" must be the only argument when it is present'
495-
test:is_deeply({ok, test_utils.strip_error(err)}, {false, err_exp},
495+
test:is(err, err_exp,
496496
'"insert" argument is forbidden with other filters (list arguments)')
497497

498498
-- test "insert" argument is forbidden with other extra argument
@@ -510,10 +510,10 @@ local function run_queries(gql_wrapper, virtbox, meta)
510510
}
511511
]]
512512
local gql_mutation_insert_5i = gql_wrapper:compile(mutation_insert_5i)
513-
local ok, err = pcall(gql_mutation_insert_5i.execute,
514-
gql_mutation_insert_5i, {})
513+
local result = gql_mutation_insert_5i:execute({})
514+
local err = test_utils.strip_error(result.errors[1].message)
515515
local err_exp = '"insert" must be the only argument when it is present'
516-
test:is_deeply({ok, test_utils.strip_error(err)}, {false, err_exp},
516+
test:is(err, err_exp,
517517
'"insert" argument is forbidden with other filters (extra arguments)')
518518

519519
-- test inserting an object into a collection with subrecord, union, array
@@ -1134,11 +1134,11 @@ local function run_queries(gql_wrapper, virtbox, meta)
11341134
user_id = 'user_id_201',
11351135
}
11361136
}
1137-
local ok, err = pcall(gql_mutation_update_4.execute, gql_mutation_update_4,
1138-
variables_update_4)
1137+
local result = gql_mutation_update_4:execute(variables_update_4)
1138+
local err = test_utils.strip_error(result.errors[1].message)
11391139
local err_exp = "Attempt to modify a tuple field which is part of index " ..
11401140
"'user_id_index' in space 'user_collection'"
1141-
test:is_deeply({ok, test_utils.strip_error(err)}, {false, err_exp},
1141+
test:is(err, err_exp,
11421142
'updating of a field of a primary key when it is NOT shard key field')
11431143

11441144
local mutation_update_5 = [[
@@ -1159,11 +1159,11 @@ local function run_queries(gql_wrapper, virtbox, meta)
11591159
order_id = 'order_id_4001',
11601160
}
11611161
}
1162-
local ok, err = pcall(gql_mutation_update_5.execute, gql_mutation_update_5,
1163-
variables_update_5)
1162+
local result = gql_mutation_update_5:execute(variables_update_5)
1163+
local err = test_utils.strip_error(result.errors[1].message)
11641164
local err_exp = "Attempt to modify a tuple field which is part of index " ..
11651165
"'order_id_index' in space 'order_collection'"
1166-
test:is_deeply({ok, test_utils.strip_error(err)}, {false, err_exp},
1166+
test:is(err, err_exp,
11671167
'updating of a field of a primary key when it is shard key field')
11681168

11691169
-- }}}

test/common/pcre.test.lua

+6-5
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ local function run_queries(gql_wrapper)
5050
middle_name: Ivanovich
5151
]]):strip())
5252

53-
test:is_deeply(result_1_1, exp_result_1_1, '1_1')
53+
test:is_deeply(result_1_1.data, exp_result_1_1, '1_1')
5454

5555
-- }}}
5656
-- {{{ offset + regexp match
@@ -71,7 +71,7 @@ local function run_queries(gql_wrapper)
7171
first_name: Vasiliy
7272
]]):strip())
7373

74-
test:is_deeply(result_1_2, exp_result_1_2, '1_2')
74+
test:is_deeply(result_1_2.data, exp_result_1_2, '1_2')
7575

7676
-- }}}
7777
-- {{{ UTF-8 in regexp
@@ -93,7 +93,7 @@ local function run_queries(gql_wrapper)
9393
middle_name: Иванович
9494
]]):strip())
9595

96-
test:is_deeply(result_1_3, exp_result_1_3, '1_3')
96+
test:is_deeply(result_1_3.data, exp_result_1_3, '1_3')
9797

9898
-- }}}
9999

@@ -120,7 +120,7 @@ local function run_queries(gql_wrapper)
120120
return gql_query_1i:execute({})
121121
end)
122122

123-
test:is_deeply(result_1i_1, exp_result_1_1, '1i_1')
123+
test:is_deeply(result_1i_1.data, exp_result_1_1, '1i_1')
124124

125125
-- }}}
126126

@@ -154,7 +154,8 @@ local function run_queries(gql_wrapper)
154154
return gql_query_2:execute({})
155155
end)
156156

157-
test:is_deeply(result_2, exp_result_2, 'regexp match by a subrecord field')
157+
test:is_deeply(result_2.data, exp_result_2,
158+
'regexp match by a subrecord field')
158159

159160
-- }}}
160161

test/common/query_timeout.test.lua

+8-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ package.path = fio.abspath(debug.getinfo(1).source:match("@?(.*/)")
77
:gsub('/./', '/'):gsub('/+$', '')) .. '/../../?.lua' .. ';' .. package.path
88

99
local tap = require('tap')
10-
local utils = require('test.test_utils')
10+
local test_utils = require('test.test_utils')
1111
local testdata = require('test.testdata.user_order_item_testdata')
1212

1313
local function run_queries(gql_wrapper)
@@ -30,18 +30,19 @@ local function run_queries(gql_wrapper)
3030
]]
3131

3232
local gql_query = gql_wrapper:compile(query)
33-
local variables = {
34-
}
35-
local ok, result = pcall(gql_query.execute, gql_query, variables)
36-
assert(ok == false, 'this test should fail')
37-
test:like(result, 'query execution timeout exceeded', 'timeout test')
33+
local variables = {}
34+
local result = gql_query:execute(variables)
35+
assert(result.data == nil, "this test should fail")
36+
assert(result.errors ~= nil, "this test should fail")
37+
local err = test_utils.strip_error(result.errors[1].message)
38+
test:like(err, 'query execution timeout exceeded', 'timeout test')
3839

3940
assert(test:check(), 'check plan')
4041
end
4142

4243
box.cfg({})
4344

44-
utils.run_testdata(testdata, {
45+
test_utils.run_testdata(testdata, {
4546
run_queries = run_queries,
4647
graphql_opts = {
4748
timeout_ms = 0.001,

test/extra/to_avro_arrays.test.lua

+3-3
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,16 @@ user_collection:
9696
]]
9797
result_expected = yaml.decode(result_expected)
9898
local result = gql_query:execute(variables)
99-
test:is_deeply(result, result_expected, 'graphql query exec result')
99+
test:is_deeply(result.data, result_expected, 'graphql query exec result')
100100
local ok, ash, r, fs, _
101101
ok, ash = avro.create(avros)
102102
assert(ok)
103-
ok, _ = avro.validate(ash, result)
103+
ok, _ = avro.validate(ash, result.data)
104104
assert(ok)
105105
test:is(ok, true, 'gql result validation by avro')
106106
ok, fs = avro.compile(ash)
107107
assert(ok)
108-
ok, r = fs.flatten(result)
108+
ok, r = fs.flatten(result.data)
109109
assert(ok)
110110
ok, r = fs.unflatten(r)
111111
assert(ok)

0 commit comments

Comments
 (0)