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

Commit 1fc8acc

Browse files
committed
WIP: Preliminary patch for update/delete mutations
1 parent 920d2bb commit 1fc8acc

File tree

4 files changed

+153
-65
lines changed

4 files changed

+153
-65
lines changed

graphql/accessor_general.lua

+37-3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ if rex == nil then
1616
rex, is_pcre2 = utils.optional_require('rex_pcre'), false
1717
end
1818
local utils = require('graphql.utils')
19+
local avro_helpers = require('graphql.avro_helpers')
1920

2021
local check = utils.check
2122

@@ -1483,10 +1484,43 @@ function accessor_general.new(opts, funcs)
14831484
extra_args = function(self, collection_name)
14841485
local collection = self.collections[collection_name]
14851486
local schema_name = collection.schema_name
1486-
local schema = table.copy(self.schemas[schema_name])
1487-
schema.name = collection_name .. '_insert'
1487+
1488+
local schema_insert = table.copy(self.schemas[schema_name])
1489+
schema_insert.name = collection_name .. '_insert'
1490+
1491+
-- XXX: opts.multi
1492+
local schema_update = {
1493+
name = collection_name .. '_update',
1494+
type = 'record',
1495+
fields = {},
1496+
}
1497+
for _, field in ipairs(self.schemas[schema_name].fields) do
1498+
local field = table.copy(field)
1499+
field.type = avro_helpers.make_avro_type_nullable(
1500+
field.type)
1501+
table.insert(schema_update.fields, field)
1502+
end
1503+
1504+
-- XXX: opts.multi
1505+
local schema_delete = 'boolean'
1506+
14881507
return {
1489-
{name = 'insert', type = schema}
1508+
{name = 'insert', type = schema_insert},
1509+
{name = 'update', type = schema_update},
1510+
{name = 'delete', type = schema_delete},
1511+
}, {
1512+
insert = {
1513+
add_to_mutations_only = true,
1514+
add_to_top_fields_only = true,
1515+
},
1516+
update = {
1517+
add_to_mutations_only = true,
1518+
add_to_top_fields_only = false,
1519+
},
1520+
delete = {
1521+
add_to_mutations_only = true,
1522+
add_to_top_fields_only = false,
1523+
},
14901524
}
14911525
end,
14921526
}

graphql/avro_helpers.lua

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
--- The module us collection of helpers to simplify avro-schema related tasks.
2+
3+
local json = require('json')
4+
5+
local avro_helpers = {}
6+
7+
--- The function converts avro type to the corresponding nullable type in
8+
--- place and returns the result.
9+
---
10+
--- We make changes in place in case of table input (`avro`) because of
11+
--- performance reasons, but we returns the result because an input (`avro`)
12+
--- can be a string. Strings in Lua are immutable.
13+
---
14+
--- In the current tarantool/avro-schema implementation we simply add '*' to
15+
--- the end of a type name or, in case of union, add 'null' branch.
16+
---
17+
--- If the type is already nullable the function leaves it as is if
18+
--- `opts.raise_on_nullable` is false or omitted. If `opts.raise_on_nullable`
19+
--- is true the function will raise an error.
20+
---
21+
--- @tparam table avro avro schema node to be converted to nullable one
22+
---
23+
--- @tparam[opt] table opts the following options:
24+
---
25+
--- * `raise_on_nullable` (boolean) raise an error on nullable type
26+
---
27+
--- @result `result` (string or table) nullable avro type
28+
function avro_helpers.make_avro_type_nullable(avro, opts)
29+
assert(avro ~= nil, "avro must not be nil")
30+
local opts = opts or {}
31+
assert(type(opts) == 'table',
32+
'opts must be nil or a table, got ' .. type(opts))
33+
local raise_on_nullable = opts.raise_on_nullable or false
34+
assert(type(raise_on_nullable) == 'boolean',
35+
'opts.raise_on_nullable must be nil or a boolean, got ' ..
36+
type(raise_on_nullable))
37+
38+
local value_type = type(avro)
39+
40+
if value_type == "string" then
41+
local is_nullable = avro:endswith("*")
42+
if raise_on_nullable and is_nullable then
43+
error('expected non-null type, got the nullable one: ' ..
44+
json.encode(avro))
45+
end
46+
return is_nullable and avro or (avro .. '*')
47+
elseif value_type == 'table' and #avro > 0 then -- union
48+
local is_nullable = false
49+
for _, branch in ipairs(avro) do
50+
if branch == 'null' then
51+
is_nullable = true
52+
break
53+
end
54+
end
55+
if raise_on_nullable and is_nullable then
56+
error('expected non-null type, got the nullable one: ' ..
57+
json.encode(avro))
58+
end
59+
-- We add 'nil' branch to the end because here we don't know whether
60+
-- the following things matter for a caller:
61+
-- * a default value (it must have the type of the first branch);
62+
-- * {,un,x}flatten data layout.
63+
if not is_nullable then
64+
table.insert(avro, 'null')
65+
end
66+
return avro
67+
elseif value_type == 'table' and #avro == 0 then
68+
return avro_helpers.make_avro_type_nullable(avro.type, opts)
69+
end
70+
71+
error("avro should be a string or a table, got " .. value_type)
72+
end
73+
74+
return avro_helpers

