Esempio n. 1
0
        private static IDictionary <Type, IEnumerable <ExecSelection <Info> > > CreateQueryTypeToSelectionsMapping(
            GraphQLType queryGraphQlType, IList <ExecSelection <Info> > selections)
        {
            var abstractFieldSelection = selections
                                         .Where(m => !m.SchemaField.Field().IsPost)
                                         .Where(s => s.TypeCondition == null);
            var typeConditionSelection = selections
                                         .Where(m => !m.SchemaField.Field().IsPost)
                                         .Where(s => s.TypeCondition != null)
                                         .GroupBy(
                s => s.TypeCondition?.Value?.Type().QueryType ?? queryGraphQlType.QueryType
                )
                                         .ToDictionary(s => s.Key, s => s.AsEnumerable());

            return(new List <GraphQLType> {
                queryGraphQlType
            }.Concat(queryGraphQlType.PossibleTypes)
                   .ToDictionary(
                       t => t.QueryType,
                       t =>
                       abstractFieldSelection
                       .Concat(
                           // Add type condition selection bindings
                           typeConditionSelection.ContainsKey(t.QueryType)
                                    ? typeConditionSelection[t.QueryType]
                                    : new List <ExecSelection <Info> >())
                       .Concat(
                           t.Interfaces.SelectMany(i => typeConditionSelection.ContainsKey(i.QueryType)
                                    ? typeConditionSelection[i.QueryType]
                                    : new List <ExecSelection <Info> >()))
                       ));
        }
Esempio n. 2
0
        private static LambdaExpression GetSelector(GraphQLSchema <TContext> schema, GraphQLType gqlType, IEnumerable <ExecSelection <Info> > selections, ExpressionOptions options)
        {
            var parameter = Expression.Parameter(gqlType.CLRType, "p");
            var init      = GetMemberInit(schema, gqlType.QueryType, selections, parameter, options);

            return(Expression.Lambda(init, parameter));
        }
Esempio n. 3
0
        private static FieldMap MapField(Field field, GraphQLType type)
        {
            var schemaField = type.Fields.First(f => f.Name == field.Name);

            return(new FieldMap
            {
                ParsedField = field,
                SchemaField = schemaField,
                Children = field.Fields.Select(f => MapField(f, schemaField.Type)).ToList(),
            });
        }
Esempio n. 4
0
        private static string CreatePropertyName(GraphQLType graphQlType, GraphQLType typeConditionType,
                                                 string fieldName)
        {
            var isObjectAbstractType = graphQlType.TypeKind == TypeKind.UNION ||
                                       graphQlType.TypeKind == TypeKind.INTERFACE;
            var isTypeConditionInterface = typeConditionType?.TypeKind == TypeKind.INTERFACE;

            return(isObjectAbstractType && !isTypeConditionInterface && typeConditionType != null
                ? GraphQLSchema <TContext> .CreatePossibleTypePropertyName(typeConditionType.Name, fieldName)
                : fieldName);
        }
Esempio n. 5
0
        public GraphQLTypeBuilder <TContext, TEntity> AddType <TEntity>(string description = null)
        {
            var type = typeof(TEntity);

            if (_types.Any(t => t.CLRType == type))
            {
                throw new ArgumentException("Type has already been added");
            }

            var gqlType = new GraphQLType(type)
            {
                IsScalar = type.IsPrimitive, Description = description ?? ""
            };

            _types.Add(gqlType);

            return(new GraphQLTypeBuilder <TContext, TEntity>(this, gqlType));
        }
Esempio n. 6
0
 public static GraphQLField NewMutation <TContext, TArgs, TMutReturn>(GraphQLSchema schema, string name, Func <TArgs, TMutReturn, LambdaExpression> exprFunc, Type fieldCLRType, GraphQLType definingType, Func <TContext, TArgs, TMutReturn> mutationFunc)
 => NewInternal <TArgs>(schema, name, exprFunc, fieldCLRType, definingType, mutationFunc);
Esempio n. 7
0
 public static GraphQLField New <TArgs>(GraphQLSchema schema, string name, Func <TArgs, LambdaExpression> exprFunc, Type fieldCLRType, GraphQLType definingType)
 => NewInternal <TArgs>(schema, name, exprFunc, fieldCLRType, definingType, null);
