validate.lua 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. return function(schema, tree)
  2. local context = {
  3. operationNames = {},
  4. hasAnonymousOperation = false,
  5. typeStack = {}
  6. }
  7. local visitors = {
  8. document = function(node)
  9. return node.definitions
  10. end,
  11. operation = function(node)
  12. local name = node.name and node.name.value
  13. if name then
  14. if context.operationNames[name] then
  15. error('Multiple operations exist named "' .. name .. '"')
  16. else
  17. context.operationNames[name] = true
  18. end
  19. else
  20. if context.hasAnonymousOperation or next(context.operationNames) then
  21. error('Cannot have more than one operation when using anonymous operations')
  22. end
  23. context.hasAnonymousOperation = true
  24. end
  25. return {node.selectionSet}
  26. end,
  27. selectionSet = function(node)
  28. return node.selections
  29. end,
  30. field = function(node)
  31. local currentType = context.typeStack[#context.typeStack].__type
  32. if currentType == 'Scalar' and node.selectionSet then
  33. error('Scalar values cannot have subselections')
  34. end
  35. local isCompositeType = currentType == 'Object' or currentType == 'Interface' or currentType == 'Union'
  36. if isCompositeType and not node.selectionSet then
  37. error('Composite types must have subselections')
  38. end
  39. if node.selectionSet then
  40. return {node.selectionSet}
  41. end
  42. end,
  43. inlineFragment = function(node)
  44. if node.selectionSet then
  45. return {node.selectionSet}
  46. end
  47. end
  48. }
  49. local root = schema.query
  50. local function visit(node)
  51. if node.kind and visitors[node.kind] then
  52. if node.kind == 'operation' then
  53. table.insert(context.typeStack, schema.query)
  54. elseif node.kind == 'field' then
  55. local parent = context.typeStack[#context.typeStack]
  56. if parent.fields[node.name.value] then
  57. table.insert(context.typeStack, parent.fields[node.name.value].kind)
  58. else
  59. error('Field "' .. node.name.value .. '" is not defined on type "' .. parent.name .. '"')
  60. end
  61. elseif node.kind == 'inlineFragment' then
  62. if node.typeCondition then
  63. local kind = schema:getType(node.typeCondition.name.value)
  64. if not kind then
  65. error('Inline fragment type condition refers to non-existent type')
  66. end
  67. if kind and kind.__type ~= 'Object' and kind.__type ~= 'Interface' and kind.__type ~= 'Union' then
  68. error('Inline fragment type condition was not an Object, Interface, or Union')
  69. end
  70. table.insert(context.typeStack, kind)
  71. end
  72. end
  73. local targets = visitors[node.kind](node)
  74. if targets then
  75. for _, target in ipairs(targets) do
  76. visit(target)
  77. end
  78. end
  79. end
  80. end
  81. visit(tree)
  82. end