rules.lua 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. local rules = {}
  2. function rules.uniqueOperationNames(node, context)
  3. local name = node.name and node.name.value
  4. if name then
  5. if context.operationNames[name] then
  6. error('Multiple operations exist named "' .. name .. '"')
  7. end
  8. context.operationNames[name] = true
  9. end
  10. end
  11. function rules.loneAnonymousOperation(node, context)
  12. local name = node.name and node.name.value
  13. if context.hasAnonymousOperation or (not name and next(context.operationNames)) then
  14. error('Cannot have more than one operation when using anonymous operations')
  15. end
  16. if not name then
  17. context.hasAnonymousOperation = true
  18. end
  19. end
  20. function rules.fieldsDefinedOnType(node, context)
  21. if context.currentField == false then
  22. local parent = context.objects[#context.objects - 1]
  23. error('Field "' .. node.name.value .. '" is not defined on type "' .. parent.name .. '"')
  24. end
  25. end
  26. function rules.argumentsDefinedOnType(node, context)
  27. if node.arguments then
  28. local parentField = context.objects[#context.objects - 1].fields[node.name.value]
  29. for _, argument in pairs(node.arguments) do
  30. local name = argument.name.value
  31. if not parentField.arguments[name] then
  32. error('Non-existent argument "' .. name .. '"')
  33. end
  34. end
  35. end
  36. end
  37. function rules.scalarFieldsAreLeaves(node, context)
  38. if context.currentField.__type == 'Scalar' and node.selectionSet then
  39. error('Scalar values cannot have subselections')
  40. end
  41. end
  42. function rules.compositeFieldsAreNotLeaves(node, context)
  43. local _type = context.currentField.__type
  44. local isCompositeType = _type == 'Object' or _type == 'Interface' or _type == 'Union'
  45. if isCompositeType and not node.selectionSet then
  46. error('Composite types must have subselections')
  47. end
  48. end
  49. function rules.inlineFragmentValidTypeCondition(node, context)
  50. if not node.typeCondition then return end
  51. local kind = context.objects[#context.objects]
  52. if kind == false then
  53. error('Inline fragment type condition refers to non-existent type')
  54. end
  55. if kind.__type ~= 'Object' and kind.__type ~= 'Interface' and kind.__type ~= 'Union' then
  56. error('Inline fragment type condition was not an Object, Interface, or Union')
  57. end
  58. end
  59. function rules.unambiguousSelections(node, context)
  60. local selectionMap = {}
  61. local function findConflict(entryA, entryB)
  62. -- Parent types can't overlap if they're different objects.
  63. -- Interface and union types may overlap.
  64. if entryA.parent ~= entryB.parent and entryA.__type == 'Object' and entryB.__type == 'Object' then
  65. return
  66. end
  67. -- Error if there are aliases that map two different fields to the same name.
  68. if entryA.field.name.value ~= entryB.field.name.value then
  69. return 'Type name mismatch'
  70. end
  71. -- Error if there are fields with the same name that have different return types.
  72. if entryA.definition and entryB.definition and entryA.definition ~= entryB.definition then
  73. return 'Return type mismatch'
  74. end
  75. -- Error if arguments are not identical for two fields with the same name.
  76. local argsA = entryA.field.arguments or {}
  77. local argsB = entryB.field.arguments or {}
  78. if #argsA ~= #argsB then
  79. return 'Argument mismatch'
  80. end
  81. local argMap = {}
  82. for i = 1, #argsA do
  83. argMap[argsA[i].name.value] = argsA[i].value
  84. end
  85. for i = 1, #argsB do
  86. local name = argsB[i].name.value
  87. if not argMap[name] then
  88. return 'Argument mismatch'
  89. elseif argMap[name].kind ~= argsB[i].value.kind then
  90. return 'Argument mismatch'
  91. elseif argMap[name].value ~= argsB[i].value.value then
  92. return 'Argument mismatch'
  93. end
  94. end
  95. end
  96. local function validateField(key, entry)
  97. if selectionMap[key] then
  98. for i = 1, #selectionMap[key] do
  99. local conflict = findConflict(selectionMap[key][i], entry)
  100. if conflict then
  101. error(conflict)
  102. end
  103. end
  104. table.insert(selectionMap[key], entry)
  105. else
  106. selectionMap[key] = { entry }
  107. end
  108. end
  109. -- Recursively make sure that there are no ambiguous selections with the same name.
  110. local function validateSelectionSet(selectionSet, parentType)
  111. for _, selection in ipairs(selectionSet.selections) do
  112. if selection.kind == 'field' then
  113. local key = selection.alias and selection.alias.name.value or selection.name.value
  114. local definition = parentType.fields[selection.name.value].kind
  115. local fieldEntry = {
  116. parent = parentType,
  117. field = selection,
  118. definition = definition
  119. }
  120. validateField(key, fieldEntry)
  121. elseif selection.kind == 'inlineFragment' then
  122. local parentType = parentType
  123. if selection.typeCondition then
  124. parentType = context.schema:getType(selection.typeCondition.name.value) or parentType
  125. end
  126. validateSelectionSet(selection.selectionSet, parentType)
  127. elseif selection.kind == 'fragmentSpread' then
  128. -- FIXME find fragment by name, get type condition to compute parentType
  129. validateSelectionSet(selection.selectionSet, parentType)
  130. end
  131. end
  132. end
  133. validateSelectionSet(node, context.objects[#context.objects])
  134. end
  135. function rules.uniqueArgumentNames(node, context)
  136. if node.arguments then
  137. local arguments = {}
  138. for _, argument in ipairs(node.arguments) do
  139. local name = argument.name.value
  140. if arguments[name] then
  141. error('Encountered multiple arguments named "' .. name .. '"')
  142. end
  143. arguments[name] = true
  144. end
  145. end
  146. end
  147. function rules.argumentsOfCorrectType(node, context)
  148. local function validateType(argumentType, valueNode)
  149. if argumentType.__type == 'NonNull' then
  150. return validateType(argumentType.ofType, valueNode)
  151. end
  152. if argumentType.__type == 'List' then
  153. if valueNode.kind ~= 'list' then
  154. error('Expected a list')
  155. end
  156. for i = 1, #valueNode.values do
  157. validateType(argumentType.ofType, valueNode.values[i])
  158. end
  159. end
  160. if argumentType.__type == 'InputObject' then
  161. if valueNode.kind ~= 'object' then
  162. error('Expected an object')
  163. end
  164. for _, field in ipairs(valueNode.values) do
  165. if not argumentType.fields[field.name] then
  166. error('Unknown input object field "' .. field.name .. '"')
  167. end
  168. validateType(argumentType.fields[field.name].kind, field.value)
  169. end
  170. end
  171. if argumentType.__type == 'Enum' then
  172. if valueNode.kind ~= 'enum' then
  173. error('Expected enum value, got ' .. valueNode.kind)
  174. end
  175. if not argumentType.values[valueNode.value] then
  176. error('Invalid enum value "' .. valueNode.value .. '"')
  177. end
  178. end
  179. if argumentType.__type == 'Scalar' then
  180. if argumentType.parseLiteral(valueNode) == nil then
  181. error('Could not coerce "' .. valueNode.value .. '" to "' .. argumentType.name .. '"')
  182. end
  183. end
  184. end
  185. if node.arguments then
  186. local parentField = context.objects[#context.objects - 1].fields[node.name.value]
  187. for _, argument in pairs(node.arguments) do
  188. local name = argument.name.value
  189. local argumentType = parentField.arguments[name]
  190. validateType(argumentType, argument.value)
  191. end
  192. end
  193. end
  194. return rules