Esempio n. 8
0
        private static GraphQLField NewInternal <TArgs>(GraphQLSchema schema, string name, Delegate exprFunc, Type fieldCLRType, GraphQLType definingType, Delegate mutationFunc)
        {
            var isList = false;

            if (fieldCLRType.IsGenericType && TypeHelpers.IsAssignableToGenericType(fieldCLRType, typeof(IEnumerable <>)))
            {
                fieldCLRType = fieldCLRType.GetGenericArguments()[0];
                isList       = true;
            }

            return(new GraphQLField
            {
                Schema = schema,
                Name = name,
                FieldCLRType = fieldCLRType,
                DefiningType = definingType,
                ArgsCLRType = typeof(TArgs),
                IsList = isList,
                ExprFunc = exprFunc,
                MutationFunc = mutationFunc,
                IsMutation = mutationFunc != null,
            });
        }
 internal GraphQLTypeBuilder(GraphQLSchema <TContext> schema, GraphQLType type)
 {
     _schema = schema;
     _type   = type;
 }
Esempio n. 10
0
 private static bool IsEqualToAnyAncestorType(GraphQLType type, string referenceTypeName)
 {
     return(type != null && (type.Name == referenceTypeName || IsEqualToAnyAncestorType(type?.BaseType, referenceTypeName)));
 }
Esempio n. 11
0
        private static MemberBinding GetBinding(GraphQLSchema <TContext> schema, ExecSelection <Info> map, GraphQLType graphQlType, Expression baseBindingExpr, ExpressionOptions options)
        {
            var toType         = graphQlType.QueryType;
            var field          = map.SchemaField.Field();
            var needsTypeCheck = baseBindingExpr.Type != map.SchemaField.DeclaringType?.Info?.Type?.CLRType;
            var propertyName   = CreatePropertyName(graphQlType, map.TypeCondition?.Value?.Type(),
                                                    map.SchemaField.FieldName);
            var toMember = toType.GetProperty(
                propertyName,
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);

            if (toMember == null)
            {
                throw new Exception($"The field '{map.SchemaField.FieldName}' does not exist on type '{graphQlType.Name}'.");
            }
            // expr is form of: (context, entity) => entity.Field
            var expr = field.GetExpression(map.Arguments.Values());

            // The field might be defined on sub-types of the specified type, so add a type cast if necessary.
            // If appropriate, `entity.Field` becomes `(entity as TFieldDefiningType).Field`
            var typeCastedBaseExpression = needsTypeCheck ? Expression.TypeAs(baseBindingExpr, field.DefiningType.CLRType) : baseBindingExpr;

            // Replace (entity) with baseBindingExpr, note expression is no longer a LambdaExpression
            // `(context, entity) => entity.Field` becomes `someOtherEntity.Entity.Field` where baseBindingExpr is `someOtherEntity.Entity`
            var replacedBase = ParameterReplacer.Replace(expr.Body, expr.Parameters[1], typeCastedBaseExpression);

            // If the entity has to be casted, add a type check:
            // `(someOtherEntity.Entity as TFieldDefininingType).Field` becomes `(someOtherEntity.Entity is TFieldDefininingType) ? (someOtherEntity.Entity as TFieldDefininingType).Field : null`
            if (needsTypeCheck && options.TypeCheckInheritance)
            {
                // The expression type has to be nullable
                if (replacedBase.Type.IsValueType)
                {
                    replacedBase = Expression.Convert(replacedBase, typeof(Nullable <>).MakeGenericType(replacedBase.Type));
                }

                var typeCheck  = Expression.TypeIs(baseBindingExpr, field.DefiningType.CLRType);
                var nullResult = Expression.Constant(null, replacedBase.Type);
                replacedBase = Expression.Condition(typeCheck, replacedBase, nullResult);
            }

            // This just makes sure that the (context) parameter is the same as the one used by the whole query
            var replacedContext = ParameterReplacer.Replace(replacedBase, expr.Parameters[0], GraphQLSchema <TContext> .DbParam);

            // If there aren't any children, then we can assume that this is a scalar entity and we don't have to map child fields
            if (!map.Selections.Any())
            {
                if (!field.IsList)
                {
                    if (options.CastAssignment && expr.Body.Type != toMember.PropertyType)
                    {
                        replacedContext = Expression.Convert(replacedContext, toMember.PropertyType);
                    }
                    return(Expression.Bind(toMember, replacedContext));
                }

                return(Expression.Bind(toMember,
                                       options.NullCheckLists
                        ? NullPropagate(replacedContext, replacedContext)
                        : replacedContext));
            }

            // If binding a single entity, just use the already built selector expression (replaced context)
            // Otherwise, if binding to a list, introduce a new parameter that will be used in a call to .Select
            var listParameter = Expression.Parameter
                                    (field.Type.CLRType, field.Type.CLRType.Name.Substring(0, 1).ToLower()); // TODO: Name conflicts in expressions?
            var bindChildrenTo = map.SchemaField.Field().IsList ? listParameter : replacedContext;

            // Now that we have our new binding parameter, build the tree for the rest of the children
            var memberInit = GetMemberInit(schema, field.Type, map.Selections.Values(), bindChildrenTo, options);

            // For single entities, we're done and we can just bind to the memberInit expression
            if (!field.IsList)
            {
                return(Expression.Bind(toMember, memberInit));
            }

            // However for lists, we need to check for null and call .Select() and .ToList() first
            var selectLambda = Expression.Lambda(memberInit, listParameter);
            var call         = Expression.Call(typeof(Enumerable), "Select", new[] { field.Type.CLRType, field.Type.QueryType }, replacedContext, selectLambda);
            var toList       = Expression.Call(typeof(Enumerable), "ToList", new[] { field.Type.QueryType }, call);

            return(Expression.Bind(toMember, options.NullCheckLists ? (Expression)NullPropagate(replacedContext, toList) : toList));
        }
