コード例 #1
0
 public GraphQLNode(string name, ExpressionResult exp, Expression relationExpression, IEnumerable <ParameterExpression> constantParameters, IEnumerable <object> constantParameterValues)
 {
     Name           = name;
     NodeExpression = exp;
     Fields         = new List <IGraphQLNode>();
     if (relationExpression != null)
     {
         RelationExpression = relationExpression;
     }
     Parameters = constantParameters?.ToList();
     ConstantParameterValues = constantParameterValues?.ToList();
 }
コード例 #2
0
 public IGraphQLBaseNode ParseFieldSelect(ExpressionResult expContext, string name, EntityGraphQLParser.ObjectSelectionContext context)
 {
     try
     {
         IGraphQLBaseNode graphQLNode = null;
         if (expContext.Type.IsEnumerableOrArray())
         {
             //var listExp = Compiler.Util.ExpressionUtil.FindDistinct(result.ExpressionResult);
             //if (listExp.Item1 != null)
             //{
             //    var item1 = (ExpressionResult)listExp.Item1;
             //    item1.AddConstantParameters(result.ExpressionResult.ConstantParameters);
             //    graphQLNode = BuildDynamicSelectOnCollection(new CompiledQueryResult(item1, result.ContextParams), name, context);
             //    graphQLNode.SetNodeExpression((ExpressionResult)Compiler.Util.ExpressionUtil.CombineExpressions(graphQLNode.GetNodeExpression(), listExp.Item2));
             //}
             //else
             //{
             //    graphQLNode = BuildDynamicSelectOnCollection(result, name, context);
             //}
             graphQLNode = BuildDynamicSelectOnCollection(expContext, name, context);
         }
         else
         {
             // Could be a list.First() that we need to turn into a select, or
             // other levels are object selection. e.g. from the top level people query I am selecting all their children { field1, etc. }
             // Can we turn a list.First() into and list.Select().First()
             var listExp = ExpressionUtil.FindIEnumerable(expContext);
             if (listExp.Item1 != null)
             {
                 // yes we can
                 // rebuild the ExpressionResult so we keep any ConstantParameters
                 var item1 = (ExpressionResult)listExp.Item1;
                 item1.AddConstantParameters(expContext.ConstantParameters);
                 item1.AddServices(expContext.Services);
                 graphQLNode = BuildDynamicSelectOnCollection(item1, name, context);
                 graphQLNode.SetCombineExpression(listExp.Item2);
             }
             else
             {
                 graphQLNode = BuildDynamicSelectForObjectGraph(expContext, currentExpressionContext.AsParameter(), name, context);
             }
         }
         return(graphQLNode);
     }
     catch (EntityGraphQLCompilerException ex)
     {
         throw SchemaException.MakeFieldCompileError($"Error compiling field {name}", ex.Message);
     }
 }
コード例 #3
0
        /// Given a syntax of someCollection { fields, to, selection, from, object }
        /// it will build a select assuming 'someCollection' is an IEnumerables
        private IGraphQLBaseNode BuildDynamicSelectOnCollection(ExpressionResult queryResult, string resultName, EntityGraphQLParser.ObjectSelectionContext context)
        {
            var elementType      = queryResult.Type.GetEnumerableOrArrayType();
            var contextParameter = Expression.Parameter(elementType, $"p_{elementType.Name}");

            var exp = queryResult;

            var oldContext = currentExpressionContext;

            currentExpressionContext = (ExpressionResult)contextParameter;
            // visit child fields. Will be more fields
            var fieldExpressions = context.children.Select(c => Visit(c)).Where(n => n != null).ToList();

            var gqlNode = new GraphQLQueryNode(schemaProvider, fragments, resultName, exp, oldContext.AsParameter(), fieldExpressions, (ExpressionResult)contextParameter);

            currentExpressionContext = oldContext;

            return(gqlNode);
        }
コード例 #4
0
        /// <summary>
        /// Given a syntax of { fields, to, selection, from, object } with a context
        /// it will build the correct select statement
        /// </summary>
        /// <param name="name"></param>
        /// <param name="context"></param>
        /// <param name="selectContext"></param>
        /// <returns></returns>
        private IGraphQLBaseNode BuildDynamicSelectForObjectGraph(ExpressionResult selectFromExp, ParameterExpression selectFromParam, string name, EntityGraphQLParser.ObjectSelectionContext context)
        {
            try
            {
                // visit child fields. Will be field or entityQueries again
                // These expression will be built on the element type
                var oldContext = currentExpressionContext;
                currentExpressionContext = selectFromExp;
                var fieldExpressions = context.children.Select(c => Visit(c)).Where(n => n != null).ToList();
                currentExpressionContext = oldContext;

                var graphQLNode = new GraphQLQueryNode(schemaProvider, fragments, name, selectFromExp, selectFromParam, fieldExpressions, selectFromExp);
                return(graphQLNode);
            }
            catch (EntityGraphQLCompilerException ex)
            {
                throw SchemaException.MakeFieldCompileError($"Failed compiling field {name}", ex.Message);
            }
        }
