Example #1
0
// 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")
		}
	}
}
Example #2
0
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
}