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

Commit 275f03c

Browse files
committed
enable calling tarantool_graphql.new() with {auto_cfg = true} to generate configs
1 parent 61a8ac0 commit 275f03c

14 files changed

+795
-315
lines changed

graphql/accessor_general.lua

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,7 @@ local function process_tuple(state, tuple, opts)
827827

828828
-- convert tuple -> object
829829
local obj = opts.unflatten_tuple(collection_name, tuple,
830-
opts.default_unflatten_tuple)
830+
opts.default_unflatten_tuple, opts.is_collection_formatted)
831831

832832
-- skip all items before pivot (the item pointed by offset)
833833
if not state.pivot_found and pivot_filter then
@@ -975,6 +975,7 @@ local function select_internal(self, collection_name, from, filter, args, extra)
975975
fetched_object_cnt_max = self.settings.fetched_object_cnt_max,
976976
collection_name = collection_name,
977977
unflatten_tuple = self.funcs.unflatten_tuple,
978+
is_collection_formatted = self.formatted_collections[collection_name] or false,
978979
default_unflatten_tuple = default_unflatten_tuple,
979980
pcre = args.pcre,
980981
resolveField = extra.resolveField,
@@ -1179,6 +1180,7 @@ local function get_pcre_argument_type(self, collection_name)
11791180
return pcre_type
11801181
end
11811182

1183+
--todo add comments about opts.formatted_collections (also assertions)
11821184
--- Create a new data accessor.
11831185
---
11841186
--- Provided `funcs` argument determines certain functions for retrieving
@@ -1231,6 +1233,7 @@ function accessor_general.new(opts, funcs)
12311233
assert(type(funcs) == 'table',
12321234
'funcs must be a table, got ' .. type(funcs))
12331235

1236+
--todo assertions for opt.formatted_collections
12341237
local schemas = opts.schemas
12351238
local collections = opts.collections
12361239
local service_fields = opts.service_fields
@@ -1285,6 +1288,7 @@ function accessor_general.new(opts, funcs)
12851288
indexes = indexes,
12861289
models = models,
12871290
default_unflatten_tuple = default_unflatten_tuple,
1291+
formatted_collections = opts.formatted_collections,
12881292
index_cache = index_cache,
12891293
funcs = funcs,
12901294
settings = {

graphql/accessor_space.lua

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ end
3131
--- ```
3232
--- return default(collection_name, tuple)
3333
--- ```
34-
local function unflatten_tuple(collection_name, tuple, default)
34+
local function unflatten_tuple(collection_name, tuple, default, is_collection_formatted)
35+
if is_collection_formatted then
36+
return tuple:tomap()
37+
end
3538
return default(collection_name, tuple)
3639
end
3740

@@ -48,6 +51,8 @@ function accessor_space.new(opts, funcs)
4851
'funcs values must be functions, got ' .. type(v))
4952
end
5053

54+
-- todo add some note in comments that funcs.unflatten_tuple must
55+
-- manage our tuple:tomap or unflatten issue
5156
local res_funcs = {
5257
is_collection_exists = funcs.is_collection_exists or
5358
is_collection_exists,

graphql/config_complement.lua

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
local utils = require('graphql.utils')
2+
local at = utils.assert_type
3+
4+
local config_complement = {}
5+
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)
11+
local type
12+
13+
if #connection_parts < #(index.fields) then
14+
type = '1:N'
15+
end
16+
17+
if #connection_parts == #(index.fields) then
18+
if index.unique then
19+
type = '1:1'
20+
else
21+
type = '1:N'
22+
end
23+
end
24+
25+
-- todo still not clear how to handle nullables
26+
return type
27+
end
28+
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+
local function sort_parts(connection_parts, index_fields)
35+
local sorted_parts = {}
36+
for _, part in pairs(connection_parts) do
37+
local is_found = false
38+
for i, index_field in ipairs(index_fields) do
39+
if part.destination_field == index_field then
40+
sorted_parts[i] = part
41+
is_found = true
42+
end
43+
end
44+
assert(is_found, ('part.destination_field %s was not found in ' ..
45+
'connection index'):format(part.destination_field))
46+
end
47+
return sorted_parts;
48+
end
49+
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]
54+
--- order of items in index.fields must be the same as in index format
55+
local function determine_connection_parts(parts, index)
56+
at(parts, 'parts', 'nil', 'number', 'table')
57+
local result_parts = {}
58+
59+
-- full index case
60+
if type(parts) == 'nil' then
61+
for i, v in ipairs(index.fields) do
62+
result_parts[i] = {source_field = v, destination_field = v}
63+
end
64+
end
65+
66+
-- index prefix case
67+
if type(parts) == 'number' then
68+
for i = 1, parts do
69+
local v = index.fields[i]
70+
result_parts[i] = {source_field = v, destination_field = v}
71+
end
72+
end
73+
74+
-- fully qualified parts case
75+
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
80+
end
81+
82+
return result_parts
83+
end
84+
85+
---
86+
--- The function complements collections' connections, described in simplified
87+
--- format, to connections in a fully specified format. Connection's
88+
--- collection determined on source_collection. Type determined on index type.
89+
--- Notice an example:
90+
---
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' :
102+
---
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+
--- },
115+
---
116+
--- ]
117+
--- }
118+
---
119+
--- @tparam table collections cfg.collections
120+
--- @tparam table connections cfg.connections - user-defined collections
121+
--- @tparam table indexes cfg.indexes - {collection_name = collection_indexes}
122+
local function complement_connections(collections, connections, indexes, schemas)
123+
if connections == nil then
124+
return collections
125+
end
126+
127+
at(collections, 'collections', 'table')
128+
at(connections, 'connections', 'table')
129+
130+
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')
137+
138+
local index = indexes[c.source_collection][c.index_name]
139+
assert(index.unique ~= nil, 'index.unique must not be nil ' ..
140+
'during connections complementing')
141+
142+
local result_c = {}
143+
result_c.parts = determine_connection_parts(c.parts, index)
144+
result_c.type = determine_connection_type(result_c.parts, index)
145+
result_c.index_name = c.index_name
146+
result_c.destination_collection = c.destination_collection
147+
result_c.name = c.name
148+
149+
local collection_connections = collections[c.source_collection].
150+
connections
151+
if collection_connections == nil then
152+
collection_connections = {}
153+
end
154+
collection_connections[#collection_connections + 1] = result_c
155+
end
156+
157+
return collections
158+
end
159+
160+
--- The function complements
161+
function config_complement.complement_cfg(cfg)
162+
cfg.collections = complement_connections(cfg.collections, cfg.connections,
163+
cfg.indexes)
164+
return cfg
165+
end
166+
167+
return config_complement

0 commit comments

Comments
 (0)