コード例 #5
0
        public override IGraphQLBaseNode VisitGqlFragment(EntityGraphQLParser.GqlFragmentContext context)
        {
            // top level syntax part. Add to the fragrments and return null
            var typeName = context.fragmentType.GetText();

            currentExpressionContext = (ExpressionResult)Expression.Parameter(schemaProvider.Type(typeName).ContextType, $"frag_{typeName}");
            var fields = new List <IGraphQLBaseNode>();

            foreach (var item in context.fields.children)
            {
                var f = Visit(item);
                if (f != null) // white space etc
                {
                    fields.Add(f);
                }
            }
            fragments.Add(new GraphQLFragment(context.fragmentName.GetText(), typeName, fields, (ParameterExpression)currentExpressionContext));
            currentExpressionContext = null;
            return(null);
        }
コード例 #6
0
        private ExpressionResult BuildEntityQueryExpression(IMethodType fieldArgumentContext, string fieldName, string argName, string query)
        {
            if (string.IsNullOrEmpty(query))
            {
                return(null);
            }
            var prop = ((Field)fieldArgumentContext).ArgumentTypesObject.GetType().GetProperties().FirstOrDefault(p => p.Name == argName && p.PropertyType.GetGenericTypeDefinition() == typeof(EntityQueryType <>));

            if (prop == null)
            {
                throw new EntityGraphQLCompilerException($"Can not find argument {argName} of type EntityQuery on field {fieldName}");
            }

            var eqlt         = prop.GetValue(((Field)fieldArgumentContext).ArgumentTypesObject) as BaseEntityQueryType;
            var contextParam = Expression.Parameter(eqlt.QueryType, $"q_{eqlt.QueryType.Name}");
            ExpressionResult expressionResult = EntityQueryCompiler.CompileWith(query, contextParam, schemaProvider, claims, methodProvider, variables).ExpressionResult;

            expressionResult = (ExpressionResult)Expression.Lambda(expressionResult.Expression, contextParam);
            return(expressionResult);
        }
コード例 #7
0
        public override GraphQLOperation VisitGqlTypeDef(EntityGraphQLParser.GqlTypeDefContext context)
        {
            var argName  = context.gqlVar().GetText().TrimStart('$');
            var isArray  = context.arrayType != null;
            var type     = isArray ? context.arrayType.type.GetText() : context.type.GetText();
            var required = context.required != null;
            ExpressionResult defaultValue = null;

            if (context.defaultValue != null)
            {
                defaultValue = constantVisitor.Visit(context.defaultValue);
            }

            if (required && !variables.ContainsKey(argName) && defaultValue == null)
            {
                throw new QueryException($"Missing required variable '{argName}' on query '{this.operation.Name}'");
            }

            this.operation.AddArgument(argName, type, isArray, required, defaultValue != null ? defaultValue : null);

            return(this.operation);
        }
コード例 #8
0
        /// <summary>
        /// Given a syntax of { fields, to, selection, from, object } with a context
        /// it will build the correct select statement
        /// </summary>
        /// <param name="name"></param>
        /// <param name="context"></param>
        /// <param name="selectContext"></param>
        /// <returns></returns>
        private IGraphQLBaseNode BuildDynamicSelectForObjectGraph(ExpressionResult selectFromExp, ParameterExpression selectFromParam, string name, EntityGraphQLParser.ObjectSelectionContext context)
        {
            try
            {
                // visit child fields. Will be field or entityQueries again
                // These expression will be built on the element type
                var oldContext = currentExpressionContext;
                currentExpressionContext = selectFromExp;
                ParameterExpression replacementParameter = null;
                // we might be using a service i.e. ctx => WithService((T r) => r.DoSomething(ctx.Entities.Select(f => f.Id).ToList()))
                // if we can we want to avoid calling that multiple times with a expression like
                // r.DoSomething(ctx.Entities.Select(f => f.Id).ToList()) == null ? null : new {
                //      Field = r.DoSomething(ctx.Entities.Select(f => f.Id).ToList()).Blah
                // }
                // by wrapping the whole thing in a method that does the null check once.
                // This means we build the fieldExpressions on a parameter of the result type
                // We can only do that if it doesn't use the oldContext unless the oldContext is the root context
                // i.e we can't do this on a field on a type because we don't have that value as it might be a selection from an ORM
                bool wrapField = (oldContext.Type == schemaProvider.ContextType || oldContext.NodeType == ExpressionType.Parameter) && currentExpressionContext.Services.Any();
                if (wrapField)
                {
                    // replace with a parameter. The expression is compiled at execution time once
                    replacementParameter     = Expression.Parameter(selectFromExp.Type, "null_wrap");
                    currentExpressionContext = (ExpressionResult)replacementParameter;
                }
                var fieldExpressions = context.children.Select(c => Visit(c)).Where(n => n != null).ToList();
                currentExpressionContext = oldContext;

                var graphQLNode = new GraphQLQueryNode(schemaProvider, fragments, name, selectFromExp, selectFromParam, fieldExpressions, selectFromExp)
                {
                    IsWrapped = wrapField
                };
                return(graphQLNode);
            }
            catch (EntityGraphQLCompilerException ex)
            {
                throw SchemaException.MakeFieldCompileError($"Failed compiling field {name}", ex.Message);
            }
        }
