Browse Source

More fragment validation rules;

bjorn 9 years ago
parent
commit
0f50131800
2 changed files with 46 additions and 4 deletions
  1. 42 2
      rules.lua
  2. 4 2
      validate.lua

+ 42 - 2
rules.lua

@@ -60,6 +60,7 @@ end
 
 function rules.unambiguousSelections(node, context)
   local selectionMap = {}
+  local seen = {}
 
   local function findConflict(entryA, entryB)
 
@@ -132,14 +133,22 @@ function rules.unambiguousSelections(node, context)
           definition = definition
         }
 
+        if seen[definition] then
+          return
+        end
+
+        seen[definition] = true
+
         validateField(key, fieldEntry)
       elseif selection.kind == 'inlineFragment' then
         local parentType = selection.typeCondition and context.schema:getType(selection.typeCondition.name.value) or parentType
         validateSelectionSet(selection.selectionSet, parentType)
       elseif selection.kind == 'fragmentSpread' then
         local fragmentDefinition = context.fragmentMap[selection.name.value]
-        local parentType = context.schema:getType(fragmentDefinition.typeCondition.name.value)
-        validateSelectionSet(fragmentDefinition.selectionSet, parentType)
+        if fragmentDefinition and fragmentDefinition.typeCondition then
+          local parentType = context.schema:getType(fragmentDefinition.typeCondition.name.value)
+          validateSelectionSet(fragmentDefinition.selectionSet, parentType)
+        end
       end
     end
   end
@@ -276,4 +285,35 @@ function rules.noUnusedFragments(node, context)
   end
 end
 
+function rules.fragmentSpreadTargetDefined(node, context)
+  if not context.fragmentMap[node.name.value] then
+    error('Fragment spread refers to non-existent fragment "' .. node.name.value .. '"')
+  end
+end
+
+function rules.fragmentDefinitionHasNoCycles(node, context)
+  local seen = { [node.name.value] = true }
+
+  local function detectCycles(selectionSet)
+    for _, selection in ipairs(selectionSet.selections) do
+      if selection.kind == 'inlineFragment' then
+        detectCycles(selection.selectionSet)
+      elseif selection.kind == 'fragmentSpread' then
+        if seen[selection.name.value] then
+          error('Fragment definition has cycles')
+        end
+
+        seen[selection.name.value] = true
+
+        local fragmentDefinition = context.fragmentMap[selection.name.value]
+        if fragmentDefinition and fragmentDefinition.typeCondition then
+          detectCycles(fragmentDefinition.selectionSet)
+        end
+      end
+    end
+  end
+
+  detectCycles(node.selectionSet)
+end
+
 return rules

+ 4 - 2
validate.lua

@@ -103,7 +103,9 @@ local visitors = {
   fragmentSpread = {
     enter = function(node, context)
       context.usedFragments[node.name.value] = true
-    end
+    end,
+
+    rules = { rules.fragmentSpreadTargetDefined }
   },
 
   fragmentDefinition = {
@@ -120,7 +122,7 @@ local visitors = {
       return { node.selectionSet }
     end,
 
-    rules = { rules.fragmentHasValidType }
+    rules = { rules.fragmentHasValidType, rules.fragmentDefinitionHasNoCycles }
   }
 }