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

Commit 24ec6c4

Browse files
committed
add support for array avro type (only with scalars); change args
generation behavior - now args are NOT generated from arrays; do small refactor; add test for array avro type usage
1 parent 417c83e commit 24ec6c4

File tree

1 file changed

+82
-13
lines changed

1 file changed

+82
-13
lines changed

graphql/tarantool_graphql.lua

Lines changed: 82 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ local tarantool_graphql = {}
2222
-- forward declarations
2323
local gql_type
2424

25+
--- Returns type of the top element in the avro schema
2526
local function avro_type(avro_schema)
2627
if type(avro_schema) == 'table' then
2728
if avro_schema.type == 'record' then
@@ -104,10 +105,18 @@ local function convert_scalar_type(avro_schema, opts)
104105
elseif avro_t == 'string*' then
105106
return types.string
106107
end
108+
109+
if opts.is_items_type then
110+
error('avro array items must have know scalar type, not: ' ..
111+
json.encode(avro_schema))
112+
end
113+
107114
if raise then
108115
error('unrecognized avro-schema scalar type: ' ..
109-
json.encode(avro_schema))
116+
json.encode(avro_schema))
110117
end
118+
119+
111120
return nil
112121
end
113122

@@ -130,8 +139,6 @@ local function gql_argument_type(state, avro_schema)
130139
('avro_schema.fields must be a table, got %s (avro_schema %s)')
131140
:format(type(avro_schema.fields), json.encode(avro_schema)))
132141

133-
---@tfixme iteration over null table
134-
--- maybe avro.scheme was meant?
135142
local fields = {}
136143
for _, field in ipairs(avro_schema.fields) do
137144

@@ -170,16 +177,31 @@ end
170177

171178
--- Returns table of record's arguments
172179
--- all arguments are nullable
173-
local function convert_record_fields_to_args(state, fields)
180+
---
181+
--- @tparam table state
182+
--- @tparam table fields
183+
--- @tparam table opts include is_for_args flag to specify
184+
--- case when the function is used to collect arguments
185+
local function convert_record_fields_to_args(state, fields, opts)
174186
local args = {}
187+
local is_for_args = opts and opts.is_for_args or false
188+
175189
for _, field in ipairs(fields) do
176190

177191
assert(type(field.name) == 'string',
178192
('field.name must be a string, got %s (schema %s)')
179193
:format(type(field.name), json.encode(field)))
180194

181195
local gql_class = gql_argument_type(state, field.type)
182-
args[field.name] = nullable(gql_class)
196+
197+
-- arrays (gql lists) and maps can't be arguments
198+
-- so these kinds are to be skipped
199+
200+
---@todo consider case when gql_class is wrapper nonNull around List
201+
--- or Map
202+
if not(is_for_args and (gql_class == 'List' or gql_class == 'Map')) then
203+
args[field.name] = nullable(gql_class)
204+
end
183205
end
184206
return args
185207
end
@@ -190,9 +212,12 @@ end
190212
--- @tparam table state for read state.accessor and previously filled
191213
--- state.types
192214
--- @tparam table fields fields part from an avro-schema
193-
local function convert_record_fields(state, fields)
215+
--- @tparam table opts include is_for_args flag to specify
216+
--- case when the function is used to collect arguments
217+
local function convert_record_fields(state, fields, opts)
194218
local res = {}
195219
local object_args = {}
220+
local is_for_args = opts and opts.is_for_args or false
196221

197222
for _, field in ipairs(fields) do
198223
assert(type(field.name) == 'string',
@@ -203,12 +228,22 @@ local function convert_record_fields(state, fields)
203228
name = field.name,
204229
kind = gql_type(state, field.type),
205230
}
206-
object_args[field.name] = nullable(res[field.name].kind)
231+
232+
233+
-- arrays (gql lists) and maps can't be arguments
234+
-- so these kinds are to be skipped
235+
236+
---@todo consider case when gql_class is wrapper nonNull around List
237+
--- or Map
238+
if not (is_for_args and (res[field.name].kind == 'List'
239+
or res[field.name].kind == 'Map')) then
240+
object_args[field.name] = nullable(res[field.name].kind)
241+
end
207242
end
208243
return res, object_args
209244
end
210245

211-
--- The function recursively converts passed avro-schema to a graphql type.
246+
--- The function recursively converts passed avro-schema to a graphql type (kind)
212247
---
213248
--- @tparam table state for read state.accessor and previously filled
214249
--- state.types (state.types are gql types)
@@ -237,12 +272,14 @@ gql_type = function(state, avro_schema, collection, collection_name)
237272
('collection and collection_name must be nils or ' ..
238273
'non-nils simultaneously, got: %s and %s'):format(type(collection),
239274
type(collection_name)))
275+
240276
local accessor = state.accessor
241277
assert(accessor ~= nil, 'state.accessor must not be nil')
242278
assert(accessor.select ~= nil, 'state.accessor.select must not be nil')
243279
assert(accessor.list_args ~= nil,
244280
'state.accessor.list_args must not be nil')
245281