Esempio n. 12
0
        private static ConditionalExpression GetMemberInit(GraphQLSchema <TContext> schema, GraphQLType graphQlType, IEnumerable <ExecSelection <Info> > selectionsEnumerable, Expression parameterExpression, ExpressionOptions options)
        {
            var queryType = graphQlType.QueryType;
            // Avoid possible multiple enumeration of selections-enumerable
            var selections = selectionsEnumerable as IList <ExecSelection <Info> > ?? selectionsEnumerable.ToList();

            // The '__typename'-field selection has to be added for queries with type conditions
            var typeConditionButNoTypeNameSelection = selections.Any() &&
                                                      selections.Any(s => s.TypeCondition != null);

            // Any '__typename' selection have to be replaced by the '__typename' selection of the target type' '__typename'-field.
            var typeNameConditionHasToBeReplaced = selections.Any(s => s.Name == "__typename");

            var bindingsWithPossibleDuplicates =
                selections.Where(s => !s.SchemaField.Info.Field.IsPost)
                .Where(s => s.Name != "__typename")
                .Select(s => GetBinding(schema, s, graphQlType, parameterExpression, options))
                .ToList();
            var uniqueBindingMemberNames = bindingsWithPossibleDuplicates.Select(b => b.Member.Name).Distinct();
            var bindings =
                uniqueBindingMemberNames.Select(name => bindingsWithPossibleDuplicates.First(b => b.Member.Name == name));

            // Add selection for '__typename'-field of the proper type
            if (typeConditionButNoTypeNameSelection || typeNameConditionHasToBeReplaced)
            {
                // Find the base types' `__typename` field
                var typeNameField = graphQlType?.Fields.Find(f => f.Name == "__typename");
                if (typeNameField != null && !typeNameField.IsPost)
                {
                    var typeNameExecSelection = new ExecSelection <Info>(
                        new SchemaField(
                            schema.Adapter.QueryTypes[graphQlType?.Name],
                            typeNameField,
                            schema.Adapter),
                        new FSharpOption <string>(typeNameField?.Name),
                        null,
                        new WithSource <ExecArgument <Info> >[] {},
                        new WithSource <ExecDirective <Info> >[] {},
                        new WithSource <ExecSelection <Info> >[] {}
                        );
                    bindings =
                        bindings
                        .Where(b => b.Member.Name != "__typename")
                        .Concat(new[]
                                { GetBinding(schema, typeNameExecSelection, graphQlType, parameterExpression, options) })
                        .ToList();
                }
            }

            var memberInit = Expression.MemberInit(Expression.New(queryType), bindings);

            return(NullPropagate(parameterExpression, memberInit));
        }
Esempio n. 13
0
 // Set return type, e.g. union types.
 internal void SetReturnType(GraphQLType type)
 {
     _type = type;
 }