コード例 #9
0
        /// <summary>
        /// This is one of our top level node.
        /// mutation MyMutation {...}
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override IGraphQLBaseNode VisitMutationQuery(EntityGraphQLParser.MutationQueryContext context)
        {
            var operation = GetOperation(context.operationName());

            foreach (var item in operation.Arguments.Where(a => a.DefaultValue != null))
            {
                variables[item.ArgName] = Expression.Lambda(item.DefaultValue.Expression).Compile().DynamicInvoke();
            }
            this.currentExpressionContext = (ExpressionResult)Expression.Parameter(schemaProvider.ContextType, $"ctx");
            var mutateFields = new List <IGraphQLBaseNode>();

            foreach (var c in context.objectSelection().children)
            {
                var n = Visit(c);
                if (n != null)
                {
                    mutateFields.Add(n);
                }
            }
            var mutation = new GraphQLQueryNode(schemaProvider, fragments, operation.Name, null, null, mutateFields, null);

            return(mutation);
        }
コード例 #10
0
        /// <summary>
        /// This is one of our top level node.
        /// query MyQuery {
        ///   entityQuery { fields [, field] },
        ///   entityQuery { fields [, field] },
        ///   ...
        /// }
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public override IGraphQLBaseNode VisitDataQuery(EntityGraphQLParser.DataQueryContext context)
        {
            var operation = GetOperation(context.operationName());

            foreach (var item in operation.Arguments.Where(a => a.DefaultValue != null))
            {
                variables[item.ArgName] = Expression.Lambda(item.DefaultValue.Expression).Compile().DynamicInvoke();
            }
            this.currentExpressionContext = (ExpressionResult)Expression.Parameter(schemaProvider.ContextType, $"ctx");
            var rootFields = new List <GraphQLQueryNode>();

            // Just visit each child node. All top level will be entityQueries
            foreach (var c in context.objectSelection().children)
            {
                var n = Visit(c);
                if (n != null)
                {
                    rootFields.Add((GraphQLQueryNode)n);
                }
            }
            var query = new GraphQLQueryNode(schemaProvider, fragments, operation.Name, currentExpressionContext, (ParameterExpression)currentExpressionContext, rootFields, null);

            return(query);
        }
