| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721 | /* * EliasDB * * Copyright 2016 Matthias Ladkau. All rights reserved. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */package interpreterimport (	"fmt"	"regexp"	"strconv"	"strings"	"devt.de/krotik/common/lang/graphql/parser"	"devt.de/krotik/common/stringutil"	"devt.de/krotik/eliasdb/graph"	"devt.de/krotik/eliasdb/graph/data")// SelectionSet Runtime// ====================/*Runtime for SelectionSets.*/type selectionSetRuntime struct {	*invalidRuntime	rtp  *GraphQLRuntimeProvider	node *parser.ASTNode}/*selectionSetRuntimeInst returns a new runtime component instance.*/func selectionSetRuntimeInst(rtp *GraphQLRuntimeProvider, node *parser.ASTNode) parser.Runtime {	return &selectionSetRuntime{&invalidRuntime{rtp, node}, rtp, node}}/*Eval evaluate this runtime component.*/func (rt *selectionSetRuntime) Eval() (map[string]interface{}, error) {	var err error	// Build result data	res := make(map[string]interface{})	for _, c := range rt.node.Children {		// Lookup nodes		if c.Name == parser.NodeField {			field := c.Runtime.(*fieldRuntime)			if field.Name() == "__schema" {				// We have an introspection query - handle this one in a special way				res[field.Alias()] = field.SelectionSetRuntime().ProcessIntrospection()			} else if field.SelectionSetRuntime() != nil {				nodes := field.SelectionSetRuntime().ProcessNodes([]string{field.Alias()},					field.Name(), field.Arguments(), nil)				res[field.Alias()] = nodes			}		}	}	return res, err}/*nodeIterator is an object which can iterate over nodes.*/type nodeIterator interface {	Next() (string, string)	HasNext() bool	Error() error}/*nodeKeyIteratorWrapper wraps around a normal node key iterator.*/type nodeKeyIteratorWrapper struct {	kind string	*graph.NodeKeyIterator}func (ni *nodeKeyIteratorWrapper) Next() (string, string) {	return ni.NodeKeyIterator.Next(), ni.kind}/*traversalIterator contains a traversal result.*/type traversalIterator struct {	index    int	nodeList []data.Node}func (ti *traversalIterator) Next() (string, string) {	next := ti.nodeList[ti.index]	ti.index++	return next.Key(), next.Kind()}func (ti *traversalIterator) HasNext() bool {	return ti.index < len(ti.nodeList)}func (ti *traversalIterator) Error() error {	return nil}func (rt *selectionSetRuntime) checkArgs(path []string, args map[string]interface{}) {	knownArgs := []string{"key", "matches", "traverse", "storeNode",		"storeEdge", "removeNode", "removeEdge", "ascending", "descending",		"from", "items", "last"}	for arg := range args {		if stringutil.IndexOf(arg, knownArgs) == -1 {			rt.rtp.handleRuntimeError(fmt.Errorf("Unknown argument: %s", arg),				path, rt.node)		}	}}/*ProcessNodes uses the selection set to lookup/store nodes. Kind is not set during atraversal.*/func (rt *selectionSetRuntime) ProcessNodes(path []string, kind string,	args map[string]interface{}, it nodeIterator) []map[string]interface{} {	var from, items, last int	var ascending, descending string	var err error	res := make([]map[string]interface{}, 0)	// Get only the attributes which were specified	attrs, aliasMap, traversalMap := rt.GetPlainFieldsAndAliases(path, kind)	addToRes := func(node data.Node) error {		var err error		r := make(map[string]interface{})		for alias, attr := range aliasMap {			if err == nil {				if traversal, ok := traversalMap[alias]; ok {					nodes, _, err := rt.rtp.gm.TraverseMulti(rt.rtp.part,						node.Key(), node.Kind(), traversal.spec, false)					if err == nil {						data.NodeSort(nodes)						r[alias] = traversal.selectionSetRuntime.ProcessNodes(							append(path, traversal.spec), "", traversal.args,							&traversalIterator{0, nodes})					}				} else {					r[alias] = node.Attr(attr)				}			}		}		if err == nil {			res = append(res, r)		}		return err	}	// Check arguments	rt.checkArgs(path, args)	if it == nil {		err = rt.handleMutationArgs(path, args, kind)	}	if err == nil {		ascending, descending, from, items, last, err = rt.handleOutputArgs(args)		if err == nil {			if key, ok := args["key"]; ok && it == nil {				var node data.Node				// Lookup a single node				if node, err = rt.rtp.FetchNode(rt.rtp.part, fmt.Sprint(key), kind); err == nil && node != nil {					addToRes(node)				}			} else {				matchesRegexMap := make(map[string]*regexp.Regexp)				matchAttrs := make([]string, 0)				// Handle matches expression				matches, matchesOk := args["matches"]				matchesMap, matchesMapOk := matches.(map[string]interface{})				if matchesOk {					if matchesMapOk {						for k, v := range matchesMap {							matchAttrs = append(matchAttrs, k)							if re, rerr := regexp.Compile(fmt.Sprint(v)); rerr == nil {								matchesRegexMap[k] = re							} else {								rt.rtp.handleRuntimeError(fmt.Errorf("Regex %s did not compile: %s", v, rerr.Error()),									path, rt.node)							}						}					} else {						rt.rtp.handleRuntimeError(fmt.Errorf("Matches expression is not a map"),							path, rt.node)					}				}				// Lookup a list of nodes				if it == nil {					var kit *graph.NodeKeyIterator					kit, err = rt.rtp.gm.NodeKeyIterator(rt.rtp.part, kind)					if kit != nil {						it = &nodeKeyIteratorWrapper{kind, kit}					}				}				if it != nil && err == nil {					for err == nil && it.HasNext() {						var node data.Node						if err = it.Error(); err == nil {							nkey, nkind := it.Next()							if kind == "" {								// If the kind is not fixed we need to reevaluate the attributes								// to query for every node								attrs, aliasMap, traversalMap = rt.GetPlainFieldsAndAliases(path, nkind)							}							if node, err = rt.rtp.FetchNodePart(rt.rtp.part, nkey,								nkind, append(attrs, matchAttrs...)); err == nil && node != nil {								if matchesOk && !rt.matchNode(node, matchesRegexMap) {									continue								}								err = addToRes(node)							}						}					}				}			}			// Check if the result should be sorted			if err == nil {				if _, aok := args["ascending"]; aok {					dataSort(res, ascending, true)				} else if _, dok := args["descending"]; dok {					dataSort(res, descending, false)				}			}			// Check if the result should be truncated			if last > 0 && last < len(res) {				res = res[len(res)-last:]			}			if from > 0 || items > 0 {				if from >= len(res) {					from = 0				}				if from+items > len(res) {					res = res[from:]				} else {					res = res[from : from+items]				}			}		}	}	rt.rtp.handleRuntimeError(err, path, rt.node)	return res}/*handleOutputModifyingArgs handles arguments which modify the output presentation.*/func (rt *selectionSetRuntime) handleOutputArgs(args map[string]interface{}) (string, string, int, int, int, error) {	var from, items, last int	var ascending, descending string	var err error	ascendingData, aok := args["ascending"]	descendingData, dok := args["descending"]	if aok && dok {		err = fmt.Errorf("Cannot specify ascending and descending sorting")	} else if aok {		ascending = fmt.Sprint(ascendingData)	} else {		descending = fmt.Sprint(descendingData)	}	if err == nil {		if lastText, ok := args["last"]; ok {			last, err = strconv.Atoi(fmt.Sprint(lastText))		}	}	if err == nil {		if fromText, ok := args["from"]; ok {			from, err = strconv.Atoi(fmt.Sprint(fromText))		}	}	if err == nil {		if itemsText, ok := args["items"]; ok {			items, err = strconv.Atoi(fmt.Sprint(itemsText))		}	}	return ascending, descending, from, items, last, err}/*handleMutationArgs handles node and edge insertion and removal.*/func (rt *selectionSetRuntime) handleMutationArgs(path []string, args map[string]interface{}, kind string) error {	var err error	if toStore, ok := args["storeNode"]; ok && rt.rtp.CheckWritePermission(path, rt.node) {		toStoreMap, ok := toStore.(map[string]interface{})		if ok {			//  Handle mutations of nodes			node := data.NewGraphNodeFromMap(toStoreMap)			if node.Kind() == "" {				node.SetAttr("kind", kind)			}			err = rt.rtp.gm.StoreNode(rt.rtp.part, node)		} else {			rt.rtp.handleRuntimeError(fmt.Errorf("Object required for node attributes and values"),				path, rt.node)		}	}	if toRemove, ok := args["removeNode"]; ok && rt.rtp.CheckWritePermission(path, rt.node) {		toRemoveMap, ok := toRemove.(map[string]interface{})		if ok {			//  Handle removal of nodes			node := data.NewGraphNodeFromMap(toRemoveMap)			if node.Kind() == "" {				node.SetAttr("kind", kind)			}			_, err = rt.rtp.gm.RemoveNode(rt.rtp.part, node.Key(), node.Kind())		} else {			rt.rtp.handleRuntimeError(fmt.Errorf("Object required for node key and kind"),				path, rt.node)		}	}	if toStore, ok := args["storeEdge"]; err == nil && ok && rt.rtp.CheckWritePermission(path, rt.node) {		toStoreMap, ok := toStore.(map[string]interface{})		if ok {			//  Handle mutations of edges			node := data.NewGraphEdgeFromNode(data.NewGraphNodeFromMap(toStoreMap))			err = rt.rtp.gm.StoreEdge(rt.rtp.part, node)		} else {			rt.rtp.handleRuntimeError(fmt.Errorf("Object required for edge attributes and values"),				path, rt.node)		}	}	if toRemove, ok := args["removeEdge"]; err == nil && ok && rt.rtp.CheckWritePermission(path, rt.node) {		toRemoveMap, ok := toRemove.(map[string]interface{})		if ok {			//  Handle mutations of edges			node := data.NewGraphEdgeFromNode(data.NewGraphNodeFromMap(toRemoveMap))			_, err = rt.rtp.gm.RemoveEdge(rt.rtp.part, node.Key(), node.Kind())		} else {			rt.rtp.handleRuntimeError(fmt.Errorf("Object required for edge key and kind"),				path, rt.node)		}	}	return err}/*matchNode matches a given node against a given node template. Returns true ifthe template matches, false otherwise.*/func (rt *selectionSetRuntime) matchNode(node data.Node, nodeTemplate map[string]*regexp.Regexp) bool {	nodeData := node.Data()	for k, v := range nodeTemplate {		// Check if the match query should be negated		negate := false		if strings.HasPrefix(k, "not_") {			k = k[4:]			negate = true		}		mapAttr, ok := nodeData[k]		if !ok {			return false // Attribute does not exist		}		if negate {			if v.MatchString(fmt.Sprint(mapAttr)) {				return false // Attribute is the same			}		} else {			if !v.MatchString(fmt.Sprint(mapAttr)) {				return false // Attribute is not the same			}		}	}	return true}/*traversal captures all required data for a traversal during node lookup.*/type traversal struct {	spec                string	args                map[string]interface{}	selectionSetRuntime *selectionSetRuntime}/*fragmentRuntime is the common interface for all fragment runtimes.*/type fragmentRuntime interface {	TypeCondition() string	SelectionSet() *parser.ASTNode}/*GetPlainFieldsAndAliases returns all fields as a list of node attributes, a map ofaliases to names and a map from aliases to traversals.*/func (rt *selectionSetRuntime) GetPlainFieldsAndAliases(path []string, kind string) (	[]string, map[string]string, map[string]*traversal) {	errMultiFields := make([]string, 0)	resList := []string{"key", "kind"}	resMap := make(map[string]string)	traversalMap := make(map[string]*traversal)	fieldList := append(rt.node.Children[:0:0], rt.node.Children...) // Copy into new slice	for i := 0; i < len(fieldList); i++ {		var lastChild *parser.ASTNode		c := fieldList[i]		if len(c.Children) > 0 {			lastChild = c.Children[len(c.Children)-1]		}		// Check for skip and include directive		if rt.skipField(path, c) {			continue		}		if c.Name == parser.NodeField {			// Handle simple fields			field := c.Runtime.(*fieldRuntime)			if _, ok := resMap[field.Alias()]; ok {				// Alias was used before				if stringutil.IndexOf(field.Alias(), errMultiFields) == -1 {					errMultiFields = append(errMultiFields, field.Alias())				}				continue			}			// Map alias to name and process the field			resMap[field.Alias()] = field.Name()			if lastChild.Name == parser.NodeSelectionSet {				args := field.Arguments()				// Handle traversals				if spec, ok := args["traverse"]; ok {					traversalMap[field.Alias()] = &traversal{						spec:                fmt.Sprint(spec),						args:                args,						selectionSetRuntime: field.SelectionSetRuntime(),					}				} else {					rt.rtp.handleRuntimeError(fmt.Errorf(						"Traversal argument is missing"), path, c)				}			} else if stringutil.IndexOf(field.Name(), resList) == -1 {				// Handle normal attribute lookup				resList = append(resList, field.Name())			}		} else if c.Name == parser.NodeFragmentSpread || c.Name == parser.NodeInlineFragment {			var fd fragmentRuntime			if c.Name == parser.NodeFragmentSpread {				// Lookup fragment spreads				fd = rt.rtp.fragments[c.Token.Val]			} else {				// Construct inline fragments				fd = c.Runtime.(*inlineFragmentDefinitionRuntime)			}			if fd.TypeCondition() != kind {				// Type condition was not met - just skip the fragment				continue			}			ss := fd.SelectionSet()			fieldList = append(fieldList, ss.Children...)		}	}	if len(errMultiFields) > 0 {		for _, name := range errMultiFields {			rt.rtp.handleRuntimeError(fmt.Errorf(				"Field identifier %s used multiple times", name),				path, rt.node)		}	}	return resList, resMap, traversalMap}/*skipField checks if a given field has a skip or include directive and returnsif the directive excludes the field.*/func (rt *selectionSetRuntime) skipField(path []string, node *parser.ASTNode) bool {	for _, c := range node.Children {		if c.Name == parser.NodeDirectives {			for _, directive := range c.Children {				rt := directive.Runtime.(*argumentExpressionRuntime)				name := rt.Name()				args := rt.Arguments()				if name == "skip" || name == "include" {					if cond, ok := args["if"]; ok {						if name == "skip" {							skip, _ := strconv.ParseBool(fmt.Sprint(cond))							return skip						}						include, _ := strconv.ParseBool(fmt.Sprint(cond))						return !include					}					rt.rtp.handleRuntimeError(fmt.Errorf(						"Directive %s is missing the 'if' argument", name), path, c)				}			}		}	}	return false}// ArgumentExpression Runtime// ==========================/*Runtime for expressions with arguments.*/type argumentExpressionRuntime struct {	*invalidRuntime	rtp  *GraphQLRuntimeProvider	node *parser.ASTNode}/*argumentExpressionRuntimeInst returns a new runtime component instance.*/func argumentExpressionRuntimeInst(rtp *GraphQLRuntimeProvider, node *parser.ASTNode) parser.Runtime {	return &argumentExpressionRuntime{&invalidRuntime{rtp, node}, rtp, node}}/*Name returns the name of this field.*/func (rt *argumentExpressionRuntime) Name() string {	if rt.node.Children[0].Name == parser.NodeAlias {		return rt.node.Children[1].Token.Val	}	return rt.node.Children[0].Token.Val}/*Arguments returns all arguments of the field as a map.*/func (rt *argumentExpressionRuntime) Arguments() map[string]interface{} {	res := make(map[string]interface{})	for _, c := range rt.node.Children {		if c.Name == parser.NodeArguments {			for _, a := range c.Children {				res[a.Children[0].Token.Val] = a.Children[1].Runtime.(*valueRuntime).Value()			}		}	}	return res}// Field Runtime// =============/*Runtime for Fields.*/type fieldRuntime struct {	*argumentExpressionRuntime	rtp  *GraphQLRuntimeProvider	node *parser.ASTNode}/*fieldRuntimeInst returns a new runtime component instance.*/func fieldRuntimeInst(rtp *GraphQLRuntimeProvider, node *parser.ASTNode) parser.Runtime {	return &fieldRuntime{&argumentExpressionRuntime{&invalidRuntime{rtp, node},		rtp, node}, rtp, node}}/*Alias returns the alias of this field.*/func (rt *fieldRuntime) Alias() string {	return rt.node.Children[0].Token.Val}/*SelectionSetRuntime returns the SelectionSet runtime of this field.*/func (rt *fieldRuntime) SelectionSetRuntime() *selectionSetRuntime {	res, _ := rt.node.Children[len(rt.node.Children)-1].Runtime.(*selectionSetRuntime)	return res}
 |