282+
-- type of the top element in the avro schema
246283
local avro_t = avro_type(avro_schema)
247284

248285
if avro_t == 'record' or avro_t == 'record*' then
@@ -255,7 +292,7 @@ gql_type = function(state, avro_schema, collection, collection_name)
255292

256293
local fields, _ = convert_record_fields(state, avro_schema.fields)
257294

258-
-- if collection param is passed
295+
-- if collection param is passed then go over all connections
259296
for _, c in ipairs((collection or {}).connections or {}) do
260297
assert(type(c.type) == 'string',
261298
'connection.type must be a string, got ' .. type(c.type))
@@ -269,6 +306,7 @@ gql_type = function(state, avro_schema, collection, collection_name)
269306
assert(type(c.parts) == 'table',
270307
'connection.parts must be a string, got ' .. type(c.parts))
271308

309+
-- gql type of connection field
272310
local destination_type =
273311
state.types[c.destination_collection]
274312
assert(destination_type ~= nil,
@@ -287,20 +325,24 @@ gql_type = function(state, avro_schema, collection, collection_name)
287325

288326
local c_list_args = state.list_arguments[c.destination_collection]
289327

328+
-- change fields that are represented by connections
290329
fields[c.name] = {
291330
name = c.name,
292331
kind = destination_type,
293332
arguments = c_args,
294333
resolve = function(parent, args_instance, info)
295334
local destination_args_names = {}
296335
local destination_args_values = {}
336+
297337
for _, part in ipairs(c.parts) do
338+
298339
assert(type(part.source_field) == 'string',
299340
'part.source_field must be a string, got ' ..
300341
type(part.destination_field))
301342
assert(type(part.destination_field) == 'string',
302343
'part.destination_field must be a string, got ' ..
303344
type(part.destination_field))
345+
304346
destination_args_names[#destination_args_names + 1] =
305347
part.destination_field
306348
destination_args_values[#destination_args_values + 1] =
@@ -344,15 +386,38 @@ gql_type = function(state, avro_schema, collection, collection_name)
344386
}
345387
end
346388

389+
-- create gql schema
347390
local res = types.object({
348391
name = collection ~= nil and collection.name or avro_schema.name,
349392
description = 'generated from avro-schema for ' ..
350393
avro_schema.name,
351394
fields = fields,
352395
})
353396
return avro_t == 'enum' and types.nonNull(res) or res
397+
354398
elseif avro_t == 'enum' then
355399
error('enums not implemented yet') -- XXX
400+
401+
elseif avro_t == 'array' or avro_t == 'array*' then
402+
403+
assert(avro_schema.items ~= nil,
404+
'items field must not be nil in array avro schema')
405+
assert(type(avro_schema.items) == 'string',
406+
'avro_schema.items must be a string, got ' .. type(avro_schema.item))
407+
408+
local gql_items_type = convert_scalar_type(avro_schema.items,
409+
{is_items_type=true, raise=true})
410+
411+
local gql_array = types.list(gql_items_type)
412+
413+
if avro_t == 'array*' then
414+
return gql_array
415+
end
416+
417+
if avro_t == 'array' then
418+
return types.nonNull(gql_array)
419+
end
420+
356421
else
357422
local res = convert_scalar_type(avro_schema, {raise = false})
358423
if res == nil then
@@ -399,15 +464,19 @@ local function parse_cfg(cfg)
399464
schema.name))
400465

401466
-- recursively converts all avro types into gql types in the given schema
467+
assert(schema.type == 'record',
468+
'top-level schema must have record avro type, not' .. schema.type)
402469
state.types[collection_name] = gql_type(state, schema, collection, collection_name)
403470

404-
-- prepare arguments
471+
-- prepare arguments (their kinds)
405472
local _, object_args = convert_record_fields(state,
406-
schema.fields)
473+
schema.fields, {is_for_args=true})
407474
local list_args = convert_record_fields_to_args(
408-
state, accessor:list_args(collection_name))
475+
state, accessor:list_args(collection_name), {is_for_args=true})
409476
local args = utils.merge_tables(object_args, list_args)
410477

478+
-- list and map (avro array and map) can't be arguments
479+
411480
state.object_arguments[collection_name] = object_args
412481
state.list_arguments[collection_name] = list_args
413482
state.all_arguments[collection_name] = args
@@ -580,4 +649,4 @@ function tarantool_graphql.new(cfg)
580649
})
581650
end
582651

583-
return tarantool_graphql
652+
return tarantool_graphql

0 commit comments

Comments
 (0)