validate.lua 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. local rules = require 'rules'
  2. local visitors = {
  3. document = {
  4. enter = function(node, context)
  5. for _, definition in ipairs(node.definitions) do
  6. if definition.kind == 'fragmentDefinition' then
  7. context.fragmentMap[definition.name.value] = definition
  8. end
  9. end
  10. end,
  11. children = function(node, context)
  12. return node.definitions
  13. end,
  14. rules = { rules.uniqueFragmentNames, exit = { rules.noUnusedFragments } }
  15. },
  16. operation = {
  17. enter = function(node, context)
  18. table.insert(context.objects, context.schema.query)
  19. end,
  20. exit = function(node, context)
  21. table.remove(context.objects)
  22. end,
  23. children = function(node)
  24. return { node.selectionSet }
  25. end,
  26. rules = {
  27. rules.uniqueOperationNames,
  28. rules.loneAnonymousOperation
  29. }
  30. },
  31. selectionSet = {
  32. children = function(node)
  33. return node.selections
  34. end,
  35. rules = { rules.unambiguousSelections }
  36. },
  37. field = {
  38. enter = function(node, context)
  39. local parentField = context.objects[#context.objects].fields[node.name.value]
  40. -- false is a special value indicating that the field was not present in the type definition.
  41. context.currentField = parentField and parentField.kind or false
  42. table.insert(context.objects, context.currentField)
  43. end,
  44. exit = function(node, context)
  45. table.remove(context.objects)
  46. context.currentField = nil
  47. end,
  48. children = function(node)
  49. if node.selectionSet then
  50. return {node.selectionSet}
  51. end
  52. end,
  53. rules = {
  54. rules.fieldsDefinedOnType,
  55. rules.argumentsDefinedOnType,
  56. rules.scalarFieldsAreLeaves,
  57. rules.compositeFieldsAreNotLeaves,
  58. rules.uniqueArgumentNames,
  59. rules.argumentsOfCorrectType,
  60. rules.requiredArgumentsPresent
  61. }
  62. },
  63. inlineFragment = {
  64. enter = function(node, context)
  65. local kind = false
  66. if node.typeCondition then
  67. kind = context.schema:getType(node.typeCondition.name.value) or false
  68. end
  69. table.insert(context.objects, kind)
  70. end,
  71. exit = function(node, context)
  72. table.remove(context.objects)
  73. end,
  74. children = function(node, context)
  75. if node.selectionSet then
  76. return {node.selectionSet}
  77. end
  78. end,
  79. rules = { rules.fragmentHasValidType }
  80. },
  81. fragmentSpread = {
  82. enter = function(node, context)
  83. context.usedFragments[node.name.value] = true
  84. end
  85. },
  86. fragmentDefinition = {
  87. enter = function(node, context)
  88. kind = context.schema:getType(node.typeCondition.name.value) or false
  89. table.insert(context.objects, kind)
  90. end,
  91. exit = function(node, context)
  92. table.remove(context.objects)
  93. end,
  94. children = function(node)
  95. return { node.selectionSet }
  96. end,
  97. rules = { rules.fragmentHasValidType }
  98. }
  99. }
  100. return function(schema, tree)
  101. local context = {
  102. schema = schema,
  103. fragmentMap = {},
  104. operationNames = {},
  105. hasAnonymousOperation = false,
  106. usedFragments = {},
  107. objects = {}
  108. }
  109. local function visit(node)
  110. local visitor = node.kind and visitors[node.kind]
  111. if not visitor then return end
  112. if visitor.enter then
  113. visitor.enter(node, context)
  114. end
  115. if visitor.rules then
  116. for i = 1, #visitor.rules do
  117. visitor.rules[i](node, context)
  118. end
  119. end
  120. if visitor.children then
  121. local children = visitor.children(node)
  122. if children then
  123. for _, child in ipairs(children) do
  124. visit(child)
  125. end
  126. end
  127. end
  128. if visitor.rules and visitor.rules.exit then
  129. for i = 1, #visitor.rules.exit do
  130. visitor.rules.exit[i](node, context)
  131. end
  132. end
  133. if visitor.exit then
  134. visitor.exit(node, context)
  135. end
  136. end
  137. return visit(tree)
  138. end