// expandFields resolves fragments to compile the list of fields that must // be resolved within a given selection set. func expandFields(ss ast.SelectionSet, parent *ResponseNode, ctx *context) { def := parent.resultType for _, s := range ss { switch sel := s.(type) { case *ast.FragmentSpread: if !shouldIncludeNode(&sel.Directives, ctx) { continue } // lookup fragment frag, ok := ctx.Fragments[sel.Name] if !ok { ctx.addErrorf("No fragment named '%s' found", sel.Name) } expandFields(frag.SelectionSet, parent, ctx) case *ast.FragmentDefinition: if !shouldIncludeNode(&sel.Directives, ctx) { continue } expandFields(sel.SelectionSet, parent, ctx) // Calls to expandFields eventually reach here once all // fragments have been resolved. case *ast.Field: if !shouldIncludeNode(&sel.Directives, ctx) { continue } name := sel.Name field, ok := def.Field(name) if !ok { ctx.addErrorf("Type has no field named '%s'", name) continue } // Register field on parent response node parent.Fields = append(parent.Fields, name) // Determine if field is a (valid) leaf if !ast.IsAbstractType(field.Definition) { if len(sel.SelectionSet) != 0 { ctx.addErrorf("Scalar type has sub-fields in query") } continue } else if len(sel.SelectionSet) == 0 { ctx.addErrorf("Abstract type has no sub-fields in query") continue } // If field is not a leaf, create a ResponseNode for the // given field and execute the field's handler. node := NewResponseNode(parent, field) parent.wg.Add(1) if ctx.serialExecution { execute(sel, node, ctx) } else { go execute(sel, node, ctx) } default: panic("Unexpected selection type") } } }
func (r *ResponseNode) marshalJSON(buf *bytes.Buffer) error { // Handle null ResponseNode if r.null == true { if !r.isNullable { panic("Response node for non-nullable type has been set to null") } log.Println("null") _, err := buf.Write([]byte("null")) if err != nil { return err } return nil } if err := buf.WriteByte(byte('{')); err != nil { return err } for i, fieldName := range r.Fields { field, ok := r.resultType.Field(fieldName) if !ok { panic("MarshalJSON called on node with invalid field") } if i != 0 { if err := buf.WriteByte(byte(',')); err != nil { return err } } if err := buf.WriteByte(byte('"')); err != nil { return err } if _, err := buf.WriteString(fieldName); err != nil { return err } if _, err := buf.Write([]byte{'"', ':'}); err != nil { return err } // If the field is a scalar, look it up in the result map and // write it to the buffer. if !ast.IsAbstractType(field.Definition) { result, ok := r.resultMap.Get(fieldName) if !ok { panic("No field set") } json, err := json.Marshal(result) if err != nil { return err } if _, err := buf.Write(json); err != nil { return err } continue } // Otherwise, the field is abstract, so we look in the list of // children for the ResponseNode which resolves it. var node *ResponseNode for _, child := range r.children { if child.name == field.Name { node = child } } if _, ok := field.Type.(*ast.ListType); ok { if err := marshalList(node, buf); err != nil { return err } } else { if err := node.marshalJSON(buf); err != nil { return err } } } if err := buf.WriteByte(byte('}')); err != nil { return err } return nil }