Exemplo n.º 1
0
        private static IDictionary <string, object> MapResults(object queryObject, IEnumerable <ExecSelection <Info> > selections, GraphQLSchema <TContext> schema)
        {
            if (queryObject == null) // TODO: Check type non-null and throw exception
            {
                return(null);
            }
            var dict             = new Dictionary <string, object>();
            var type             = queryObject.GetType();
            var graphQlQueryType = schema.GetGQLTypeByQueryType(queryObject.GetType());

            var queryTypeToSelections = CreateQueryTypeToSelectionsMapping(graphQlQueryType, selections.ToList());

            if (!queryTypeToSelections.ContainsKey(type))
            {
                return(dict);
            }

            foreach (var map in selections)
            {
                var key               = map.Name;
                var field             = map.SchemaField.Field();
                var typeConditionType = map.TypeCondition?.Value?.Type();
                var propertyName      = field.Name == "__typename" ? field.Name : CreatePropertyName(graphQlQueryType, typeConditionType, field.Name);

                object obj = null;

                if (field.IsPost)
                {
                    obj = field.PostFieldFunc();
                }
                else if (type.GetProperty(propertyName) != null)
                {
                    obj = type.GetProperty(propertyName).GetGetMethod().Invoke(queryObject, new object[] {});
                }
                else
                {
                    continue;
                }


                // Filter fields for selections with type conditions - the '__typename'-property has to be present.
                if (map.TypeCondition != null)
                {
                    // The selection has a type condition, i.e. the result has a type with included types.
                    // The result contains all fields of all included types and has to be filtered.
                    // Sample:
                    //
                    // Types:
                    //      - Character     [name: string]
                    //      - Human         [height: float]           extends Character
                    //      - Stormtrooper  [specialization: string]  extends Human
                    //      - Droid         [orimaryFunction: string] extends Character
                    //
                    // Sample query:
                    //      query { heros { name, ... on Human { height }, ... on Stormtrooper { specialization }, ... on Droid { primaryFunction } } }
                    //
                    // The ExecSelection for 'height', 'specialization' and 'primaryFunction' have a type condition with the following type names:
                    //      - height:           Human
                    //      - specialization:   Stormtrooper
                    //      - primaryFunction:  Droid
                    //
                    // To filter the result properly, we have to consider the following cases:
                    // - Human:
                    //      - Include: 'name', 'height'
                    //      - Exclude: 'specialization', 'primaryFunction'
                    //  => (1) Filter results of selections with a type-condition-name != result.__typename
                    //
                    //  - Stormtrooper
                    //      - Include: 'name', 'height', and 'specialization'
                    //      - Exclude: 'primaryFunction'
                    // => Same as Human (1), but:
                    //  => (2) include results of selections with the same type-condition-name of any ancestor-type.
                    //
                    var selectionConditionTypeName = map.TypeCondition.Value?.TypeName;
                    var typenameProp   = type.GetRuntimeProperty("__typename");
                    var resultTypeName = (string)typenameProp?.GetValue(queryObject);

                    // (1) type-condition-name != result.__typename
                    if (selectionConditionTypeName != resultTypeName)
                    {
                        // (2) Check ancestor types
                        var resultGraphQlType = schema.Types.FirstOrDefault(t => t.Name == resultTypeName);
                        if (resultGraphQlType != null &&
                            resultGraphQlType.Name != selectionConditionTypeName &&
                            resultGraphQlType.Interfaces.All(i => i.Name != selectionConditionTypeName))
                        {
                            continue;
                        }
                    }
                }

                if (obj == null && !dict.ContainsKey(key))
                {
                    dict.Add(key, null);
                    continue;
                }

                if (field.IsPost && map.Selections.Any())
                {
                    var selector = GetSelector(schema, field.Type, map.Selections.Values(),
                                               new ExpressionOptions(null, castAssignment: true, nullCheckLists: true,
                                                                     typeCheckInheritance: true));
                    obj = selector.Compile().DynamicInvoke(obj);
                }

                // Due to type conditions a key might occur multiple times
                if (map.Selections.Any())
                {
                    var listObj = obj as IEnumerable <object>;
                    if (listObj != null && !dict.ContainsKey(key))
                    {
                        dict.Add(key, listObj.Select(o => MapResults(o, map.Selections.Values(), schema)).ToList());
                    }
                    else if (obj != null && !dict.ContainsKey(key))
                    {
                        dict.Add(key, MapResults(obj, map.Selections.Values(), schema));
                    }
                    else
                    {
                        throw new Exception("Shouldn't be here");
                    }
                }
                else
                {
                    if (!dict.ContainsKey(key))
                    {
                        dict.Add(key, obj);
                    }
                }
            }
            return(dict);
        }