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

Commit 6c35c10

Browse files
committed
WIP: Integrate expressions into GraphQL
Part of #13.
1 parent 2394ca1 commit 6c35c10

File tree

4 files changed

+193
-2
lines changed

4 files changed

+193
-2
lines changed

graphql/core/rules.lua

+39
Original file line numberDiff line numberDiff line change
@@ -604,4 +604,43 @@ end
604604

605605
-- }}}
606606

607+
-- {{{ compile filter argument
608+
609+
function rules.compileFilterArgument(node, context)
610+
local expressions = require('expressions') -- XXX: move upward
611+
612+
assert(node.kind == 'argument')
613+
local name = node.name.value
614+
615+
-- XXX: 1:1-connected object can have field 'filter'
616+
if name ~= 'filter' then return end
617+
618+
-- save compiled expression
619+
local value = node.value
620+
assert(type(value) == 'string') -- XXX: better error
621+
node.compiled = expressions.new(value)
622+
623+
-- XXX: don't blindly find the pattern {kind = 'variable', ...}, but either
624+
-- traverse a tree according to node kinds or export used variables info from
625+
-- expressions module
626+
627+
-- mark used variables
628+
local open_set = {node.compiled}
629+
while true do
630+
local e_node = table.remove(open_set, 1)
631+
if e_node == nil then break end
632+
if type(e_node) == 'table' then
633+
if e_node.kind == 'variable' then
634+
context.variableReferences[e_node.name] = true
635+
else
636+
for _, child_e_node in pairs(e_node) do
637+
table.insert(open_set, child_e_node)
638+
end
639+
end
640+
end
641+
end
642+
end
643+
644+
-- }}}
645+
607646
return rules

graphql/core/validate.lua

+4-1
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,10 @@ local visitors = {
265265
end)
266266
end,
267267

268-
rules = { rules.uniqueInputObjectFields }
268+
rules = {
269+
rules.uniqueInputObjectFields,
270+
rules.compileFilterArgument,
271+
}
269272
},
270273

271274
inputObject = {

graphql/gen_arguments.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ function gen_arguments.list_args(db_schema, collection_name)
348348
return {
349349
{name = 'limit', type = 'int*'},
350350
{name = 'offset', type = offset_type},
351-
-- {name = 'filter', type = ...},
351+
{name = 'filter', type = 'string*'},
352352
pcre_field,
353353
}
354354
end

test/common/expressions.test.lua

+149
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
#!/usr/bin/env tarantool
2+
3+
-- https://github.com/tarantool/graphql/issues/13
4+
5+
local fio = require('fio')
6+
7+
-- require in-repo version of graphql/ sources despite current working directory
8+
package.path = fio.abspath(debug.getinfo(1).source:match("@?(.*/)")
9+
:gsub('/./', '/'):gsub('/+$', '')) .. '/../../?.lua' .. ';' .. package.path
10+
11+
local tap = require('tap')
12+
local yaml = require('yaml')
13+
local test_utils = require('test.test_utils')
14+
local testdata = require('test.testdata.common_testdata')
15+
16+
local function run_queries(gql_wrapper)
17+
local test = tap.test('expressions')
18+
test:plan(3)
19+
20+
local exp_result_1_and_2 = test_utils.deeply_number_tostring(yaml.decode(([[
21+
---
22+
order_collection:
23+
- order_id: order_id_4
24+
price: 4.3333333333333
25+
- order_id: order_id_5
26+
price: 4.6666666666667
27+
]]):strip()))
28+
29+
-- {{{ test expresion (use variables and &&)
30+
31+
local query_1 = [[
32+
query get_by_expr($order_id_from: Double, $order_id_to: Double) {
33+
order_collection(
34+
filter: "order_id >= $order_id_from && order_id < $order_id_to"
35+
) {
36+
order_id
37+
price
38+
}
39+
}
40+
]]
41+
42+
local gql_query_1 = test_utils.show_trace(function()
43+
return gql_wrapper:compile(query_1)
44+
end)
45+
46+
local variables_1 = {
47+
order_id_from = 4,
48+
order_id_to = 5,
49+
}
50+
51+
local result_1 = test_utils.show_trace(function()
52+
return gql_query_1:execute(variables_1)
53+
end)
54+
55+
local result_1_data = test_utils.deeply_number_tostring(result_1.data)
56+
test:is_deeply(result_1_data, exp_result_1_and_2,
57+
'expressions with variables and &&')
58+
59+
-- }}}
60+
61+
-- {{{ test expresion (use immediate values and &&)
62+
63+
local query_2 = [[
64+
query get_by_expr {
65+
order_collection(
66+
filter: "order_id >= 4 && order_id < 5"
67+
) {
68+
order_id
69+
price
70+
}
71+
}
72+
]]
73+
74+
local gql_query_2 = test_utils.show_trace(function()
75+
return gql_wrapper:compile(query_2)
76+
end)
77+
78+
local variables_2 = {}
79+
80+
local result_2 = test_utils.show_trace(function()
81+
return gql_query_2:execute(variables_2)
82+
end)
83+
84+
local result_2_data = test_utils.deeply_number_tostring(result_2.data)
85+
test:is_deeply(result_2_data, exp_result_1_and_2,
86+
'expression with immediate values and &&')
87+
88+
-- }}}
89+
90+
-- {{{ test expression with nested fields and ||
91+
92+
local query_3 = [[
93+
query get_by_expr {
94+
order_metainfo_collection(
95+
filter: "store.address.city == 1 || store.address.city == 42"
96+
) {
97+
order_metainfo_id
98+
store {
99+
address {
100+
city
101+
}
102+
}
103+
}
104+
}
105+
]]
106+
107+
local gql_query_3 = test_utils.show_trace(function()
108+
return gql_wrapper:compile(query_3)
109+
end)
110+
111+
local exp_result_3 = test_utils.deeply_number_tostring(yaml.decode(([[
112+
---
113+
order_metainfo_collection:
114+
- order_metainfo_id: order_metainfo_id_1
115+
store:
116+
address:
117+
city: city 1
118+
- order_metainfo_id: order_metainfo_id_42
119+
store:
120+
address:
121+
city: city 42
122+
]]):strip()))
123+
124+
local variables_3 = {}
125+
126+
local result_3 = test_utils.show_trace(function()
127+
return gql_query_3:execute(variables_3)
128+
end)
129+
130+
local result_3_data = test_utils.deeply_number_tostring(result_3.data)
131+
test:is_deeply(result_3_data, exp_result_3,
132+
'expression with nested fields and ||')
133+
134+
-- }}}
135+
136+
assert(test:check(), 'check plan')
137+
end
138+
139+
box.cfg({})
140+
141+
test_utils.run_testdata(testdata, {
142+
run_queries = run_queries,
143+
graphql_opts = {
144+
-- gh-137: timeout exceeded
145+
timeout_ms = 10000, -- 10 seconds
146+
}
147+
})
148+
149+
os.exit()

0 commit comments

Comments
 (0)