Skip to content

Commit 17b642d

Browse files
no1semanTotktonada
authored andcommitted
Propagate defaults to operation callback
Before this PR there is no fast and convenient way to get operation and directives defaults set when creating the schema. Now it's possible, operation and directives default can be easily found is in the #3 argument of callback - map "info". The keys are: defaultValues - for operation defaults directivesDefaultValues - for directives defaults
1 parent a5476e8 commit 17b642d

File tree

3 files changed

+294
-2
lines changed

3 files changed

+294
-2
lines changed

graphql/execute.lua

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,7 @@ local function getFieldEntry(objectType, object, fields, context)
335335
variableValues = context.variables,
336336
defaultValues = context.defaultValues,
337337
directives = directives,
338+
directivesDefaultValues = context.schema.directivesDefaultValues,
338339
}
339340

340341
local resolvedObject, err = (fieldType.resolve or defaultResolver)(object, arguments, info)
@@ -352,9 +353,27 @@ evaluateSelections = function(objectType, object, selections, context)
352353
local result = {}
353354
local err
354355
local fields = collectFields(objectType, selections, {}, {}, context)
356+
local defaultValues
357+
358+
if context.defaultValues == nil then
359+
if context.schema.defaultValues ~= nil and type(context.schema.defaultValues) == 'table' then
360+
local operationDefaults = context.schema.defaultValues[context.operation.operation]
361+
if operationDefaults ~= nil and type(operationDefaults) == 'table' then
362+
defaultValues = context.schema.defaultValues[context.operation.operation]
363+
end
364+
end
365+
else
366+
defaultValues = context.defaultValues
367+
end
368+
355369
for _, field in ipairs(fields) do
356370
assert(result[field.name] == nil,
357371
'two selections into the one field: ' .. field.name)
372+
373+
if defaultValues ~= nil then
374+
context.defaultValues = defaultValues[field.name]
375+
end
376+
358377
result[field.name], err = getFieldEntry(objectType, object, {field.selection},
359378
context)
360379
if err ~= nil then

graphql/schema.lua

Lines changed: 94 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@ end
88
local schema = {}
99
schema.__index = schema
1010

11-
function schema.create(config, name)
11+
function schema.create(config, name, opts)
1212
assert(type(config.query) == 'table', 'must provide query object')
1313
assert(not config.mutation or type(config.mutation) == 'table', 'mutation must be a table if provided')
1414

15+
opts = opts or {}
1516
local self = setmetatable({}, schema)
1617

1718
for k, v in pairs(config) do
@@ -34,9 +35,101 @@ function schema.create(config, name)
3435
self:generateTypeMap(introspection.__Schema)
3536
self:generateDirectiveMap()
3637

38+
if opts.defaultValues == true then
39+
self.defaultValues = {}
40+
self.defaultValues.mutation = self:extractDefaults(self.mutation)
41+
self.defaultValues.query = self:extractDefaults(self.query)
42+
end
43+
44+
if opts.directivesDefaultValues == true then
45+
self.directivesDefaultValues = {}
46+
47+
for directiveName, directive in pairs(self.directiveMap or {}) do
48+
self.directivesDefaultValues[directiveName] = self:extractDefaults(directive)
49+
end
50+
end
51+
3752
return self
3853
end
3954

