1
+ --- Config complement module provide an ability to complement user-defined config
2
+ --- (in a simplified format) to a fully specified format.
3
+ ---
4
+ ---- -- Notes:
5
+ ---
6
+ --- * Currently the module complements only connections (cfg.connections),
7
+ -- see @{complement_connections}.
8
+
9
+ local json = require (' json' )
1
10
local utils = require (' graphql.utils' )
2
- local at = utils .assert_type
11
+ local check = utils .check
12
+ local get_spaces_formats = require (' graphql.simple_config' ).get_spaces_formats
3
13
4
14
local config_complement = {}
5
15
6
- --- The function determines connection type by fully qualified connection.parts
7
- --- and source collection space format
8
- --- Items in parts must be sorted by destination_field to match field order
9
- --- in connection's index
10
- local function determine_connection_type (connection_parts , index , source_format )
16
+ --- The function determines connection type by connection.parts
17
+ --- and source collection space format.
18
+ local function determine_connection_type (connection_parts , index , source_space_format )
11
19
local type
12
20
13
21
if # connection_parts < # (index .fields ) then
@@ -22,142 +30,189 @@ local function determine_connection_type(connection_parts, index, source_format)
22
30
end
23
31
end
24
32
25
- -- todo still not clear how to handle nullables
33
+ local is_all_nullable = true
34
+ local is_all_not_nullable = true
35
+
36
+ for _ , connection_part in pairs (connection_parts ) do
37
+ for _ ,field_format in ipairs (source_space_format ) do
38
+ if connection_part .source_field == field_format .name then
39
+ if field_format .is_nullable == true then
40
+ is_all_not_nullable = false
41
+ else
42
+ is_all_nullable = false
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ if is_all_nullable == is_all_not_nullable then
49
+ error (' source_fields in connection_parts must be all nullable or ' ..
50
+ ' not nullable at the same time' )
51
+ end
52
+
53
+ if is_all_nullable then
54
+ type = type .. ' *'
55
+ end
56
+
26
57
return type
27
58
end
28
59
29
- --- XXX Currently if connections_parts have destination_fields that do not
30
- --- exist in connection's index the assertion is raised. Maybe it is not a
31
- --- desirable behavior. Consider the following case: you have working
32
- --- tarantool_graphql, then you try to change configuration a bit and
33
- --- tarantool_graphql crushes.
34
60
local function sort_parts (connection_parts , index_fields )
35
61
local sorted_parts = {}
62
+
63
+ -- check if fields in connection_parts exist in index_fields
36
64
for _ , part in pairs (connection_parts ) do
37
65
local is_found = false
38
66
for i , index_field in ipairs (index_fields ) do
39
67
if part .destination_field == index_field then
40
- sorted_parts [i ] = part
41
68
is_found = true
42
69
end
43
70
end
44
71
assert (is_found , (' part.destination_field %s was not found in ' ..
45
- ' connection index' ):format (part .destination_field ))
72
+ ' connection index %s' ):format (part .destination_field ,
73
+ json .encode (index_fields )))
46
74
end
47
- return sorted_parts ;
75
+
76
+ -- sort parts and check that sorted_parts form index prefix
77
+ -- (including index itself)
78
+ for i = 1 , utils .size (connection_parts ) do
79
+ local index_field = index_fields [i ]
80
+ for _ , part in pairs (connection_parts ) do
81
+ if part .destination_field == index_field then
82
+ sorted_parts [i ] = index_field
83
+ break
84
+ end
85
+ -- no match found
86
+ error ((' given parts %s does not form an index or an index ' ..
87
+ ' prefix %s' ):format (json .encode (connection_parts ),
88
+ json .encode (index_fields )))
89
+ end
90
+ end
91
+ return sorted_parts
48
92
end
49
93
50
- --- The functions determines fully qualified connection's parts based on
51
- --- connection's index and incompletely qualified connection's part given by user
52
- --- @tparam table parts incompletely qualified connection's part given by user
53
- --- @tparam table index connection index (cfg.indexes[collection][index_name]
94
+ --- The functions determines fully defined connection's parts based on
95
+ --- connection's index and partially defined connection's part given by user.
96
+ --- @tparam table parts partially defined connection's part given by user
97
+ --- @tparam table index connection index (cfg.indexes[collection][index_name])
54
98
--- order of items in index.fields must be the same as in index format
55
99
local function determine_connection_parts (parts , index )
56
- at (parts , ' parts' , ' nil' , ' number' , ' table' )
100
+ check (parts , ' parts' , ' nil' , ' number' , ' table' )
57
101
local result_parts = {}
58
102
59
- -- full index case
103
+ -- User defined no parts of the connection. All connection's index fields
104
+ -- are taken as 'parts'
60
105
if type (parts ) == ' nil' then
61
106
for i , v in ipairs (index .fields ) do
62
107
result_parts [i ] = {source_field = v , destination_field = v }
63
108
end
64
109
end
65
110
66
- -- index prefix case
111
+ -- User defined a number of fields of index which must form index prefix.
112
+ -- First 'number' index fields are taken as 'parts'
67
113
if type (parts ) == ' number' then
68
114
for i = 1 , parts do
69
115
local v = index .fields [i ]
70
116
result_parts [i ] = {source_field = v , destination_field = v }
71
117
end
72
118
end
73
119
74
- -- fully qualified parts case
120
+ -- User defined parts as pairs of {source_field: foo_field,
121
+ -- destination_field: boo_field}. These 'parts' may correspond either to full
122
+ -- index or index prefix
75
123
if type (parts ) == ' table' then
76
- local sorted_parts = sort_parts (parts , index .fields )
77
- for i , v in ipairs (sorted_parts ) do
78
- result_parts [i ] = {source_field = v , destination_field = v }
79
- end
124
+ -- sorting parts is necessary to check if user defined part form an
125
+ -- index or an index prefix
126
+ result_parts = sort_parts (parts , index .fields )
80
127
end
81
128
82
129
return result_parts
83
130
end
84
131
85
- ---
86
132
--- The function complements collections' connections, described in simplified
87
133
--- format, to connections in a fully specified format. Connection's
88
134
--- collection determined on source_collection. Type determined on index type.
89
135
--- Notice an example:
90
136
---
91
- --- "connections" : [
92
- --- {
93
- --- "name": "order_connection",
94
- --- "source_collection": "user_collection",
95
- --- "destination_collection": "order_collection"
96
- --- "index_name": "user_id_index",
97
- --- "parts" : nil | number | table (destination fields can be omitted)
98
- --- },
99
- --- ...
100
- --- ]
101
- --- will produce following complement in 'user_collection' :
137
+ --- "connections" : [
138
+ --- {
139
+ --- "name": "order_connection",
140
+ --- "source_collection": "user_collection",
141
+ --- "destination_collection": "order_collection"
142
+ --- "index_name": "user_id_index",
143
+ --- "parts" : nil | number | table (destination fields can be omitted)
144
+ --- in case of 'table' expected format is:
145
+ --- "parts": [
146
+ --- {"source_field": "user_id", "destination_field": "user_id"},
147
+ --- ...
148
+ --- ]
149
+ --- },
150
+ --- ...
151
+ --- ]
102
152
---
103
- --- "user_collection": {
104
- --- "schema_name": "user",
105
- --- "connections": [
106
- --- {
107
- --- "type": "1:N",
108
- --- "name": "order_connection",
109
- --- "destination_collection": "order_collection",
110
- --- "parts": [
111
- --- { "source_field": "user_id", "destination_field": "user_id" }
112
- --- ],
113
- --- "index_name": "user_id_index"
114
- --- },
153
+ --- will produce following complement in 'user_collection' :
115
154
---
116
- --- ]
117
- --- }
155
+ --- "user_collection": {
156
+ --- "schema_name": "user",
157
+ --- "connections": [
158
+ --- {
159
+ --- "type": "1:N",
160
+ --- "name": "order_connection",
161
+ --- "destination_collection": "order_collection",
162
+ --- "parts": [
163
+ --- { "source_field": "user_id", "destination_field": "user_id" }
164
+ --- ],
165
+ --- "index_name": "user_id_index"
166
+ --- },
167
+ --- ]
168
+ --- }
118
169
---
119
170
--- @tparam table collections cfg.collections
120
171
--- @tparam table connections cfg.connections - user-defined collections
121
- --- @tparam table indexes cfg.indexes - {collection_name = collection_indexes}
172
+ --- @tparam table indexes cfg.indexes - {[ collection_name] = collection_indexes, ... }
122
173
local function complement_connections (collections , connections , indexes , schemas )
123
174
if connections == nil then
124
175
return collections
125
176
end
126
177
127
- at (collections , ' collections' , ' table' )
128
- at (connections , ' connections' , ' table' )
178
+ check (collections , ' collections' , ' table' )
179
+ check (connections , ' connections' , ' table' )
180
+
181
+ local spaces_formats = get_spaces_formats ()
129
182
130
183
for _ , c in pairs (connections ) do
131
- at (c .name , ' connection.name' , ' string' )
132
- at (c .source_collection , ' connection.source_collection' , ' string' )
133
- at (c .destination_collection , ' connection.destination_collection' ,
134
- ' string' )
135
- at (c .index_name , ' connection.index_name' , ' string' )
136
- at (c .parts , ' connection.parts' , ' string' , ' table' , ' nil' )
184
+ check (c .name , ' connection.name' , ' string' )
185
+ check (c .source_collection , ' connection.source_collection' , ' string' )
186
+ check (c .destination_collection , ' connection.destination_collection' ,
187
+ ' string' )
188
+ check (c .index_name , ' connection.index_name' , ' string' )
189
+ check (c .parts , ' connection.parts' , ' string' , ' table' , ' nil' )
137
190
138
191
local index = indexes [c .source_collection ][c .index_name ]
139
192
assert (index .unique ~= nil , ' index.unique must not be nil ' ..
140
193
' during connections complementing' )
141
194
142
195
local result_c = {}
196
+ result_c .source_collection = c .source_collection
197
+ result_c .destination_collection = c .destination_collection
143
198
result_c .parts = determine_connection_parts (c .parts , index )
144
- result_c .type = determine_connection_type (result_c .parts , index )
199
+
200
+ local source_space_format = spaces_formats [result_c .source_collection ]
201
+
202
+ result_c .type = determine_connection_type (result_c .parts , index ,
203
+ source_space_format )
145
204
result_c .index_name = c .index_name
146
- result_c .destination_collection = c .destination_collection
147
205
result_c .name = c .name
148
206
149
207
local collection_connections = collections [c .source_collection ].
150
- connections
151
- if collection_connections == nil then
152
- collection_connections = {}
153
- end
208
+ connections or {}
154
209
collection_connections [# collection_connections + 1 ] = result_c
155
210
end
156
-
157
211
return collections
158
212
end
159
213
160
- --- The function complements
214
+ --- The function complements cfg.collection.connections using given
215
+ --- cfg.connections. See @{complement_connections} for details.
161
216
function config_complement .complement_cfg (cfg )
162
217
cfg .collections = complement_connections (cfg .collections , cfg .connections ,
163
218
cfg .indexes )
0 commit comments