graphql/query_to_avro.lua

+5-49
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
--- * The best way to use this module is to just call `avro_schema` method on
66
--- compiled query object.
77

8-
local json = require('json')
98
local path = "graphql.core"
109
local introspection = require(path .. '.introspection')
1110
local query_util = require(path .. '.query_util')
11+
local avro_helpers = require('graphql.avro_helpers')
1212

1313
-- module functions
1414
local query_to_avro = {}
@@ -25,6 +25,7 @@ local gql_scalar_to_avro_index = {
2525
Float = "double",
2626
Boolean = "boolean"
2727
}
28+
2829
local function gql_scalar_to_avro(fieldType)
2930
assert(fieldType.__type == "Scalar", "GraphQL scalar field expected")
3031
assert(fieldType.name ~= "Map", "Map type is not supported")
@@ -33,53 +34,6 @@ local function gql_scalar_to_avro(fieldType)
3334
return result
3435
end
3536

36-
--- The function converts avro type to the corresponding nullable type in
37-
--- place and returns the result.
38-
---
39-
--- We make changes in place in case of table input (`avro`) because of
40-
--- performance reasons, but we returns the result because an input (`avro`)
41-
--- can be a string. Strings in Lua are immutable.
42-
---
43-
--- In the current tarantool/avro-schema implementation we simply add '*' to
44-
--- the end of a type name.
45-
---
46-
--- If the type is already nullable the function leaves it as if
47-
--- `opts.raise_on_nullable` is false or omitted. If `opts.raise_on_nullable`
48-
--- is true the function will raise an error.
49-
---
50-
--- @tparam table avro avro schema node to be converted to nullable one
51-
---
52-
--- @tparam[opt] table opts the following options:
53-
---
54-
--- * `raise_on_nullable` (boolean) raise an error on nullable type
55-
---
56-
--- @result `result` (string or table) nullable avro type
57-
local function make_avro_type_nullable(avro, opts)
58-
assert(avro ~= nil, "avro must not be nil")
59-
local opts = opts or {}
60-
assert(type(opts) == 'table',
61-
'opts must be nil or a table, got ' .. type(opts))
62-
local raise_on_nullable = opts.raise_on_nullable or false
63-
assert(type(raise_on_nullable) == 'boolean',
64-
'opts.raise_on_nullable must be nil or a boolean, got ' ..
65-
type(raise_on_nullable))
66-
67-
local value_type = type(avro)
68-
69-
if value_type == "string" then
70-
local is_nullable = avro:endswith("*")
71-
if raise_on_nullable and is_nullable then
72-
error('expected non-null type, got the nullable one: ' ..
73-
json.encode(avro))
74-
end
75-
return is_nullable and avro or (avro .. '*')
76-
elseif value_type == "table" then
77-
return make_avro_type_nullable(avro.type, opts)
78-
end
79-
80-
error("avro should be a string or a table, got " .. value_type)
81-
end
82-
8337
--- Convert GraphQL type to avro-schema with selecting fields.
8438
---
8539
--- @tparam table fieldType GraphQL type
@@ -123,7 +77,9 @@ local function gql_type_to_avro(fieldType, subSelections, context)
12377
end
12478

