Browse Source

Fragment rules; Bugfixes;

bjorn 9 years ago
parent
commit
9f0298389e
3 changed files with 64 additions and 3 deletions
  1. 47 0
      rules.lua
  2. 10 1
      schema.lua
  3. 7 2
      validate.lua

+ 47 - 0
rules.lua

@@ -125,8 +125,11 @@ function rules.unambiguousSelections(node, context)
   local function validateSelectionSet(selectionSet, parentType)
     for _, selection in ipairs(selectionSet.selections) do
       if selection.kind == 'field' then
+        if not parentType or not parentType.fields[selection.name.value] then return end
+
         local key = selection.alias and selection.alias.name.value or selection.name.value
         local definition = parentType.fields[selection.name.value].kind
+
         local fieldEntry = {
           parent = parentType,
           field = selection,
@@ -316,4 +319,48 @@ function rules.fragmentDefinitionHasNoCycles(node, context)
   detectCycles(node.selectionSet)
 end
 
+function rules.fragmentSpreadIsPossible(node, context)
+  local fragment = node.kind == 'inlineFragment' and node or context.fragmentMap[node.name.value]
+  local parentType = context.objects[#context.objects - 1]
+
+  local fragmentType
+  if node.kind == 'inlineFragment' then
+    fragmentType = node.typeCondition and context.schema:getType(node.typeCondition.name.value) or parentType
+  else
+    fragmentType = context.schema:getType(fragment.typeCondition.name.value)
+  end
+
+  -- Some types are not present in the schema.  Let other rules handle this.
+  if not parentType or not fragmentType then return end
+
+  local function getTypes(kind)
+    if kind.__type == 'Object' then
+      return { [kind] = kind }
+    elseif kind.__type == 'Interface' then
+      return context.schema:getImplementors(kind.name)
+    elseif kind.__type == 'Union' then
+      local types = {}
+      for i = 1, #kind.types do
+        types[kind.types[i]] = kind.types[i]
+      end
+      return types
+    end
+  end
+
+  local parentTypes = getTypes(parentType)
+  local fragmentTypes = getTypes(fragmentType)
+  local valid = false
+
+  for _, kind in pairs(parentTypes) do
+    if fragmentTypes[kind] then
+      valid = true
+      break
+    end
+  end
+
+  if not valid then
+    error('Fragment type condition is not possible for given type')
+  end
+end
+
 return rules

+ 10 - 1
schema.lua

@@ -10,6 +10,7 @@ function schema.create(config)
   end
 
   self.typeMap = {}
+  self.interfaceMap = {}
 
   local function generateTypeMap(node)
     if node.__type == 'NonNull' or node.__type == 'List' then
@@ -25,10 +26,12 @@ function schema.create(config)
     if node.__type == 'Object' and node.interfaces then
       for _, interface in ipairs(node.interfaces) do
         generateTypeMap(interface)
+        self.interfaceMap[interface.name] = self.interfaceMap[interface.name] or {}
+        self.interfaceMap[interface.name][node] = node
       end
     end
 
-    if node.__type == 'Object' or node.__type == 'Interface' or node.__type == 'Union' then
+    if node.__type == 'Object' or node.__type == 'Interface' or node.__type == 'InputObject' then
       for fieldName, field in pairs(node.fields) do
         if field.arguments then
           for _, argument in pairs(field.arguments) do
@@ -50,4 +53,10 @@ function schema:getType(name)
   return self.typeMap[name]
 end
 
+function schema:getImplementors(interface)
+  local kind = self:getType(interface)
+  local isInterface = kind and kind.__type == 'Interface'
+  return self.interfaceMap[interface] or (isInterface and {} or nil)
+end
+
 return schema

+ 7 - 2
validate.lua

@@ -97,15 +97,20 @@ local visitors = {
       end
     end,
 
-    rules = { rules.fragmentHasValidType }
+    rules = { rules.fragmentHasValidType, rules.fragmentSpreadIsPossible }
   },
 
   fragmentSpread = {
     enter = function(node, context)
       context.usedFragments[node.name.value] = true
+
+      local fragment = context.fragmentMap[node.name.value]
+      local fragmentType = context.schema:getType(fragment.typeCondition.name.value) or false
+
+      table.insert(context.objects, fragmentType)
     end,
 
-    rules = { rules.fragmentSpreadTargetDefined }
+    rules = { rules.fragmentSpreadTargetDefined, rules.fragmentSpreadIsPossible }
   },
 
   fragmentDefinition = {