Skip to content

Support custom directives #26

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions graphql/execute.lua
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,36 @@ local function getFieldEntry(objectType, object, fields, context)

arguments = setmetatable(arguments, {__index=positions})

local directiveMap = {}
for _, directive in ipairs(firstField.directives or {}) do
directiveMap[directive.name.value] = directive
end

local directives = {}

if next(directiveMap) ~= nil then
util.map_name(context.schema.directives or {}, function(directive, directive_name)
local supplied_directive = directiveMap[directive_name]
if supplied_directive == nil then
return nil
end

local directiveArgumentMap = {}
for _, argument in ipairs(supplied_directive.arguments or {}) do
directiveArgumentMap[argument.name.value] = argument
end

directives[directive_name] = util.map(directive.arguments or {}, function(argument, name)
local supplied = directiveArgumentMap[name] and directiveArgumentMap[name].value
if argument.kind then argument = argument.kind end
return util.coerceValue(supplied, argument, context.variables, {
strict_non_null = true,
defaultValues = defaultValues,
})
end)
end)
end

local info = {
context = context,
fieldName = fieldName,
Expand All @@ -298,6 +328,7 @@ local function getFieldEntry(objectType, object, fields, context)
operation = context.operation,
variableValues = context.variables,
defaultValues = context.defaultValues,
directives = directives,
}

local resolvedObject, err = (fieldType.resolve or defaultResolver)(object, arguments, info)
Expand Down
79 changes: 79 additions & 0 deletions graphql/introspection.lua
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ __Directive = types.object({
if directive.onFragmentDefinition then table.insert(res, 'FRAGMENT_DEFINITION') end
if directive.onFragmentSpread then table.insert(res, 'FRAGMENT_SPREAD') end
if directive.onInlineFragment then table.insert(res, 'INLINE_FRAGMENT') end
if directive.onVariableDefinition then table.insert(res, 'VARIABLE_DEFINITION') end
if directive.onSchema then table.insert(res, 'SCHEMA') end
if directive.onScalar then table.insert(res, 'SCALAR') end
if directive.onObject then table.insert(res, 'OBJECT') end
if directive.onFieldDefinition then table.insert(res, 'FIELD_DEFINITION') end
if directive.onArgumentDefinition then table.insert(res, 'ARGUMENT_DEFINITION') end
if directive.onInterface then table.insert(res, 'INTERFACE') end
if directive.onUnion then table.insert(res, 'UNION') end
if directive.onEnum then table.insert(res, 'ENUM') end
if directive.onEnumValue then table.insert(res, 'ENUM_VALUE') end
if directive.onInputObject then table.insert(res, 'INPUT_OBJECT') end
if directive.onInputFieldDefinition then table.insert(res, 'INPUT_FIELD_DEFINITION') end

return res
end,
Expand All @@ -118,6 +130,13 @@ __Directive = types.object({
kind = types.nonNull(types.list(types.nonNull(__InputValue))),
resolve = resolveArgs,
},

isRepeatable = {
kind = types.nonNull(types.boolean),
resolve = function(directive)
return directive.isRepeatable == true
end,
},
}
end,
})
Expand Down Expand Up @@ -160,6 +179,66 @@ __DirectiveLocation = types.enum({
value = 'INLINE_FRAGMENT',
description = 'Location adjacent to an inline fragment.',
},

VARIABLE_DEFINITION = {
value = 'VARIABLE_DEFINITION',
description = 'Location adjacent to a variable definition.',
},

SCHEMA = {
value = 'SCHEMA',
description = 'Location adjacent to schema.',
},

SCALAR = {
value = 'SCALAR',
description = 'Location adjacent to a scalar.',
},

OBJECT = {
value = 'OBJECT',
description = 'Location adjacent to an object.',
},

FIELD_DEFINITION = {
value = 'FIELD_DEFINITION',
description = 'Location adjacent to a field definition.',
},

ARGUMENT_DEFINITION = {
value = 'ARGUMENT_DEFINITION',
description = 'Location adjacent to an argument definition.',
},

INTERFACE = {
value = 'INTERFACE',
description = 'Location adjacent to an interface.',
},

UNION = {
value = 'UNION',
description = 'Location adjacent to an union.',
},

ENUM = {
value = 'ENUM',
description = 'Location adjacent to an enum.',
},

ENUM_VALUE = {
value = 'ENUM_VALUE',
description = 'Location adjacent to an enum value.',
},

INPUT_OBJECT = {
value = 'INPUT_OBJECT',
description = 'Location adjacent to an input object.',
},

INPUT_FIELD_DEFINITION = {
value = 'INPUT_FIELD_DEFINITION',
description = 'Location adjacent to an input field definition.',
},
},
})

Expand Down
22 changes: 22 additions & 0 deletions graphql/schema.lua
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,28 @@ end
function schema:generateDirectiveMap()
for _, directive in ipairs(self.directives) do
self.directiveMap[directive.name] = directive
if directive.arguments ~= nil then
for name, argument in pairs(directive.arguments) do

-- BEGIN_HACK: resolve type names to real types
if type(argument) == 'string' then
argument = types.resolve(argument, self.name)
directive.arguments[name] = argument
end

if type(argument.kind) == 'string' then
argument.kind = types.resolve(argument.kind, self.name)
end
-- END_HACK: resolve type names to real types

local argumentType = argument.__type and argument or argument.kind
if argumentType == nil then
error('Must supply type for argument "' .. name .. '" on "' .. directive.name .. '"')
end
argumentType.defaultValue = argument.defaultValue
self:generateTypeMap(argumentType)
end
end
end
end

Expand Down
13 changes: 13 additions & 0 deletions graphql/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,19 @@ function types.directive(config)
onFragmentDefinition = config.onFragmentDefinition,
onFragmentSpread = config.onFragmentSpread,
onInlineFragment = config.onInlineFragment,
onVariableDefinition = config.onVariableDefinition,
onSchema = config.onSchema,
onScalar = config.onScalar,
onObject = config.onObject,
onFieldDefinition = config.onFieldDefinition,
onArgumentDefinition = config.onArgumentDefinition,
onInterface = config.onInterface,
onUnion = config.onUnion,
onEnum = config.onEnum,
onEnumValue = config.onEnumValue,
onInputObject = config.onInputObject,
onInputFieldDefinition = config.onInputFieldDefinition,
isRepeatable = config.isRepeatable or false,
}

return instance
Expand Down
11 changes: 11 additions & 0 deletions graphql/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ local function map(t, fn)
return res
end

local function map_name(t, fn)
local res = {}
for _, v in pairs(t or {}) do
if v.name then
res[v.name] = fn(v, v.name)
end
end
return res
end

local function find(t, fn)
for k, v in pairs(t) do
if fn(v, k) then return v end
Expand Down Expand Up @@ -270,6 +280,7 @@ end

return {
map = map,
map_name = map_name,
find = find,
filter = filter,
values = values,
Expand Down
Loading