55+
function schema:extractDefaults(node)
56+
if not node then return end
57+
58+
local defaultValues
59+
local nodeType = node.__type ~= nil and node or node.kind
60+
61+
if nodeType.__type == 'NonNull' then
62+
return self:extractDefaults(nodeType.ofType)
63+
end
64+
65+
if nodeType.__type == 'Enum' then
66+
return node.defaultValue
67+
end
68+
69+
if nodeType.__type == 'Scalar' then
70+
return node.defaultValue
71+
end
72+
73+
node.fields = type(node.fields) == 'function' and node.fields() or node.fields
74+
75+
if nodeType.__type == 'Object' or nodeType.__type == 'InputObject' then
76+
for fieldName, field in pairs(nodeType.fields or {}) do
77+
local fieldDefaultValue = self:extractDefaults(field)
78+
if fieldDefaultValue ~= nil then
79+
defaultValues = defaultValues or {}
80+
defaultValues[fieldName] = fieldDefaultValue
81+
end
82+
83+
for argumentName, argument in pairs(field.arguments or {}) do
84+
-- BEGIN_HACK: resolve type names to real types
85+
if type(argument) == 'string' then
86+
argument = types.resolve(argument, self.name)
87+
field.arguments[argumentName] = argument
88+
end
89+
90+
if type(argument.kind) == 'string' then
91+
argument.kind = types.resolve(argument.kind, self.name)
92+
end
93+
-- END_HACK: resolve type names to real types
94+
95+
local argumentDefaultValue = self:extractDefaults(argument)
96+
if argumentDefaultValue ~= nil then
97+
defaultValues = defaultValues or {}
98+
defaultValues[fieldName] = defaultValues[fieldName] or {}
99+
defaultValues[fieldName][argumentName] = argumentDefaultValue
100+
end
101+
end
102+
end
103+
return defaultValues
104+
end
105+
106+
if nodeType.__type =='Directive' then
107+
for argumentName, argument in pairs(nodeType.arguments or {}) do
108+
-- BEGIN_HACK: resolve type names to real types
109+
if type(argument) == 'string' then
110+
argument = types.resolve(argument, self.name)
111+
nodeType.arguments[argumentName] = argument
112+
end
113+
114+
if type(argument.kind) == 'string' then
115+
argument.kind = types.resolve(argument.kind, self.name)
116+
end
117+
-- END_HACK: resolve type names to real types
118+
119+
local argumentDefaultValue = self:extractDefaults(argument)
120+
if argumentDefaultValue ~= nil then
121+
defaultValues = defaultValues or {}
122+
defaultValues[argumentName] = argumentDefaultValue
123+
end
124+
end
125+
return defaultValues
126+
end
127+
128+
if nodeType.__type == 'List' then
129+
return self:extractDefaults(nodeType.ofType)
130+
end
131+
end
132+
40133
function schema:generateTypeMap(node)
41134
if not node or (self.typeMap[node.name] and self.typeMap[node.name] == node) then return end
42135

test/integration/graphql_test.lua

Lines changed: 181 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ local function check_request(query, query_schema, mutation_schema, directives, o
2525
directives = directives,
2626
}
2727

28-
local compiled_schema = schema.create(root, test_schema_name)
28+
local compiled_schema = schema.create(root, test_schema_name, opts)
2929

3030
local parsed = parse.parse(query)
3131

