2
2
---
3
3
--- Random notes:
4
4
---
5
- --- * The best way to use this module is to just call `avro_schema` methon on
5
+ --- * The best way to use this module is to just call `avro_schema` method on
6
6
--- compiled query object.
7
7
local path = " graphql.core"
8
8
local introspection = require (path .. ' .introspection' )
@@ -11,6 +11,9 @@ local query_util = require(path .. '.query_util')
11
11
-- module functions
12
12
local query_to_avro = {}
13
13
14
+ -- forward declaration
15
+ local object_to_avro
16
+
14
17
local gql_scalar_to_avro_index = {
15
18
String = " string" ,
16
19
Int = " int" ,
@@ -28,79 +31,84 @@ local function gql_scalar_to_avro(fieldType)
28
31
return result
29
32
end
30
33
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
34
+ --- The function converts avro type to the corresponding nullable type in
35
+ --- place and returns the result.
36
+ ---
37
+ --- We make changes in place in case of table input (`avro`) because of
38
+ --- performance reasons, but we returns the result because an input (`avro`)
39
+ --- can be a string. Strings in Lua are immutable.
40
+ ---
41
+ --- In the current tarantool/avro-schema implementation we simply add '*' to
42
+ --- the end of a type name.
43
+ ---
44
+ --- If the type is already nullable the function leaves it as is.
45
+ ---
46
+ --- @tparam table avro avro schema node to be converted to nullable one
47
+ ---
48
+ --- @result `result` (string or table) nullable avro type
41
49
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
50
+ assert (avro ~= nil , " avro must not be nil" )
51
+
52
+ local value_type = type (avro )
53
+
54
+ if value_type == " string" then
55
+ return avro :endswith (" *" ) and avro or (avro .. ' *' )
56
+ elseif value_type == " table" then
57
+ return make_avro_type_nullable (avro .type )
53
58
end
54
- error (" Avro type should be a string or table, got :" .. type_type )
55
- end
56
59
57
- local object_to_avro
60
+ error (" avro should be a string or a table, got " .. value_type )
61
+ end
58
62
59
- local function complete_field_to_avro (fieldType , result , subSelections , context ,
60
- NonNull )
63
+ --- Convert GraphQL type to avro-schema with selecting fields.
64
+ ---
65
+ --- @tparam table fieldType GraphQL type
66
+ ---
67
+ --- @tparam table subSelections fields to select from resulting avro-schema
68
+ --- (internal graphql-lua format)
69
+ ---
70
+ --- @tparam table context current traversal context, here it just falls to the
71
+ --- called functions (internal graphql-lua format)
72
+ ---
73
+ --- @tresult table `result` is the resulting avro-schema
74
+ local function gql_type_to_avro (fieldType , subSelections , context )
61
75
local fieldTypeName = fieldType .__type
62
- if fieldTypeName == ' NonNull' then
63
- -- In case the field is NonNull, the real type is in ofType attribute.
76
+ local isNonNull = false
77
+
78
+ -- In case the field is NonNull, the real type is in ofType attribute.
79
+ while fieldTypeName == ' NonNull' do
64
80
fieldType = fieldType .ofType
65
81
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
82
+ isNonNull = true
72
83
end
73
84
85
+ local result
86
+
74
87
if fieldTypeName == ' List' then
75
88
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 = {
89
+ local innerTypeAvro = gql_type_to_avro (innerType , subSelections ,
90
+ context )
91
+ result = {
83
92
type = " array" ,
84
- items = items
93
+ items = innerTypeAvro ,
85
94
}
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
95
+ elseif fieldTypeName == ' Scalar' then
96
+ result = gql_scalar_to_avro (fieldType )
97
+ elseif fieldTypeName == ' Object' then
98
+ result = object_to_avro (fieldType , subSelections , context )
97
99
elseif fieldTypeName == ' Interface' or fieldTypeName == ' Union' then
98
100
error (' Interfaces and Unions are not supported yet' )
101
+ else
102
+ error (string.format (' Unknown type "%s"' , tostring (fieldTypeName )))
99
103
end
100
- error (string.format (' Unknown type "%s"' , fieldTypeName ))
104
+
105
+ if not isNonNull then
106
+ result = make_avro_type_nullable (result )
107
+ end
108
+ return result
101
109
end
102
110
103
- --- The function converts a single Object field to avro format
111
+ --- The function converts a single Object field to avro format.
104
112
local function field_to_avro (object_type , fields , context )
105
113
local firstField = fields [1 ]
106
114
assert (# fields == 1 , " The aliases are not considered yet" )
@@ -109,11 +117,13 @@ local function field_to_avro(object_type, fields, context)
109
117
object_type .fields [fieldName ]
110
118
assert (fieldType ~= nil )
111
119
local subSelections = query_util .mergeSelectionSets (fields )
112
- local result = {}
113
- result .name = fieldName
114
- result = complete_field_to_avro (fieldType .kind , result , subSelections ,
120
+
121
+ local fieldTypeAvro = gql_type_to_avro (fieldType .kind , subSelections ,
115
122
context )
116
- return result
123
+ return {
124
+ name = fieldName ,
125
+ type = fieldTypeAvro ,
126
+ }
117
127
end
118
128
119
129
--- Convert GraphQL object to avro record.
127
137
--- of the fields is `namespace_parts` -- table of names of records from the
128
138
--- root to the current object
129
139
---
130
- --- @treturn table corresponding Avro schema
140
+ --- @treturn table `result` is the corresponding Avro schema
131
141
object_to_avro = function (object_type , selections , context )
132
142
local groupedFieldSet = query_util .collectFields (object_type , selections ,
133
143
{}, {}, context )
@@ -152,11 +162,11 @@ end
152
162
---
153
163
--- @tparam table query object which avro schema should be created for
154
164
---
155
- --- @treturn table `avro_schema` avro schema for any `query:execute()` result.
165
+ --- @treturn table `avro_schema` avro schema for any `query:execute()` result
156
166
function query_to_avro .convert (query )
157
167
assert (type (query ) == " table" ,
158
- ' query should be a table, got: ' .. type ( table )
159
- .. ' ; hint: use ":" instead of "."' )
168
+ ( ' query should be a table, got: %s; ' ..
169
+ ' hint: use ":" instead of "."' ): format ( type ( table )) )
160
170
local state = query .state
161
171
local context = query_util .buildContext (state .schema , query .ast , {}, {},
162
172
query .operation_name )
0 commit comments