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

Commit f8ef8dc

Browse files
committed
Compile a query with several operations
Compile and execute an operation w/o name. Fixes #50.
1 parent 5eb048b commit f8ef8dc

File tree

4 files changed

+159
-42
lines changed

4 files changed

+159
-42
lines changed

graphql/query_to_avro.lua

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -135,18 +135,22 @@ object_to_avro = function(object_type, selections, context)
135135
return result
136136
end
137137

138-
--- Create an Avro schema for a given query.
138+
--- Create an Avro schema for a given query / operation.
139139
---
140-
--- @tparam table query object which avro schema should be created for
140+
--- @tparam table qstate compiled query for which the avro schema should be
141+
--- created
141142
---
142-
--- @treturn table `avro_schema` avro schema for any `query:execute()` result
143-
function query_to_avro.convert(query)
144-
assert(type(query) == "table",
145-
('query should be a table, got: %s; ' ..
143+
--- @tparam[opt] string operation_name optional operation name
144+
---
145+
--- @treturn table `avro_schema` avro schema for any
146+
--- `qstate:execute(..., operation_name)` result
147+
function query_to_avro.convert(qstate, operation_name)
148+
assert(type(qstate) == "table",
149+
('qstate should be a table, got: %s; ' ..
146150
'hint: use ":" instead of "."'):format(type(table)))
147-
local state = query.state
148-
local context = query_util.buildContext(state.schema, query.ast, {}, {},
149-
query.operation_name)
151+
local state = qstate.state
152+
local context = query_util.buildContext(state.schema, qstate.ast, {}, {},
153+
operation_name)
150154
-- The variable is necessary to avoid fullname interferention.
151155
-- Each nested Avro record creates it's namespace.
152156
context.namespace_parts = {}

graphql/server/server.lua

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,14 @@ function server.init(graphql, host, port)
107107
}
108108
end
109109

110-
local ok, result = pcall(compiled_query.execute, compiled_query, variables)
110+
local operation_name = parsed.operationName
111+
-- box.NULL -> nil
112+
if operation_name == nil then
113+
operation_name = nil
114+
end
115+
116+
local ok, result = pcall(compiled_query.execute, compiled_query,
117+
variables, operation_name)
111118
if not ok then
112119
return {
113120
status = 200,

graphql/tarantool_graphql.lua

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1617,64 +1617,67 @@ local function parse_cfg(cfg)
16171617
return state
16181618
end
16191619

1620-
--- The function just makes some reasonable assertions on input
1621-
--- and then call graphql-lua execute.
1622-
local function gql_execute(qstate, variables)
1620+
--- Execute an operation from compiled query.
1621+
---
1622+
--- @tparam qstate compiled query
1623+
---
1624+
--- @tparam variables variables to pass to the query
1625+
---
1626+
--- @tparam[opt] string operation_name optional operation name
1627+
---
1628+
--- @treturn table result of the operation
1629+
local function gql_execute(qstate, variables, operation_name)
16231630
assert(qstate.state)
16241631
local state = qstate.state
16251632
assert(state.schema)
16261633

1627-
assert(type(variables) == 'table', 'variables must be table, got ' ..
1628-
type(variables))
1634+
check(variables, 'variables', 'table')
1635+
check(operation_name, 'operation_name', 'string', 'nil')
16291636

16301637
local root_value = {}
1631-
local operation_name = qstate.operation_name
1632-
assert(type(operation_name) == 'string',
1633-
'operation_name must be a string, got ' .. type(operation_name))
16341638

16351639
return execute(state.schema, qstate.ast, root_value, variables,
16361640
operation_name)
16371641
end
16381642

1639-
local function compile_and_execute(state, query, variables)
1643+
--- Compile a query and execute an operation.
1644+
---
1645+
--- See @{gql_compile} and @{gql_execute} for parameters description.
1646+
---
1647+
--- @treturn table result of the operation
1648+
local function compile_and_execute(state, query, variables, operation_name)
16401649
assert(type(state) == 'table', 'use :gql_execute(...) instead of ' ..
16411650
'.execute(...)')
1651+
assert(state.schema ~= nil, 'have not compiled schema')
16421652
check(query, 'query', 'string')
16431653
check(variables, 'variables', 'table', 'nil')
1654+
check(operation_name, 'operation_name', 'string', 'nil')
1655+
16441656
local compiled_query = state:compile(query)
1645-
return compiled_query:execute(variables)
1657+
return compiled_query:execute(variables, operation_name)
16461658
end
16471659

1648-
--- The function parses a query string, validate the resulting query
1649-
--- against the GraphQL schema and provides an object with the function to
1650-
--- execute the query with specific variables values.
1660+
--- Parse GraphQL query string, validate against the GraphQL schema and
1661+
--- provide an object with the function to execute an operation from the
1662+
--- request with specific variables values.
16511663
---
1652-
--- @tparam table state current state of graphql, including
1653-
--- schemas, collections and accessor
1654-
--- @tparam string query query string
1664+
--- @tparam table state a tarantool_graphql instance
1665+
---
1666+
--- @tparam string query text of a GraphQL query
1667+
---
1668+
--- @treturn table compiled query with `execute` and `avro_schema` functions
16551669
local function gql_compile(state, query)
16561670
assert(type(state) == 'table' and type(query) == 'string',
16571671
'use :validate(...) instead of .validate(...)')
16581672
assert(state.schema ~= nil, 'have not compiled schema')
1673+
check(query, 'query', 'string')
16591674

16601675
local ast = parse(query)
1661-
1662-
local operation_name
1663-
for _, definition in pairs(ast.definitions) do
1664-
if definition.kind == 'operation' then
1665-
operation_name = definition.name.value
1666-
end
1667-
end
1668-
1669-
assert(operation_name, "there is no 'operation' in query " ..
1670-
"definitions:\n" .. yaml.encode(ast))
1671-
16721676
validate(state.schema, ast)
16731677

16741678
local qstate = {
16751679
state = state,
16761680
ast = ast,
1677-
operation_name = operation_name,
16781681
}
16791682

16801683
local gql_query = setmetatable(qstate, {
@@ -1719,11 +1722,11 @@ function tarantool_graphql.compile(query)
17191722
return default_instance:compile(query)
17201723
end
17211724

1722-
function tarantool_graphql.execute(query, variables)
1725+
function tarantool_graphql.execute(query, variables, operation_name)
17231726
if default_instance == nil then
17241727
default_instance = tarantool_graphql.new()
17251728
end
1726-
return default_instance:execute(query, variables)
1729+
return default_instance:execute(query, variables, operation_name)
17271730
end
17281731

17291732
function tarantool_graphql.start_server()

test/testdata/common_testdata.lua

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ end
311311

312312
function common_testdata.run_queries(gql_wrapper)
313313
local test = tap.test('common')
314-
test:plan(18)
314+
test:plan(24)
315315

316316
local query_1 = [[
317317
query user_by_order($order_id: String) {
@@ -338,13 +338,116 @@ function common_testdata.run_queries(gql_wrapper)
338338
first_name: Ivan
339339
]]):strip())
340340

341+
local variables_1 = {order_id = 'order_id_1'}
342+
341343
utils.show_trace(function()
342-
local variables_1 = {order_id = 'order_id_1'}
343344
local gql_query_1 = gql_wrapper:compile(query_1)
344345
local result = gql_query_1:execute(variables_1)
345346
test:is_deeply(result, exp_result_1, '1')
346347
end)
347348

349+
local query_1n = [[
350+
query($order_id: String) {
351+
order_collection(order_id: $order_id) {
352+
order_id
353+
description
354+
user_connection {
355+
user_id
356+
last_name
357+
first_name
358+
}
359+
}
360+
}
361+
]]
362+
363+
utils.show_trace(function()
364+
local gql_query_1n = gql_wrapper:compile(query_1n)
365+
local result = gql_query_1n:execute(variables_1)
366+
test:is_deeply(result, exp_result_1, '1n')
367+
end)
368+
369+
local query_1inn = [[
370+
{
371+
order_collection(order_id: "order_id_1") {
372+
order_id
373+
description
374+
user_connection {
375+
user_id
376+
last_name
377+
first_name
378+
}
379+
}
380+
}
381+
]]
382+
383+
utils.show_trace(function()
384+
local gql_query_1inn = gql_wrapper:compile(query_1inn)
385+
local result = gql_query_1inn:execute({})
386+
test:is_deeply(result, exp_result_1, '1inn')
387+
end)
388+
389+
local query_1tn = [[
390+
query get_order {
391+
order_collection(order_id: "order_id_1") {
392+
order_id
393+
description
394+
}
395+
}
396+
query {
397+
order_collection(order_id: "order_id_1") {
398+
order_id
399+
description
400+
}
401+
}
402+
]]
403+
404+
local err_exp = 'Cannot have more than one operation when using ' ..
405+
'anonymous operations'
406+
local ok, err = pcall(gql_wrapper.compile, gql_wrapper, query_1tn)
407+
test:is_deeply({ok, test_utils.strip_error(err)}, {false, err_exp},
408+
'unnamed query should be a single one')
409+
410+
local query_1t = [[
411+
query user_by_order {
412+
order_collection(order_id: "order_id_1") {
413+
order_id
414+
description
415+
user_connection {
416+
user_id
417+
last_name
418+
first_name
419+
}
420+
}
421+
}
422+
query get_order {
423+
order_collection(order_id: "order_id_1") {
424+
order_id
425+
description
426+
}
427+
}
428+
]]
429+
430+
local gql_query_1t = utils.show_trace(function()
431+
return gql_wrapper:compile(query_1t)
432+
end)
433+
434+
local err_exp = 'Operation name must be specified if more than one ' ..
435+
'operation exists.'
436+
local ok, err = pcall(gql_query_1t.execute, gql_query_1t, {})
437+
test:is_deeply({ok, test_utils.strip_error(err)}, {false, err_exp},
438+
'non-determined query name should give an error')
439+
440+
local err_exp = 'Unknown operation "non_existent_operation"'
441+
local ok, err = pcall(gql_query_1t.execute, gql_query_1t, {},
442+
'non_existent_operation')
443+
test:is_deeply({ok, test_utils.strip_error(err)}, {false, err_exp},
444+
'wrong operation name should give an error')
445+
446+
utils.show_trace(function()
447+
local result = gql_query_1t:execute({}, 'user_by_order')
448+
test:is_deeply(result, exp_result_1, 'execute an operation by name')
449+
end)
450+
348451
local query_2 = [[
349452
query user_order($user_id: String, $first_name: String, $limit: Int,
350453
$offset: String) {

0 commit comments

Comments
 (0)