@@ -1871,3 +1871,183 @@ function g.test_mutation_and_directive_arguments_default_values()
18711871
-- directive default values used on the same argument type
18721872
t.assert_equals(compiled_schema.typeMap['Int'].defaultValue, nil)
18731873
end
1874+
1875+
g.test_propagate_defaults_to_callback = function()
1876+
local query = '{test_mutation}'
1877+
1878+
local function callback(_, _, info)
1879+
return json.encode({
1880+
defaultValues = info.defaultValues,
1881+
directivesDefaultValues = info.directivesDefaultValues,
1882+
})
1883+
end
1884+
1885+
local input_object = types.inputObject({
1886+
name = 'test_input_object',
1887+
fields = {
1888+
nested_int_arg = {
1889+
kind = types.int,
1890+
defaultValue = 2,
1891+
},
1892+
nested_string_arg = {
1893+
kind = types.string,
1894+
defaultValue = 'default nested value',
1895+
},
1896+
nested_boolean_arg = {
1897+
kind = types.boolean,
1898+
defaultValue = true,
1899+
},
1900+
nested_float_arg = {
1901+
kind = types.float,
1902+
defaultValue = 1.1,
1903+
},
1904+
nested_long_arg = {
1905+
kind = types.long,
1906+
defaultValue = 2^50,
1907+
},
1908+
nested_list_scalar_arg = {
1909+
kind = types.list(types.string),
1910+
-- defaultValue seems illogical
1911+
}
1912+
},
1913+
kind = types.string,
1914+
})
1915+
1916+
local mutation_schema = {
1917+
['test_mutation'] = {
1918+
kind = types.string.nonNull,
1919+
arguments = {
1920+
int_arg = {
1921+
kind = types.int,
1922+
defaultValue = 1,
1923+
},
1924+
string_arg = {
1925+
kind = types.string,
1926+
defaultValue = 'string_arg'
1927+
},
1928+
boolean_arg = {
1929+
kind = types.boolean,
1930+
defaultValue = false,
1931+
},
1932+
float_arg = {
1933+
kind = types.float,
1934+
defaultValue = 1.1,
1935+
},
1936+
long_arg = {
1937+
kind = types.long,
1938+
defaultValue = 2^50,
1939+
},
1940+
object_arg = {
1941+
kind = input_object,
1942+
-- defaultValue seems illogical
1943+
},
1944+
list_scalar_arg = {
1945+
kind = types.list(types.string),
1946+
-- defaultValue seems illogical
1947+
}
1948+
},
1949+
resolve = callback,
1950+
}
1951+
}
1952+
1953+
local directives = {
1954+
types.directive({
1955+
schema = schema,
1956+
name = 'timeout',
1957+
description = 'Request execute timeout',
1958+
arguments = {
1959+
int_arg = {
1960+
kind = types.int,
1961+
defaultValue = 1,
1962+
},
1963+
string_arg = {
1964+
kind = types.string,
1965+
defaultValue = 'string_arg'
1966+
},
1967+
boolean_arg = {
1968+
kind = types.boolean,
1969+
defaultValue = false,
1970+
},
1971+
float_arg = {
1972+
kind = types.float,
1973+
defaultValue = 1.1,
1974+
},
1975+
long_arg = {
1976+
kind = types.long,
1977+
defaultValue = 2^50,
1978+
},
1979+
object = input_object
1980+
},
1981+
onField = true,
1982+
})
1983+
}
1984+
1985+
local result = {
1986+
defaultValues = {
1987+
boolean_arg = false,
1988+
int_arg = 1,
1989+
float_arg = 1.1,
1990+
long_arg = 2^50,
1991+
object_arg = {
1992+
nested_boolean_arg = true,
1993+
nested_float_arg = 1.1,
1994+
nested_int_arg = 2,
1995+
nested_long_arg = 2^50,
1996+
nested_string_arg = "default nested value",
1997+
},
1998+
string_arg = "string_arg",
1999+
},
2000+
directivesDefaultValues = {
2001+
timeout = {
2002+
boolean_arg = false,
2003+
float_arg = 1.1,
2004+
int_arg = 1,
2005+
long_arg = 2^50,
2006+
object = {
2007+
nested_boolean_arg = true,
2008+
nested_float_arg = 1.1,
2009+
nested_int_arg = 2,
2010+
nested_long_arg = 2^50,
2011+
nested_string_arg = "default nested value",
2012+
},
2013+
string_arg = "string_arg",
2014+
},
2015+
},
2016+
}
2017+
2018+
local data, errors = check_request(
2019+
query,
2020+
mutation_schema,
2021+
nil,
2022+
directives,
2023+
{ defaultValues = true, directivesDefaultValues = true, }
2024+
)
2025+
2026+
t.assert_equals(errors, nil)
2027+
t.assert_items_equals(json.decode(data.test_mutation), result)
2028+
2029+
query = '{prefix{test_mutation}}'
2030+
2031+
local mutation_schema_with_prefix = {
2032+
['prefix'] = {
2033+
kind = types.object({
2034+
name = 'prefix',
2035+
fields = mutation_schema,
2036+
}),
2037+
arguments = {},
2038+
resolve = function()
2039+
return {}
2040+
end,
2041+
}
2042+
}
2043+
2044+
data, errors = check_request(
2045+
query,
2046+
mutation_schema_with_prefix,
2047+
nil,
2048+
directives,
2049+
{ defaultValues = true, directivesDefaultValues = true, }
2050+
)
2051+
t.assert_equals(errors, nil)
2052+
t.assert_items_equals(json.decode(data.prefix.test_mutation), result)
2053+
end

0 commit comments

Comments
 (0)