public TypeDescriptorModel(
     OutputTypeModel typeModel,
     NamedTypeDescriptor namedTypeDescriptor)
 {
     TypeModel           = typeModel;
     NamedTypeDescriptor = namedTypeDescriptor;
 }
예제 #2
0
        private static void RegisterType(
            ClientModel model,
            IMapperContext context,
            Dictionary <NameString, TypeDescriptorModel> typeDescriptors,
            OutputTypeModel outputType,
            List <UnionType> unionTypes,
            TypeKind?kind = null,
            OperationModel?operationModel = null)
        {
            if (typeDescriptors.TryGetValue(
                    outputType.Name,
                    out TypeDescriptorModel descriptorModel))
            {
                return;
            }

            if (operationModel is not null && outputType.IsInterface)
            {
                descriptorModel = CreateInterfaceTypeModel(
                    model,
                    context,
                    typeDescriptors,
                    outputType,
                    unionTypes,
                    descriptorModel,
                    operationModel,
                    kind);
            }
        public OutputTypeModel AnalyzeOperation(
            IDocumentAnalyzerContext context,
            SelectionSetVariants selectionSetVariants)
        {
            Path rootSelectionPath = Path.Root.Append(context.OperationName);

            FragmentNode returnTypeFragment =
                FragmentHelper.CreateFragmentNode(
                    context.OperationType,
                    rootSelectionPath,
                    selectionSetVariants.ReturnType);

            returnTypeFragment = FragmentHelper.RewriteForConcreteType(returnTypeFragment);

            OutputTypeModel returnType =
                FragmentHelper.CreateInterface(
                    context,
                    returnTypeFragment,
                    rootSelectionPath);

            FragmentHelper.CreateClass(
                context,
                returnTypeFragment,
                selectionSetVariants.ReturnType,
                returnType);

            return(returnType);
        }
예제 #4
0
        public async Task One_Document_One_Op_One_Field_No_Fragments()
        {
            // arrange
            var schema =
                await new ServiceCollection()
                .AddStarWarsRepositories()
                .AddGraphQL()
                .AddStarWars()
                .BuildSchemaAsync();

            schema =
                SchemaHelper.Load(
                    ("", schema.ToDocument()),
                    ("", Utf8GraphQLParser.Parse(
                         @"extend scalar String @runtimeType(name: ""Abc"")")));

            var document =
                Utf8GraphQLParser.Parse(@"
                    query GetHero {
                        hero(episode: NEW_HOPE) {
                            name
                        }
                    }");

            // act
            ClientModel clientModel =
                DocumentAnalyzer
                .New()
                .SetSchema(schema)
                .AddDocument(document)
                .Analyze();

            // assert
            Assert.Empty(
                clientModel.InputObjectTypes);

            Assert.Collection(
                clientModel.LeafTypes,
                type =>
            {
                Assert.Equal("String", type.Name);
                Assert.Equal("Abc", type.RuntimeType);
            });

            Assert.Collection(
                clientModel.Operations,
                op =>
            {
                Assert.Equal("IGetHero", op.ResultType.Name);

                Assert.Collection(
                    op.GetImplementations(op.ResultType),
                    model => Assert.Equal("GetHero", model.Name));

                OutputTypeModel fieldResultType =
                    op.GetFieldResultType(op.ResultType.Fields.Single().SyntaxNode);
                Assert.Equal("IGetHero_Hero", fieldResultType.Name);
            });
        }
        private OutputTypeModel AnalyzeWithDefaults(
            IDocumentAnalyzerContext context,
            FieldSelection fieldSelection,
            SelectionSetVariants selectionVariants)
        {
            FragmentNode returnTypeFragment =
                FragmentHelper.CreateFragmentNode(
                    selectionVariants.ReturnType,
                    fieldSelection.Path);

            OutputTypeModel returnType =
                FragmentHelper.CreateInterface(
                    context,
                    returnTypeFragment,
                    fieldSelection.Path);

            context.RegisterSelectionSet(
                returnType.Type,
                selectionVariants.ReturnType.SyntaxNode,
                returnType.SelectionSet);

            foreach (SelectionSet selectionSet in selectionVariants.Variants)
            {
                returnTypeFragment = FragmentHelper.CreateFragmentNode(
                    selectionSet,
                    fieldSelection.Path,
                    appendTypeName: true);

                returnTypeFragment = FragmentHelper.RewriteForConcreteType(returnTypeFragment);

                OutputTypeModel @interface =
                    FragmentHelper.CreateInterface(
                        context,
                        returnTypeFragment,
                        fieldSelection.Path,
                        new[] { returnType });

                OutputTypeModel @class =
                    FragmentHelper.CreateClass(
                        context,
                        returnTypeFragment,
                        selectionSet,
                        @interface);

                context.RegisterSelectionSet(
                    selectionSet.Type,
                    selectionSet.SyntaxNode,
                    @class.SelectionSet);
            }

            return(returnType);
        }
 private static void CollectClassesThatImplementInterface(
     OperationModel operation,
     OutputTypeModel outputType,
     Dictionary <NameString, TypeDescriptorModel> typeDescriptors,
     HashSet <NamedTypeDescriptor> classes)
 {
     foreach (var type in operation.GetImplementations(outputType))
     {
         if (type.IsInterface)
         {
             CollectClassesThatImplementInterface(
                 operation,
                 type,
                 typeDescriptors,
                 classes);
         }
         else
         {
             classes.Add(typeDescriptors[type.Name].NamedTypeDescriptor);
         }
     }
 }
예제 #7
0
        public async Task Create_TypeModels_Infer_From_Fragments_With_HoistedFragment()
        {
            // arrange
            var schema =
                await new ServiceCollection()
                .AddStarWarsRepositories()
                .AddGraphQL()
                .AddStarWars()
                .BuildSchemaAsync();

            var document =
                Utf8GraphQLParser.Parse(@"
                    query GetHero {
                        hero(episode: NEW_HOPE) @returns(fragment: ""Hero"") {
                            ... Characters
                        }
                    }

                    fragment Characters on Character {
                        ... Human
                        ... Droid
                    }

                    fragment Hero on Character {
                        name
                    }

                    fragment Human on Human {
                        ... Hero
                        homePlanet
                    }

                    fragment Droid on Droid {
                        ... Hero
                        primaryFunction
                    }");

            var context = new DocumentAnalyzerContext(schema, document);
            var selectionSetVariants = context.CollectFields();
            var fieldSelection       = selectionSetVariants.ReturnType.Fields.First();

            selectionSetVariants = context.CollectFields(fieldSelection);

            // act
            var list = new List <OutputTypeModel>();
            var returnTypeFragmentName = FragmentHelper.GetReturnTypeName(fieldSelection);
            var returnTypeFragment     = FragmentHelper.CreateFragmentNode(
                selectionSetVariants.Variants[0],
                fieldSelection.Path,
                appendTypeName: true);

            returnTypeFragment = FragmentHelper.GetFragment(
                returnTypeFragment,
                returnTypeFragmentName !);
            list.Add(FragmentHelper.CreateInterface(
                         context,
                         returnTypeFragment !,
                         fieldSelection.Path));

            foreach (SelectionSet selectionSet in
                     selectionSetVariants.Variants.OrderBy(t => t.Type.Name.Value))
            {
                returnTypeFragment = FragmentHelper.CreateFragmentNode(
                    selectionSet,
                    fieldSelection.Path,
                    appendTypeName: true);

                returnTypeFragment = FragmentHelper.RewriteForConcreteType(returnTypeFragment);

                OutputTypeModel @interface = FragmentHelper.CreateInterface(
                    context,
                    returnTypeFragment,
                    fieldSelection.Path,
                    new [] { list[0] });

                OutputTypeModel @class = FragmentHelper.CreateClass(
                    context,
                    returnTypeFragment,
                    selectionSet,
                    @interface);

                list.Add(@interface);
                list.Add(@class);
            }

            // assert
            Assert.Collection(
                list,
                type =>
            {
                Assert.Equal("IHero", type.Name);

                Assert.Empty(type.Implements);

                Assert.Collection(
                    type.Fields,
                    field => Assert.Equal("Name", field.Name));
            },
                type =>
            {
                Assert.Equal("IGetHero_Hero_Droid", type.Name);

                Assert.Collection(
                    type.Implements,
                    impl => Assert.Equal("ICharacters_Droid", impl.Name));

                Assert.Empty(type.Fields);
            },
                type =>
            {
                Assert.Equal("GetHero_Hero_Droid", type.Name);

                Assert.Collection(
                    type.Implements,
                    impl => Assert.Equal("IGetHero_Hero_Droid", impl.Name));

                Assert.Collection(
                    type.Fields,
                    field => Assert.Equal("Name", field.Name),
                    field => Assert.Equal("PrimaryFunction", field.Name));
            },
                type =>
            {
                Assert.Equal("IGetHero_Hero_Human", type.Name);

                Assert.Collection(
                    type.Implements,
                    impl => Assert.Equal("ICharacters_Human", impl.Name));

                Assert.Empty(type.Fields);
            },
                type =>
            {
                Assert.Equal("GetHero_Hero_Human", type.Name);

                Assert.Collection(
                    type.Implements,
                    impl => Assert.Equal("IGetHero_Hero_Human", impl.Name));

                Assert.Collection(
                    type.Fields,
                    field => Assert.Equal("Name", field.Name),
                    field => Assert.Equal("HomePlanet", field.Name));
            });
        }
예제 #8
0
        public async Task Create_TypeModels_Infer_TypeStructure()
        {
            // arrange
            var schema =
                await new ServiceCollection()
                .AddStarWarsRepositories()
                .AddGraphQL()
                .AddStarWars()
                .BuildSchemaAsync();

            var document =
                Utf8GraphQLParser.Parse(@"
                    query GetHero {
                        hero(episode: NEW_HOPE) {
                            name
                        }
                    }");

            var context = new DocumentAnalyzerContext(schema, document);
            var selectionSetVariants = context.CollectFields();
            var fieldSelection       = selectionSetVariants.ReturnType.Fields.First();

            selectionSetVariants = context.CollectFields(fieldSelection);

            // act
            var list = new List <OutputTypeModel>();
            var returnTypeFragment = FragmentHelper.CreateFragmentNode(
                selectionSetVariants.ReturnType,
                fieldSelection.Path);

            list.Add(FragmentHelper.CreateInterface(
                         context,
                         returnTypeFragment,
                         fieldSelection.Path));

            foreach (SelectionSet selectionSet in
                     selectionSetVariants.Variants.OrderBy(t => t.Type.Name.Value))
            {
                returnTypeFragment = FragmentHelper.CreateFragmentNode(
                    selectionSet,
                    fieldSelection.Path,
                    appendTypeName: true);

                OutputTypeModel @interface = FragmentHelper.CreateInterface(
                    context,
                    returnTypeFragment,
                    fieldSelection.Path,
                    new [] { list[0] });

                OutputTypeModel @class = FragmentHelper.CreateClass(
                    context,
                    returnTypeFragment,
                    selectionSet,
                    @interface);

                list.Add(@interface);
                list.Add(@class);
            }

            // assert
            Assert.Collection(
                list,
                type =>
            {
                Assert.Equal("IGetHero_Hero", type.Name);

                Assert.Empty(type.Implements);

                Assert.Collection(
                    type.Fields,
                    field => Assert.Equal("Name", field.Name));
            },
                type =>
            {
                Assert.Equal("IGetHero_Hero_Droid", type.Name);

                Assert.Collection(
                    type.Implements,
                    impl => Assert.Equal("IGetHero_Hero", impl.Name));

                Assert.Empty(type.Fields);
            },
                type =>
            {
                Assert.Equal("GetHero_Hero_Droid", type.Name);

                Assert.Collection(
                    type.Implements,
                    impl => Assert.Equal("IGetHero_Hero_Droid", impl.Name));

                Assert.Collection(
                    type.Fields,
                    field => Assert.Equal("Name", field.Name));
            },
                type =>
            {
                Assert.Equal("IGetHero_Hero_Human", type.Name);

                Assert.Collection(
                    type.Implements,
                    impl => Assert.Equal("IGetHero_Hero", impl.Name));

                Assert.Empty(type.Fields);
            },
                type =>
            {
                Assert.Equal("GetHero_Hero_Human", type.Name);

                Assert.Collection(
                    type.Implements,
                    impl => Assert.Equal("IGetHero_Hero_Human", impl.Name));

                Assert.Collection(
                    type.Fields,
                    field => Assert.Equal("Name", field.Name));
            });
        }
        private static void RegisterType(
            ClientModel model,
            IMapperContext context,
            Dictionary <NameString, TypeDescriptorModel> typeDescriptors,
            OutputTypeModel outputType,
            List <UnionType> unionTypes,
            TypeKind?kind = null,
            OperationModel?operationModel = null)
        {
            if (!typeDescriptors.TryGetValue(
                    outputType.Name,
                    out TypeDescriptorModel descriptorModel))
            {
                IReadOnlyList <NamedTypeDescriptor> implementedBy =
                    Array.Empty <NamedTypeDescriptor>();

                if (operationModel is not null && outputType.IsInterface)
                {
                    var classes = new HashSet <NamedTypeDescriptor>();
                    CollectClassesThatImplementInterface(
                        operationModel,
                        outputType,
                        typeDescriptors,
                        classes);
                    implementedBy = classes.ToList();
                }

                TypeKind fallbackKind;
                string?  complexDataTypeParent = null;
                if (outputType.Type.IsEntity())
                {
                    fallbackKind = TypeKind.EntityType;
                }
                else
                {
                    if (outputType.Type.IsAbstractType())
                    {
                        fallbackKind          = TypeKind.ComplexDataType;
                        complexDataTypeParent = outputType.Type.Name;
                    }
                    else
                    {
                        OutputTypeModel?mostAbstractTypeModel = outputType;
                        while (mostAbstractTypeModel.Implements.Count > 0)
                        {
                            mostAbstractTypeModel = mostAbstractTypeModel.Implements[0];
                        }

                        complexDataTypeParent = mostAbstractTypeModel.Type.Name;

                        if (complexDataTypeParent == outputType.Type.Name)
                        {
                            fallbackKind = TypeKind.DataType;
                        }
                        else
                        {
                            fallbackKind = TypeKind.ComplexDataType;
                        }
                    }
                }

                var finalKind = kind ?? fallbackKind;
                descriptorModel = new TypeDescriptorModel(
                    outputType,
                    new NamedTypeDescriptor(
                        finalKind == TypeKind.ResultType
                            ? NamingConventions.ResultRootTypeNameFromTypeName(outputType.Name)
                            : outputType.Name,
                        context.Namespace,
                        outputType.IsInterface,
                        outputType.Implements.Select(
                            t => finalKind == TypeKind.ResultType
                                ? (NameString)NamingConventions.ResultRootTypeNameFromTypeName(
                                t.Name)
                                : t.Name).ToList(),
                        kind: finalKind,
                        graphQLTypeName: outputType.Type.Name,
                        implementedBy: implementedBy,
                        complexDataTypeParent: complexDataTypeParent));

                typeDescriptors.Add(
                    outputType.Name,
                    descriptorModel);
            }
        }
        private OutputTypeModel AnalyzeWithHoistedFragment(
            IDocumentAnalyzerContext context,
            FieldSelection fieldSelection,
            SelectionSetVariants selectionVariants,
            string fragmentName)
        {
            FragmentNode?returnTypeFragment =
                FragmentHelper.CreateFragmentNode(
                    selectionVariants.Variants[0],
                    fieldSelection.Path,
                    appendTypeName: true);

            returnTypeFragment = FragmentHelper.GetFragment(returnTypeFragment, fragmentName);

            if (returnTypeFragment is null)
            {
                // TODO : throw helper
                throw new GraphQLException(
                          "The specified return fragment does not exist.");
            }

            OutputTypeModel returnType =
                FragmentHelper.CreateInterface(context, returnTypeFragment, fieldSelection.Path);

            context.RegisterSelectionSet(
                returnType.Type,
                selectionVariants.ReturnType.SyntaxNode,
                returnType.SelectionSet);

            foreach (SelectionSet selectionSet in selectionVariants.Variants)
            {
                returnTypeFragment = FragmentHelper.CreateFragmentNode(
                    selectionSet,
                    fieldSelection.Path,
                    appendTypeName: true);

                returnTypeFragment = FragmentHelper.RewriteForConcreteType(returnTypeFragment);

                if (FragmentHelper.GetFragment(returnTypeFragment, fragmentName) is null)
                {
                    // TODO : throw helper
                    throw new GraphQLException(
                              "The specified return fragment must be implement by all type fragments.");
                }

                OutputTypeModel @interface =
                    FragmentHelper.CreateInterface(
                        context,
                        returnTypeFragment,
                        fieldSelection.Path,
                        new[] { returnType });

                OutputTypeModel @class =
                    FragmentHelper.CreateClass(
                        context,
                        returnTypeFragment,
                        selectionSet,
                        @interface);

                context.RegisterSelectionSet(
                    selectionSet.Type,
                    selectionSet.SyntaxNode,
                    @class.SelectionSet);
            }

            return(returnType);
        }
        private OutputTypeModel AnalyzeWithHoistedFragment(
            IDocumentAnalyzerContext context,
            FieldSelection fieldSelection,
            SelectionSetVariants selectionVariants,
            string fragmentName)
        {
            FragmentNode?returnTypeFragment =
                FragmentHelper.CreateFragmentNode(
                    selectionVariants.Variants[0],
                    fieldSelection.Path,
                    appendTypeName: true);

            returnTypeFragment = FragmentHelper.GetFragment(returnTypeFragment, fragmentName);

            if (returnTypeFragment is null)
            {
                throw ThrowHelper.ReturnFragmentDoesNotExist();
            }

            OutputTypeModel returnType =
                FragmentHelper.CreateInterface(context, returnTypeFragment, fieldSelection.Path);

            context.RegisterSelectionSet(
                returnType.Type,
                selectionVariants.ReturnType.SyntaxNode,
                returnType.SelectionSet);

            foreach (SelectionSet selectionSet in selectionVariants.Variants)
            {
                returnTypeFragment = FragmentHelper.CreateFragmentNode(
                    selectionSet,
                    fieldSelection.Path,
                    appendTypeName: true);

                returnTypeFragment = FragmentHelper.RewriteForConcreteType(returnTypeFragment);

                if (FragmentHelper.GetFragment(returnTypeFragment, fragmentName) is null)
                {
                    throw ThrowHelper.FragmentMustBeImplementedByAllTypeFragments();
                }

                OutputTypeModel @interface =
                    FragmentHelper.CreateInterface(
                        context,
                        returnTypeFragment,
                        fieldSelection.Path,
                        new[] { returnType });

                OutputTypeModel @class =
                    FragmentHelper.CreateClass(
                        context,
                        returnTypeFragment,
                        selectionSet,
                        @interface);

                context.RegisterSelectionSet(
                    selectionSet.Type,
                    selectionSet.SyntaxNode,
                    @class.SelectionSet);
            }

            return(returnType);
        }