Skip to content
This repository was archived by the owner on Apr 14, 2022. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0aaf2b1

Browse files
KhatskevichTotktonada
authored andcommittedMar 13, 2018
Feature: convert graphql query to avro schema
Extend query object: add `avro_schema` method, which produces Avro schema which can be used to verify or flatten any `query_exequte` result. Closes #7
1 parent 7f6f892 commit 0aaf2b1

8 files changed

+770
-1
lines changed
 

‎Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ default:
33

44
.PHONY: lint
55
lint:
6-
luacheck graphql/*.lua test/local/*.lua test/testdata/*.lua \
6+
luacheck graphql/*.lua \
7+
test/local/*.lua \
8+
test/testdata/*.lua \
79
test/common/*.test.lua test/common/lua/*.lua \
10+
test/extra/*.test.lua \
811
--no-redefined --no-unused-args
912

1013
.PHONY: test

‎graphql/query_to_avro.lua

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
--- Module for convertion GraphQL query to Avro schema.
2+
---
3+
--- Random notes:
4+
---
5+
--- * The best way to use this module is to just call `avro_schema` methon on
6+
--- compiled query object.
7+
local path = "graphql.core"
8+
local introspection = require(path .. '.introspection')
9+
local query_util = require(path .. '.query_util')
10+
11+
-- module functions
12+
local query_to_avro = {}
13+
14+
local gql_scalar_to_avro_index = {
15+
String = "string",
16+
Int = "int",
17+
Long = "long",
18+
-- GraphQL Float is double precision according to graphql.org.
19+
-- More info http://graphql.org/learn/schema/#scalar-types
20+
Float = "double",
21+
Boolean = "boolean"
22+
}
23+
local function gql_scalar_to_avro(fieldType)
24+
assert(fieldType.__type == "Scalar", "GraphQL scalar field expected")
25+
assert(fieldType.name ~= "Map", "Map type is not supported")
26+
local result = gql_scalar_to_avro_index[fieldType.name]
27+
assert(result ~= nil, "Unexpected scalar type: " .. fieldType.name)
28+
return result
29+
end
30+
31+
-- The function converts avro type to nullable.
32+
-- In current tarantool/avro-schema implementation we simply add '*'
33+
-- to the end of type name.
34+
-- The function do not copy the resulting type but changes it in place.
35+
--
36+
-- @tparam table avro schema node to be converted to nullable
37+
--
38+
-- @tresult table schema node; basically it is the passed schema node,
39+
-- however in nullable type implementation through unions it can be different
40+
-- node
41+
local function make_avro_type_nullable(avro)
42+
assert(avro.type ~= nil, "Avro `type` field is necessary")
43+
local type_type = type(avro.type)
44+
if type_type == "string" then
45+
assert(avro.type:endswith("*") == false,
46+
"Avro type should not be nullable already")
47+
avro.type = avro.type .. '*'
48+
return avro
49+
end
50+
if type_type == "table" then
51+
avro.type = make_avro_type_nullable(avro.type)
52+
return avro
53+
end
54+
error("Avro type should be a string or table, got :" .. type_type)
55+
end
56+
57+
local object_to_avro
58+
59+
local function complete_field_to_avro(fieldType, result, subSelections, context,
60+
NonNull)
61+
local fieldTypeName = fieldType.__type
62+
if fieldTypeName == 'NonNull' then
63+
-- In case the field is NonNull, the real type is in ofType attribute.
64+
fieldType = fieldType.ofType
65+
fieldTypeName = fieldType.__type
66+
elseif NonNull ~= true then
67+
-- Call complete_field second time and make result nullable.
68+
result = complete_field_to_avro(fieldType, result, subSelections,
69+
context, true)
70+
result = make_avro_type_nullable(result)
71+
return result
72+
end
73+
74+
if fieldTypeName == 'List' then
75+
local innerType = fieldType.ofType
76+
-- Steal type from virtual object.
77+
-- This is necessary because in case of arrays type should be
78+
-- "completed" into results `items` field, but in other cases (Object,
79+
-- Scalar) it should be completed into `type` field.
80+
local items = complete_field_to_avro(innerType, {}, subSelections,
81+
context).type
82+
result.type = {
83+
type = "array",
84+
items = items
85+
}
86+
return result
87+
end
88+
89+
if fieldTypeName == 'Scalar' then
90+
result.type = gql_scalar_to_avro(fieldType)
91+
return result
92+
end
93+
94+
if fieldTypeName == 'Object' then
95+
result.type = object_to_avro(fieldType, subSelections, context)
96+
return result
97+
elseif fieldTypeName == 'Interface' or fieldTypeName == 'Union' then
98+
error('Interfaces and Unions are not supported yet')
99+
end
100+
error(string.format('Unknown type "%s"', fieldTypeName))
101+
end
102+
103+
--- The function converts a single Object field to avro format
104+
local function field_to_avro(object_type, fields, context)
105+
local firstField = fields[1]
106+
assert(#fields == 1, "The aliases are not considered yet")
107+
local fieldName = firstField.name.value
108+
local fieldType = introspection.fieldMap[fieldName] or
109+
object_type.fields[fieldName]
110+
assert(fieldType ~= nil)
111+
local subSelections = query_util.mergeSelectionSets(fields)
112+
local result = {}
113+
result.name = fieldName
114+
result = complete_field_to_avro(fieldType.kind, result, subSelections,
115+
context)
116+
return result
117+
end
118+
119+
--- Convert GraphQL object to avro record.
120+
---
121+
--- @tparam table object_type GraphQL type object to be converted to Avro schema
122+
---
123+
--- @tparam table selections GraphQL representations of fields which should be
124+
--- in the output of the query
125+
---
126+
--- @tparam table context additional information for Avro schema generation; one
127+
--- of the fields is `namespace_parts` -- table of names of records from the
128+
--- root to the current object
129+
---
130+
--- @treturn table corresponding Avro schema
131+
object_to_avro = function(object_type, selections, context)
132+
local groupedFieldSet = query_util.collectFields(object_type, selections,
133+
{}, {}, context)
134+
local result = {
135+
type = 'record',
136+
name = object_type.name,
137+
fields = {}
138+
}
139+
if #context.namespace_parts ~= 0 then
140+
result.namespace = table.concat(context.namespace_parts, ".")
141+
end
142+
table.insert(context.namespace_parts, result.name)
143+
for _, fields in pairs(groupedFieldSet) do
144+
local avro_field = field_to_avro(object_type, fields, context)
145+
table.insert(result.fields, avro_field)
146+
end
147+
context.namespace_parts[#context.namespace_parts] = nil
148+
return result
149+
end
150+
151+
--- Create an Avro schema for a given query.
152+
---
153+
--- @tparam table query object which avro schema should be created for
154+
---
155+
--- @treturn table `avro_schema` avro schema for any `query:execute()` result.
156+
function query_to_avro.convert(query)
157+
assert(type(query) == "table",
158+
'query should be a table, got: ' .. type(table)
159+
.. '; hint: use ":" instead of "."')
160+
local state = query.state
161+
local context = query_util.buildContext(state.schema, query.ast, {}, {},
162+
query.operation_name)
163+
-- The variable is necessary to avoid fullname interferention.
164+
-- Each nested Avro record creates it's namespace.
165+
context.namespace_parts = {}
166+
local rootType = state.schema[context.operation.operation]
167+
local selections = context.operation.selectionSet.selections
168+
return object_to_avro(rootType, selections, context)
169+
end
170+
171+
return query_to_avro

‎graphql/tarantool_graphql.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ local schema = require('graphql.core.schema')
1414
local types = require('graphql.core.types')
1515
local validate = require('graphql.core.validate')
1616
local execute = require('graphql.core.execute')
17+
local query_to_avro = require('graphql.query_to_avro')
1718

1819
local utils = require('graphql.utils')
1920

@@ -661,6 +662,7 @@ local function gql_compile(state, query)
661662
local gql_query = setmetatable(qstate, {
662663
__index = {
663664
execute = gql_execute,
665+
avro_schema = query_to_avro.convert
664666
}
665667
})
666668
return gql_query

‎test/extra/suite.ini

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[default]
2+
core = app
3+
description = tests on features which are not related to specific executor
4+
lua_libs = ../common/lua/test_data_user_order.lua ../testdata/array_and_map_testdata.lua \
5+
../testdata/nullable_index_testdata.lua

‎test/extra/to_avro_arrays.test.lua

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env tarantool
2+
local fio = require('fio')
3+
local yaml = require('yaml')
4+
local avro = require('avro_schema')
5+
local testdata = require('array_and_map_testdata')
6+
local test = require('tap').test('to avro schema')
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' .. ';' ..
10+
package.path
11+
12+
local graphql = require('graphql')
13+
14+
box.cfg{wal_mode="none"}
15+
test:plan(4)
16+
17+
testdata.init_spaces()
18+
testdata.fill_test_data()
19+
local meta = testdata.get_test_metadata()
20+
21+
local accessor = graphql.accessor_space.new({
22+
schemas = meta.schemas,
23+
collections = meta.collections,
24+
service_fields = meta.service_fields,
25+
indexes = meta.indexes,
26+
})
27+
28+
local gql_wrapper = graphql.new({
29+
schemas = meta.schemas,
30+
collections = meta.collections,
31+
accessor = accessor,
32+
})
33+
34+
-- We do not select `customer_balances` and `favorite_holidays` because thay are
35+
-- is of `Map` type, which is not supported.
36+
local query = [[
37+
query user_holidays($user_id: String) {
38+
user_collection(user_id: $user_id) {
39+
user_id
40+
favorite_food
41+
user_balances {
42+
value
43+
}
44+
}
45+
}
46+
]]
47+
local expected_avro_schema = [[
48+
type: record
49+
name: Query
50+
fields:
51+
- name: user_collection
52+
type:
53+
type: array
54+
items:
55+
type: record
56+
fields:
57+
- name: user_id
58+
type: string
59+
- name: user_balances
60+
type:
61+
type: array
62+
items:
63+
type: record
64+
fields:
65+
- name: value
66+
type: int
67+
name: balance
68+
namespace: Query.user_collection
69+
- name: favorite_food
70+
type:
71+
type: array
72+
items: string
73+
name: user_collection
74+
namespace: Query
75+
]]
76+
expected_avro_schema = yaml.decode(expected_avro_schema)
77+
local gql_query = gql_wrapper:compile(query)
78+
local variables = {
79+
user_id = 'user_id_1',
80+
}
81+
82+
local avros = gql_query:avro_schema()
83+
84+
test:is_deeply(avros, expected_avro_schema, "generated avro schema")
85+
local result_expected = [[
86+
user_collection:
87+
- user_id: user_id_1
88+
user_balances:
89+
- value: 33
90+
- value: 44
91+
favorite_food:
92+
- meat
93+
- potato
94+
]]
95+
result_expected = yaml.decode(result_expected)
96+
local result = gql_query:execute(variables)
97+
test:is_deeply(result, result_expected, 'graphql qury exec result')
98+
local ok, ash, r, fs, _
99+
ok, ash = avro.create(avros)
100+
assert(ok)
101+
ok, _ = avro.validate(ash, result)
102+
assert(ok)
103+
test:is(ok, true, 'gql result validation by avro')
104+
ok, fs = avro.compile(ash)
105+
assert(ok)
106+
ok, r = fs.flatten(result)
107+
assert(ok)
108+
ok, r = fs.unflatten(r)
109+
assert(ok)
110+
test:is_deeply(r, result_expected, 'res = unflatten(flatten(res))')
111+
112+
os.exit(test:check() == true and 0 or 1)

‎test/extra/to_avro_huge.test.lua

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
#!/usr/bin/env tarantool
2+
local fio = require('fio')
3+
local yaml = require('yaml')
4+
local avro = require('avro_schema')
5+
local data = require('test_data_user_order')
6+
local test = require('tap').test('to avro schema')
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' .. ';' ..
10+
package.path
11+
12+
local graphql = require('graphql')
13+
14+
box.cfg{wal_mode="none"}
15+
test:plan(4)
16+
17+
data.init_spaces()
18+
data.fill_test_data(box.space)
19+
20+
local accessor = graphql.accessor_space.new({
21+
schemas = data.meta.schemas,
22+
collections = data.meta.collections,
23+
service_fields = data.meta.service_fields,
24+
indexes = data.meta.indexes,
25+
})
26+
27+
local gql_wrapper = graphql.new({
28+
schemas = data.meta.schemas,
29+
collections = data.meta.collections,
30+
accessor = accessor,
31+
})
32+
33+
local query = [[
34+
query object_result_max($user_id: Int, $order_id: Int) {
35+
user_collection(id: $user_id) {
36+
id
37+
last_name
38+
first_name
39+
order_connection(limit: 1) {
40+
id
41+
user_id
42+
description
43+
order__order_item {
44+
order_id
45+
item_id
46+
order_item__item{
47+
id
48+
name
49+
description
50+
price
51+
}
52+
}
53+
}
54+
},
55+
order_collection(id: $order_id) {
56+
id
57+
description
58+
user_connection {
59+
id
60+
first_name
61+
last_name
62+
order_connection(limit: 1) {
63+
id
64+
order__order_item {
65+
order_item__item {
66+
name
67+
price
68+
}
69+
}
70+
}
71+
}
72+
}
73+
}
74+
75+
]]
76+
local expected_avro_schema = [[
77+
type: record
78+
name: Query
79+
fields:
80+
- name: user_collection
81+
type:
82+
type: array
83+
items:
84+
type: record
85+
fields:
86+
- name: order_connection
87+
type:
88+
type: array
89+
items:
90+
type: record
91+
fields:
92+
- name: user_id
93+
type: int
94+
- name: order__order_item
95+
type:
96+
type: array
97+
items:
98+
type: record
99+
fields:
100+
- name: order_id
101+
type: int
102+
- name: item_id
103+
type: int
104+
- name: order_item__item
105+
type:
106+
type: record
107+
fields:
108+
- name: description
109+
type: string
110+
- name: price
111+
type: string
112+
- name: name
113+
type: string
114+
- name: id
115+
type: int
116+
name: item_collection
117+
namespace: Query.user_collection.order_collection.order_item_collection
118+
name: order_item_collection
119+
namespace: Query.user_collection.order_collection
120+
- name: description
121+
type: string
122+
- name: id
123+
type: int
124+
name: order_collection
125+
namespace: Query.user_collection
126+
- name: last_name
127+
type: string
128+
- name: first_name
129+
type: string
130+
- name: id
131+
type: int
132+
name: user_collection
133+
namespace: Query
134+
- name: order_collection
135+
type:
136+
type: array
137+
items:
138+
type: record
139+
fields:
140+
- name: user_connection
141+
type:
142+
type: record
143+
fields:
144+
- name: order_connection
145+
type:
146+
type: array
147+
items:
148+
type: record
149+
fields:
150+
- name: order__order_item
151+
type:
152+
type: array
153+
items:
154+
type: record
155+
fields:
156+
- name: order_item__item
157+
type:
158+
type: record
159+
fields:
160+
- name: name
161+
type: string
162+
- name: price
163+
type: string
164+
name: item_collection
165+
namespace: Query.order_collection.user_collection.order_collection.order_item_collection
166+
name: order_item_collection
167+
namespace: Query.order_collection.user_collection.order_collection
168+
- name: id
169+
type: int
170+
name: order_collection
171+
namespace: Query.order_collection.user_collection
172+
- name: last_name
173+
type: string
174+
- name: first_name
175+
type: string
176+
- name: id
177+
type: int
178+
name: user_collection
179+
namespace: Query.order_collection
180+
- name: id
181+
type: int
182+
- name: description
183+
type: string
184+
name: order_collection
185+
namespace: Query
186+
187+
]]
188+
expected_avro_schema = yaml.decode(expected_avro_schema)
189+
local gql_query = gql_wrapper:compile(query)
190+
local variables = {
191+
user_id = 5,
192+
order_id = 20
193+
}
194+
195+
local avros = gql_query:avro_schema()
196+
test:is_deeply(avros, expected_avro_schema, "generated avro schema")
197+
local result_expected = [[
198+
user_collection:
199+
- order_connection:
200+
- user_id: 5
201+
id: 11
202+
description: order of user 5
203+
order__order_item:
204+
- order_id: 1
205+
item_id: 11
206+
order_item__item:
207+
id: 11
208+
price: '9.74'
209+
name: Money
210+
description: lobortis ultrices. Vivamus rhoncus.
211+
- order_id: 29
212+
item_id: 11
213+
order_item__item:
214+
id: 11
215+
price: '9.74'
216+
name: Money
217+
description: lobortis ultrices. Vivamus rhoncus.
218+
- order_id: 30
219+
item_id: 11
220+
order_item__item:
221+
id: 11
222+
price: '9.74'
223+
name: Money
224+
description: lobortis ultrices. Vivamus rhoncus.
225+
last_name: user ln 5
226+
first_name: user fn 5
227+
id: 5
228+
order_collection:
229+
- description: order of user 6
230+
user_connection:
231+
order_connection:
232+
- id: 16
233+
order__order_item:
234+
- order_item__item:
235+
name: Cup
236+
price: '8.05'
237+
- order_item__item:
238+
name: Cup
239+
price: '8.05'
240+
- order_item__item:
241+
name: Cup
242+
price: '8.05'
243+
last_name: user ln 6
244+
first_name: user fn 6
245+
id: 6
246+
id: 20
247+
]]
248+
result_expected = yaml.decode(result_expected)
249+
local result = gql_query:execute(variables)
250+
test:is_deeply(result, result_expected, 'graphql qury exec result')
251+
local ok, ash, r, fs, _
252+
ok, ash = avro.create(avros)
253+
assert(ok)
254+
ok, _ = avro.validate(ash, result)
255+
assert(ok)
256+
test:is(ok, true, 'gql result validation by avro')
257+
ok, fs = avro.compile(ash)
258+
assert(ok)
259+
ok, r = fs.flatten(result)
260+
assert(ok)
261+
ok, r = fs.unflatten(r)
262+
-- The test can fail if wrong avro-schema version is installed.
263+
-- Please install avro-schema >= fea0ead9d1.
264+
assert(ok)
265+
test:is_deeply(r, result_expected, 'res = unflatten(flatten(res))')
266+
267+
os.exit(test:check() == true and 0 or 1)

‎test/extra/to_avro_nested.test.lua

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/usr/bin/env tarantool
2+
local fio = require('fio')
3+
local yaml = require('yaml')
4+
local avro = require('avro_schema')
5+
local data = require('test_data_nested_record')
6+
local test = require('tap').test('to avro schema')
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' .. ';' ..
10+
package.path
11+
12+
local graphql = require('graphql')
13+
14+
box.cfg{wal_mode="none"}
15+
test:plan(4)
16+
17+
data.init_spaces()
18+
data.fill_test_data(box.space)
19+
20+
local accessor = graphql.accessor_space.new({
21+
schemas = data.meta.schemas,
22+
collections = data.meta.collections,
23+
service_fields = data.meta.service_fields,
24+
indexes = data.meta.indexes,
25+
})
26+
27+
local gql_wrapper = graphql.new({
28+
schemas = data.meta.schemas,
29+
collections = data.meta.collections,
30+
accessor = accessor,
31+
})
32+
33+
local query = [[
34+
query getUserByUid($uid: Long) {
35+
user(uid: $uid) {
36+
uid
37+
p1
38+
p2
39+
nested {
40+
x
41+
y
42+
}
43+
}
44+
}
45+
]]
46+
local expected_avro_schema = [[
47+
type: record
48+
name: Query
49+
fields:
50+
- name: user
51+
type:
52+
type: array
53+
items:
54+
type: record
55+
fields:
56+
- name: p2
57+
type: string
58+
- name: p1
59+
type: string
60+
- name: uid
61+
type: long
62+
- name: nested
63+
type:
64+
type: record
65+
fields:
66+
- name: y
67+
type: long
68+
- name: x
69+
type: long
70+
name: nested
71+
namespace: Query.user
72+
name: user
73+
namespace: Query
74+
]]
75+
expected_avro_schema = yaml.decode(expected_avro_schema)
76+
local gql_query = gql_wrapper:compile(query)
77+
local variables = {
78+
uid = 1,
79+
}
80+
81+
local avros = gql_query:avro_schema()
82+
83+
test:is_deeply(avros, expected_avro_schema, "generated avro schema")
84+
local result_expected = [[
85+
user:
86+
- p2: p2 1
87+
p1: p1 1
88+
uid: 1
89+
nested:
90+
y: 2001
91+
x: 1001
92+
]]
93+
result_expected = yaml.decode(result_expected)
94+
local result = gql_query:execute(variables)
95+
test:is_deeply(result, result_expected, 'graphql qury exec result')
96+
local ok, ash, r, fs, _
97+
ok, ash = avro.create(avros)
98+
assert(ok)
99+
ok, _ = avro.validate(ash, result)
100+
assert(ok)
101+
test:is(ok, true, 'gql result validation by avro')
102+
ok, fs = avro.compile(ash)
103+
assert(ok)
104+
ok, r = fs.flatten(result)
105+
assert(ok)
106+
ok, r = fs.unflatten(r)
107+
assert(ok)
108+
test:is_deeply(r, result_expected, 'res = unflatten(flatten(res))')
109+
110+
os.exit(test:check() == true and 0 or 1)

‎test/extra/to_avro_nullable.test.lua

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/usr/bin/env tarantool
2+
local fio = require('fio')
3+
local yaml = require('yaml')
4+
local avro = require('avro_schema')
5+
local testdata = require('nullable_index_testdata')
6+
local test = require('tap').test('to avro schema')
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' .. ';' ..
10+
package.path
11+
12+
local graphql = require('graphql')
13+
14+
box.cfg{wal_mode="none"}
15+
test:plan(4)
16+
17+
testdata.init_spaces()
18+
testdata.fill_test_data()
19+
local meta = testdata.get_test_metadata()
20+
21+
local accessor = graphql.accessor_space.new({
22+
schemas = meta.schemas,
23+
collections = meta.collections,
24+
service_fields = meta.service_fields,
25+
indexes = meta.indexes,
26+
})
27+
28+
local gql_wrapper = graphql.new({
29+
schemas = meta.schemas,
30+
collections = meta.collections,
31+
accessor = accessor,
32+
})
33+
34+
-- We do not select `customer_balances` and `favorite_holidays` because thay are
35+
-- is of `Map` type, which is not supported.
36+
local query = [[
37+
query get_foo($id: String) {
38+
bar(id: $id) {
39+
id
40+
id_or_null_1
41+
id_or_null_2
42+
id_or_null_3
43+
}
44+
}
45+
]]
46+
local expected_avro_schema = [[
47+
type: record
48+
name: Query
49+
fields:
50+
- name: bar
51+
type:
52+
type: array
53+
items:
54+
type: record
55+
fields:
56+
- name: id_or_null_1
57+
type: string*
58+
- name: id_or_null_3
59+
type: string*
60+
- name: id_or_null_2
61+
type: string*
62+
- name: id
63+
type: string
64+
name: bar
65+
namespace: Query
66+
]]
67+
expected_avro_schema = yaml.decode(expected_avro_schema)
68+
local gql_query = gql_wrapper:compile(query)
69+
local variables = {
70+
id = '101',
71+
}
72+
73+
local avros = gql_query:avro_schema()
74+
75+
test:is_deeply(avros, expected_avro_schema, "generated avro schema")
76+
local result_expected = [[
77+
bar:
78+
- id_or_null_3: '101'
79+
id_or_null_2: '101'
80+
id: '101'
81+
]]
82+
result_expected = yaml.decode(result_expected)
83+
local result = gql_query:execute(variables)
84+
test:is_deeply(result, result_expected, 'graphql qury exec result')
85+
local ok, ash, r, fs, _
86+
ok, ash = avro.create(avros)
87+
assert(ok)
88+
ok, _ = avro.validate(ash, result)
89+
assert(ok)
90+
test:is(ok, true, 'gql result validation by avro')
91+
ok, fs = avro.compile(ash)
92+
assert(ok)
93+
ok, r = fs.flatten(result)
94+
assert(ok)
95+
ok, r = fs.unflatten(r)
96+
assert(ok)
97+
test:is_deeply(r, result_expected, 'res = unflatten(flatten(res))')
98+
99+
os.exit(test:check() == true and 0 or 1)

0 commit comments

Comments
 (0)
This repository has been archived.