@@ -22,6 +22,7 @@ local tarantool_graphql = {}
22
22
-- forward declarations
23
23
local gql_type
24
24
25
+ --- Returns type of the top element in the avro schema
25
26
local function avro_type (avro_schema )
26
27
if type (avro_schema ) == ' table' then
27
28
if avro_schema .type == ' record' then
@@ -104,10 +105,18 @@ local function convert_scalar_type(avro_schema, opts)
104
105
elseif avro_t == ' string*' then
105
106
return types .string
106
107
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
+
107
114
if raise then
108
115
error (' unrecognized avro-schema scalar type: ' ..
109
- json .encode (avro_schema ))
116
+ json .encode (avro_schema ))
110
117
end
118
+
119
+
111
120
return nil
112
121
end
113
122
@@ -130,8 +139,6 @@ local function gql_argument_type(state, avro_schema)
130
139
(' avro_schema.fields must be a table, got %s (avro_schema %s)' )
131
140
:format (type (avro_schema .fields ), json .encode (avro_schema )))
132
141
133
- --- @tfixme iteration over null table
134
- --- maybe avro.scheme was meant?
135
142
local fields = {}
136
143
for _ , field in ipairs (avro_schema .fields ) do
137
144
@@ -170,16 +177,31 @@ end
170
177
171
178
--- Returns table of record's arguments
172
179
--- 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 )
174
186
local args = {}
187
+ local is_for_args = opts and opts .is_for_args or false
188
+
175
189
for _ , field in ipairs (fields ) do
176
190
177
191
assert (type (field .name ) == ' string' ,
178
192
(' field.name must be a string, got %s (schema %s)' )
179
193
:format (type (field .name ), json .encode (field )))
180
194
181
195
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
183
205
end
184
206
return args
185
207
end
190
212
--- @tparam table state for read state.accessor and previously filled
191
213
--- state.types
192
214
--- @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 )
194
218
local res = {}
195
219
local object_args = {}
220
+ local is_for_args = opts and opts .is_for_args or false
196
221
197
222
for _ , field in ipairs (fields ) do
198
223
assert (type (field .name ) == ' string' ,
@@ -203,12 +228,22 @@ local function convert_record_fields(state, fields)
203
228
name = field .name ,
204
229
kind = gql_type (state , field .type ),
205
230
}
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
207
242
end
208
243
return res , object_args
209
244
end
210
245
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)
212
247
---
213
248
--- @tparam table state for read state.accessor and previously filled
214
249
--- state.types (state.types are gql types)
@@ -237,12 +272,14 @@ gql_type = function(state, avro_schema, collection, collection_name)
237
272
(' collection and collection_name must be nils or ' ..
238
273
' non-nils simultaneously, got: %s and %s' ):format (type (collection ),
239
274
type (collection_name )))
275
+
240
276
local accessor = state .accessor
241
277
assert (accessor ~= nil , ' state.accessor must not be nil' )
242
278
assert (accessor .select ~= nil , ' state.accessor.select must not be nil' )
243
279
assert (accessor .list_args ~= nil ,
244
280
' state.accessor.list_args must not be nil' )
245
281
282
+ -- type of the top element in the avro schema
246
283
local avro_t = avro_type (avro_schema )
247
284
248
285
if avro_t == ' record' or avro_t == ' record*' then
@@ -255,7 +292,7 @@ gql_type = function(state, avro_schema, collection, collection_name)
255
292
256
293
local fields , _ = convert_record_fields (state , avro_schema .fields )
257
294
258
- -- if collection param is passed
295
+ -- if collection param is passed then go over all connections
259
296
for _ , c in ipairs ((collection or {}).connections or {}) do
260
297
assert (type (c .type ) == ' string' ,
261
298
' connection.type must be a string, got ' .. type (c .type ))
@@ -269,6 +306,7 @@ gql_type = function(state, avro_schema, collection, collection_name)
269
306
assert (type (c .parts ) == ' table' ,
270
307
' connection.parts must be a string, got ' .. type (c .parts ))
271
308
309
+ -- gql type of connection field
272
310
local destination_type =
273
311
state .types [c .destination_collection ]
274
312
assert (destination_type ~= nil ,
@@ -287,20 +325,24 @@ gql_type = function(state, avro_schema, collection, collection_name)
287
325
288
326
local c_list_args = state .list_arguments [c .destination_collection ]
289
327
328
+ -- change fields that are represented by connections
290
329
fields [c .name ] = {
291
330
name = c .name ,
292
331
kind = destination_type ,
293
332
arguments = c_args ,
294
333
resolve = function (parent , args_instance , info )
295
334
local destination_args_names = {}
296
335
local destination_args_values = {}
336
+
297
337
for _ , part in ipairs (c .parts ) do
338
+
298
339
assert (type (part .source_field ) == ' string' ,
299
340
' part.source_field must be a string, got ' ..
300
341
type (part .destination_field ))
301
342
assert (type (part .destination_field ) == ' string' ,
302
343
' part.destination_field must be a string, got ' ..
303
344
type (part .destination_field ))
345
+
304
346
destination_args_names [# destination_args_names + 1 ] =
305
347
part .destination_field
306
348
destination_args_values [# destination_args_values + 1 ] =
@@ -344,15 +386,38 @@ gql_type = function(state, avro_schema, collection, collection_name)
344
386
}
345
387
end
346
388
389
+ -- create gql schema
347
390
local res = types .object ({
348
391
name = collection ~= nil and collection .name or avro_schema .name ,
349
392
description = ' generated from avro-schema for ' ..
350
393
avro_schema .name ,
351
394
fields = fields ,
352
395
})
353
396
return avro_t == ' enum' and types .nonNull (res ) or res
397
+
354
398
elseif avro_t == ' enum' then
355
399
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
+
356
421
else
357
422
local res = convert_scalar_type (avro_schema , {raise = false })
358
423
if res == nil then
@@ -399,15 +464,19 @@ local function parse_cfg(cfg)
399
464
schema .name ))
400
465
401
466
-- 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 )
402
469
state .types [collection_name ] = gql_type (state , schema , collection , collection_name )
403
470
404
- -- prepare arguments
471
+ -- prepare arguments (their kinds)
405
472
local _ , object_args = convert_record_fields (state ,
406
- schema .fields )
473
+ schema .fields , { is_for_args = true } )
407
474
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 } )
409
476
local args = utils .merge_tables (object_args , list_args )
410
477
478
+ -- list and map (avro array and map) can't be arguments
479
+
411
480
state .object_arguments [collection_name ] = object_args
412
481
state .list_arguments [collection_name ] = list_args
413
482
state .all_arguments [collection_name ] = args
@@ -580,4 +649,4 @@ function tarantool_graphql.new(cfg)
580
649
})
581
650
end
582
651
583
- return tarantool_graphql
652
+ return tarantool_graphql
0 commit comments