12579
if not isNonNull then
126-
result = make_avro_type_nullable(result, {raise_on_nullable = true})
80+
result = avro_helpers.make_avro_type_nullable(result, {
81+
raise_on_nullable = true,
82+
})
12783
end
12884
return result
12985
end

graphql/tarantool_graphql.lua

+37-13
Original file line numberDiff line numberDiff line change
@@ -1248,16 +1248,26 @@ local function create_root_collection(state)
12481248
root_types[what] = nullable(gql_type(state, root_schema,
12491249
root_collection, nil))
12501250

1251-
end
1252-
1253-
-- add extra arguments to top-level fields (collections)
1254-
for collection_name, field in pairs(root_types['Mutation'].fields) do
1255-
-- Prevent exposing an argument into the query schema subtree (it is
1256-
-- needed because we use a booking table for arguments).
1257-
field.arguments = table.copy(field.arguments)
1258-
1259-
for name, arg in pairs(state.extra_arguments[collection_name]) do
1260-
field.arguments[name] = arg
1251+
-- add extra arguments to top-level fields (collections)
1252+
for collection_name, field in pairs(root_types[what].fields) do
1253+
-- Prevent exposing an argument inserted, say, into the mutation schema
1254+
-- subtree to the query subtree (it is needed because we use a booking
1255+
-- table for arguments).
1256+
field.arguments = table.copy(field.arguments)
1257+
1258+
local extra_args = state.extra_arguments[collection_name]
1259+
local extra_args_meta = state.extra_arguments_meta[collection_name]
1260+
1261+
for arg_name, arg in pairs(extra_args) do
1262+
local meta = extra_args_meta[arg_name]
1263+
check(meta, 'meta', 'table')
1264+
-- note: we handle add_to_top_fields_only == false case in
1265+
-- add_connection_arguments
1266+
if meta.add_to_top_fields_only and what == 'Mutation' or
1267+
not meta.add_to_mutations_only then
1268+
field.arguments[arg_name] = arg
1269+
end
1270+
end
12611271
end
12621272
end
12631273

@@ -1343,6 +1353,8 @@ local function add_connection_arguments(state)
13431353
}
13441354
end
13451355
end)
1356+
1357+
-- XXX: handle add_to_top_fields_only == false case of extra_arguments
13461358
end
13471359

13481360
local function parse_cfg(cfg)
@@ -1362,6 +1374,7 @@ local function parse_cfg(cfg)
13621374
-- argument. We capture extra_arguments[collection_name] into the resolve
13631375
-- function and sure it exists and will not be changed.
13641376
state.extra_arguments = utils.gen_booking_table({})
1377+
state.extra_arguments_meta = {}
13651378

13661379
-- map from avro-schema names to graphql types
13671380
state.definitions = {}
@@ -1415,12 +1428,16 @@ local function parse_cfg(cfg)
14151428
{skip_compound = true})
14161429
local list_args = convert_record_fields_to_args(
14171430
accessor:list_args(collection_name))
1418-
local extra_args = convert_record_fields_to_args(
1419-
accessor:extra_args(collection_name), {dont_skip = true})
1431+
local extra_args_avro, extra_args_meta = accessor:extra_args(
1432+
collection_name)
1433+
check(extra_args_meta, 'extra_args_meta', 'table')
1434+
local extra_args = convert_record_fields_to_args(extra_args_avro,
1435+
{dont_skip = true})
14201436

14211437
state.object_arguments[collection_name] = object_args
14221438
state.list_arguments[collection_name] = list_args
14231439
state.extra_arguments[collection_name] = extra_args
1440+
state.extra_arguments_meta[collection_name] = extra_args_meta
14241441
end
14251442

14261443
add_connection_arguments(state)
@@ -1683,7 +1700,14 @@ end
16831700
--- }
16841701
--- end,
16851702
--- extra_args = function(self, collection_name)
1686-
--- return ...
1703+
--- ...
1704+
--- local args_meta = {
1705+
--- arg_name = {
1706+
--- add_to_mutations_only = true / false,
1707+
--- add_to_top_fields_only = true / false,
1708+
--- }
1709+
--- }
1710+
--- return schemas_list, args_meta
16871711
--- end
16881712
--- }
16891713
--- }),

0 commit comments

Comments
 (0)