コード例 #11
0
        /// <summary>
        /// The dotnet Expression for this node. Could be as simple as (Person p) => p.Name
        /// Or as complex as (DbContext ctx) => ctx.People.Where(...).Select(p => new {...}).First()
        /// If there is a object selection (new {} in a Select() or not) we will build the NodeExpression on
        /// Execute() so we can look up any query fragment selections
        /// </summary>
        /// <value></value>
        public ExpressionResult GetNodeExpression()
        {
            // we might have to build the expression on request as when we prase the query
            // document the fragment referenced might be defined later in the document
            if (nodeExpression == null && fieldSelection != null && fieldSelection.Any())
            {
                var  replacer        = new ParameterReplacer();
                var  selectionFields = new List <IGraphQLNode>();
                bool isSelect        = fieldSelectionBaseExpression.Type.IsEnumerableOrArray();

                foreach (var field in fieldSelection)
                {
                    if (field is GraphQLFragmentSelect)
                    {
                        var fragment = queryFragments.FirstOrDefault(i => i.Name == field.Name);
                        if (fragment == null)
                        {
                            throw new EntityQuerySchemaException($"Fragment '{field.Name}' not found in query document");
                        }

                        foreach (IGraphQLNode fragField in fragment.Fields)
                        {
                            ExpressionResult exp = null;
                            if (isSelect)
                            {
                                exp = (ExpressionResult)replacer.Replace(fragField.GetNodeExpression(), fragment.SelectContext, fieldParameter);
                            }
                            else
                            {
                                exp = (ExpressionResult)replacer.Replace(fragField.GetNodeExpression(), fragment.SelectContext, fieldSelectionBaseExpression);
                            }
                            // new object as we reuse fragments
                            selectionFields.Add(new GraphQLNode(schemaProvider, queryFragments, fragField.Name, exp, null, null, null, null));

                            // pull any constant values up
                            foreach (var item in fragField.ConstantParameters)
                            {
                                constantParameters.Add(item.Key, item.Value);
                            }
                        }
                    }
                    else
                    {
                        var gfield = (IGraphQLNode)field;
                        selectionFields.Add(gfield);
                        // pull any constant values up
                        foreach (var item in gfield.ConstantParameters)
                        {
                            constantParameters.Add(item.Key, item.Value);
                        }
                    }
                }
                if (isSelect)
                {
                    // build a .Select(...) - returning a list<>
                    nodeExpression = (ExpressionResult)ExpressionUtil.SelectDynamicToList(fieldParameter, fieldSelectionBaseExpression, selectionFields, schemaProvider);
                }
                else
                {
                    // build a new {...} - returning a single object {}
                    var newExp   = ExpressionUtil.CreateNewExpression(fieldSelectionBaseExpression, selectionFields, schemaProvider);
                    var anonType = newExp.Type;
                    // make a null check from this new expression
                    newExp         = Expression.Condition(Expression.MakeBinary(ExpressionType.Equal, fieldSelectionBaseExpression, Expression.Constant(null)), Expression.Constant(null, anonType), newExp, anonType);
                    nodeExpression = (ExpressionResult)newExp;
                }
                foreach (var field in selectionFields)
                {
                    foreach (var cp in field.ConstantParameters)
                    {
                        if (!constantParameters.ContainsKey(cp.Key))
                        {
                            constantParameters.Add(cp.Key, cp.Value);
                        }
                    }
                }

                foreach (var item in fieldSelectionBaseExpression.ConstantParameters)
                {
                    constantParameters.Add(item.Key, item.Value);
                }
            }
            return(nodeExpression);
        }
コード例 #12
0
 public void SetNodeExpression(ExpressionResult expr)
 {
     nodeExpression = expr;
 }
コード例 #13
0
 public void SetNodeExpression(ExpressionResult expr)
 {
     throw new NotImplementedException();
 }
コード例 #14
0
 public CompiledQueryResult(ExpressionResult expressionResult, List <ParameterExpression> contextParams)
 {
     this.ExpressionResult = expressionResult;
     this.contextParams    = contextParams;
 }
コード例 #15
0
        public override IGraphQLBaseNode VisitField(EntityGraphQLParser.FieldContext context)
        {
            var    fieldName       = context.fieldDef.GetText();
            string schemaTypeName  = schemaProvider.GetSchemaTypeNameForClrType(currentExpressionContext.Type);
            var    actualFieldName = schemaProvider.GetActualFieldName(schemaTypeName, fieldName, claims);

            var args = context.argsCall != null?ParseGqlCall(actualFieldName, context.argsCall) : null;

            var alias = context.alias?.name.GetText();

            if (schemaProvider.HasMutation(actualFieldName))
            {
                var mutationType = schemaProvider.GetMutations().First(m => m.Name == actualFieldName);
                if (args == null)
                {
                    throw new EntityGraphQLCompilerException($"Missing bracets for mutation call {alias ?? fieldName}");
                }
                if (context.select != null)
                {
                    var expContext = (ExpressionResult)Expression.Parameter(mutationType.ReturnTypeClr, $"mut_{actualFieldName}");
                    var oldContext = currentExpressionContext;
                    currentExpressionContext = expContext;
                    var select = ParseFieldSelect(expContext, actualFieldName, context.select);
                    currentExpressionContext = oldContext;
                    return(new GraphQLMutationNode(mutationType, args, (GraphQLQueryNode)select));
                }
                else
                {
                    var resultName = alias ?? actualFieldName;

                    return(new GraphQLMutationNode(mutationType, args, null));
                }
            }
            else
            {
                if (!schemaProvider.TypeHasField(schemaTypeName, actualFieldName, args != null ? args.Select(d => d.Key) : new string[0], claims))
                {
                    throw new EntityGraphQLCompilerException($"Field {actualFieldName} not found on type {schemaTypeName}");
                }

                var result = schemaProvider.GetExpressionForField(currentExpressionContext, schemaTypeName, actualFieldName, args, claims);

                IGraphQLBaseNode fieldResult;
                var resultName = alias ?? actualFieldName;

                if (context.select != null)
                {
                    fieldResult = ParseFieldSelect(result, resultName, context.select);
                }
                else
                {
                    fieldResult = new GraphQLQueryNode(schemaProvider, fragments, resultName, result, currentExpressionContext.AsParameter(), null, null);
                }

                if (context.directive != null)
                {
                    return(ProcessFieldDirective((GraphQLQueryNode)fieldResult, context.directive));
                }
                return(fieldResult);
            }
        }
