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);
        }
        public async Task Union_With_Fragment_Definition_Two_Models()
        {
            // arrange
            var schema =
                await new ServiceCollection()
                .AddStarWarsRepositories()
                .AddGraphQL()
                .AddStarWars()
                .BuildSchemaAsync();

            var document =
                Utf8GraphQLParser.Parse(@"
                    query GetHero {
                        search(text: ""hello"") {
                            ... Hero
                            ... Starship
                        }
                    }

                    fragment Hero on Character {
                        name
                        ... Human
                        ... Droid
                    }

                    fragment Human on Human {
                        homePlanet
                    }

                    fragment Droid on Droid {
                        primaryFunction
                    }

                    fragment Starship on Starship {
                        length
                    }");

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

            selectionSetVariants = context.CollectFields(fieldSelection);

            // act
            var analyzer = new InterfaceTypeSelectionSetAnalyzer();
            var result   = analyzer.Analyze(context, fieldSelection, selectionSetVariants);

            // assert
            Assert.Equal("IGetHero_Search", result.Name);

            Assert.Collection(
                context.GetImplementations(result),
                model => Assert.Equal("IGetHero_Search_Starship", model.Name),
                model => Assert.Equal("IGetHero_Search_Human", model.Name),
                model => Assert.Equal("IGetHero_Search_Droid", model.Name));

            Assert.Empty(result.Fields);
        }
        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);
        }
        public async Task Interface_With_Default_Names_Two_Models()
        {
            // arrange
            var schema =
                await new ServiceCollection()
                .AddStarWarsRepositories()
                .AddGraphQL()
                .AddStarWars()
                .BuildSchemaAsync();

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

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

            selectionSetVariants = context.CollectFields(fieldSelection);

            // act
            var analyzer = new InterfaceTypeSelectionSetAnalyzer();
            var result   = analyzer.Analyze(context, fieldSelection, selectionSetVariants);

            // assert
            Assert.Equal("IGetHero_Hero", result.Name);

            Assert.Collection(
                context.GetImplementations(result),
                model => Assert.Equal("IGetHero_Hero_Human", model.Name),
                model => Assert.Equal("IGetHero_Hero_Droid", model.Name));

            Assert.Collection(
                result.Fields,
                field => Assert.Equal("Name", field.Name));
        }
        public async Task Interface_With_Fragment_Definition_One_Model()
        {
            // arrange
            var schema =
                await new ServiceCollection()
                .AddStarWarsRepositories()
                .AddGraphQL()
                .AddStarWars()
                .BuildSchemaAsync();

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

                    fragment Hero on Character {
                        name
                    }");

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

            selectionSetVariants = context.CollectFields(fieldSelection);

            // act
            var analyzer = new InterfaceTypeSelectionSetAnalyzer();
            var result   = analyzer.Analyze(context, fieldSelection, selectionSetVariants);

            // assert
            Assert.Equal("IGetHero_Hero", result.Name);

            Assert.Collection(
                context.GetImplementations(result).OrderBy(t => t.Name),
                model => Assert.Equal("IGetHero_Hero_Droid", model.Name),
                model => Assert.Equal("IGetHero_Hero_Human", model.Name));

            Assert.Empty(result.Fields);
        }
        public override OutputTypeModel Analyze(
            IDocumentAnalyzerContext context,
            FieldSelection fieldSelection,
            SelectionSetVariants selectionVariants)
        {
            var returnTypeFragmentName = FragmentHelper.GetReturnTypeName(fieldSelection);

            if (returnTypeFragmentName is null)
            {
                return(AnalyzeWithDefaults(
                           context,
                           fieldSelection,
                           selectionVariants));
            }

            return(AnalyzeWithHoistedFragment(
                       context,
                       fieldSelection,
                       selectionVariants,
                       returnTypeFragmentName));
        }
        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);
        }
 public abstract OutputTypeModel Analyze(
     IDocumentAnalyzerContext context,
     FieldSelection fieldSelection,
     SelectionSetVariants selectionSetVariants);