コード例 #16
0
 public QueryResult(ExpressionResult expressionResult, List <ParameterExpression> contextParams, IEnumerable <object> parameterValues)
 {
     this.expressionResult        = expressionResult;
     this.contextParams           = contextParams;
     this.constantParameterValues = parameterValues;
 }
コード例 #17
0
 public GraphQlOperationArgument(string argName, object type, bool isArray, bool required, ExpressionResult defaultValue)
 {
     this.ArgName  = argName;
     this.Type     = type;
     this.IsArray  = isArray;
     this.Required = required;
     DefaultValue  = defaultValue;
 }
コード例 #18
0
 internal void AddArgument(string argName, object type, bool isArray, bool required, ExpressionResult defaultValue)
 {
     Arguments.Add(new GraphQlOperationArgument(argName, type, isArray, required, defaultValue));
 }
コード例 #19
0
        public ExpressionResult ParseGqlarg(IField fieldArgumentContext, EntityGraphQLParser.GqlargContext context)
        {
            ExpressionResult gqlVarValue = null;

            if (context.gqlVar() != null)
            {
                string varKey = context.gqlVar().GetText().TrimStart('$');
                if (variables == null)
                {
                    throw new EntityGraphQLCompilerException($"Missing variable {varKey}");
                }
                object value = variables.GetValueFor(varKey);
                gqlVarValue = (ExpressionResult)Expression.Constant(value);
            }
            else
            {
                // this is an expression
                gqlVarValue = constantVisitor.Visit(context.gqlvalue);
            }

            string argName = context.gqlfield.GetText();

            if (fieldArgumentContext != null && fieldArgumentContext.HasArgumentByName(argName))
            {
                var argType = fieldArgumentContext.GetArgumentType(argName);

                if (gqlVarValue != null && gqlVarValue.Type == typeof(string) && gqlVarValue.NodeType == ExpressionType.Constant)
                {
                    string strValue = (string)((ConstantExpression)gqlVarValue).Value;
                    if (
                        (argType.Type.TypeDotnet == typeof(Guid) || argType.Type.TypeDotnet == typeof(Guid?) ||
                         argType.Type.TypeDotnet == typeof(RequiredField <Guid>) || argType.Type.TypeDotnet == typeof(RequiredField <Guid?>)) && ConstantVisitor.GuidRegex.IsMatch(strValue))
                    {
                        return((ExpressionResult)Expression.Constant(Guid.Parse(strValue)));
                    }
                    if (argType.Type.TypeDotnet.IsConstructedGenericType && argType.Type.TypeDotnet.GetGenericTypeDefinition() == typeof(EntityQueryType <>))
                    {
                        string query = strValue;
                        if (query.StartsWith("\""))
                        {
                            query = query.Substring(1, context.gqlvalue.GetText().Length - 2);
                        }
                        return(BuildEntityQueryExpression(fieldArgumentContext, fieldArgumentContext.Name, argName, query));
                    }

                    var argumentNonNullType = argType.Type.TypeDotnet.IsNullableType() ? Nullable.GetUnderlyingType(argType.Type.TypeDotnet) : argType.Type.TypeDotnet;
                    if (argumentNonNullType.GetTypeInfo().IsEnum)
                    {
                        var enumName   = strValue;
                        var valueIndex = Enum.GetNames(argumentNonNullType).ToList().FindIndex(n => n == enumName);
                        if (valueIndex == -1)
                        {
                            throw new EntityGraphQLCompilerException($"Value {enumName} is not valid for argument {context.gqlfield.GetText()}");
                        }
                        var enumValue = Enum.GetValues(argumentNonNullType).GetValue(valueIndex);
                        return((ExpressionResult)Expression.Constant(enumValue));
                    }
                }
            }
            return(gqlVarValue);
        }