public ContentPageGraphType(
            IDataLoaderContextAccessor dataLoaderContextAccessor,
            IMetaFieldServiceProvider metaFieldServiceProvider,
            IMetaFieldService metaFieldService,
            IMetaFieldLookupService metaFieldLookupService,
            IContentPageService contentPageService,
            IContentPageLookupService contentPageLookupService
            )
        {
            Name = "ContentPage";

            Field <StringGraphType, string>()
            .Name("Id")
            .Description("Globally unique identifier, eg: gid://ContentPage/1000")
            .Resolve(ctx => ctx.Source.Id);

            Field <StringGraphType, string>()
            .Name("ParentId")
            .Description("Globally unique identifier of parent, 'gid://' if none")
            .Resolve(ctx => ctx.Source.ParentId);

            Field <StringGraphType, string>()
            .Name("Handle")
            .Description("A human-friendly unique string for the content page")
            .Resolve(ctx =>
            {
                var userContext = (StorefrontGraphUserContext)ctx.UserContext;

                return(ctx.Source.Handles
                       .FirstOrDefault(x => x.LanguageCode == userContext.LanguageCode)
                       ?.Value);
            });

            Field <StringGraphType, string>()
            .Name("Title")
            .Description("The title of the content page")
            .Resolve(ctx =>
            {
                var userContext = (StorefrontGraphUserContext)ctx.UserContext;

                return(ctx.Source.Titles
                       .FirstOrDefault(x => x.LanguageCode == userContext.LanguageCode)
                       ?.Value);
            });

            Field <StringGraphType, string>()
            .Name("Url")
            .Description("The url of the content page")
            .Resolve(ctx =>
            {
                var userContext = (StorefrontGraphUserContext)ctx.UserContext;

                return(ctx.Source.Urls
                       .FirstOrDefault(x => x.LanguageCode == userContext.LanguageCode)
                       ?.Value);
            });

            Field <StringGraphType, string>()
            .Name("Type")
            .Description("The type of the content page")
            .Resolve(ctx => ctx.Source.Type);

            Field <StringGraphType, string>()
            .Name("Description")
            .Description("The description of the content page")
            .Resolve(ctx =>
            {
                var userContext = (StorefrontGraphUserContext)ctx.UserContext;

                return(ctx.Source.Descriptions
                       .FirstOrDefault(x => x.LanguageCode == userContext.LanguageCode)
                       ?.Value);
            });

            Field <DateTimeGraphType, DateTime>()
            .Name("CreatedAt")
            .Description("The timestamp of content page creation")
            .Resolve(ctx => ctx.Source.CreatedAt.ToDateTime());

            Field <DateTimeGraphType, DateTime>()
            .Name("UpdatedAt")
            .Description("The timestamp of the latest content page update")
            .Resolve(ctx => ctx.Source.UpdatedAt.ToDateTime());

            Field <ImageGraphType, Image>()
            .Name("PrimaryImage")
            .Description("The primary image of the content page")
            .Resolve(ctx => ctx.Source.PrimaryImage);

            #region Meta-fields

            Field <MetaFieldGraphType, MetaField>()
            .Name("MetaField")
            .Description("Connection to a specific meta-field")
            .Argument <NonNullGraphType <StringGraphType> >("namespace", "Namespace of the meta-field")
            .Argument <NonNullGraphType <StringGraphType> >("name", "Name of the meta-field")
            .ResolveAsync(async ctx =>
            {
                if (!metaFieldServiceProvider.IsEnabled)
                {
                    throw new ExecutionError("Meta-fields not supported.");
                }

                var result = await metaFieldService.GetBySearchAsync(
                    ctx.Source.Id,
                    ctx.GetArgument <string>("namespace"),
                    ctx.GetArgument <string>("name"));

                return(result.FirstOrDefault());
            });

            Field <ListGraphType <MetaFieldGraphType>, IList <MetaField> >()
            .Name("MetaFields")
            .Description("Connection to a all meta-fields")
            .ResolveAsync(ctx =>
            {
                if (!metaFieldServiceProvider.IsEnabled)
                {
                    throw new ExecutionError("Meta-fields not supported.");
                }

                var loader = dataLoaderContextAccessor.Context
                             .GetOrAddBatchLoader <string, IList <MetaField> >("MetaField.LookupByParentIdsAsync", metaFieldLookupService.LookupByParentIdsAsync);
                return(loader.LoadAsync(ctx.Source.Id));
            });

            #endregion Meta-fields

            #region Content pages

            Field <ContentPageGraphType, ContentPage>()
            .Name("Parent")
            .ResolveAsync(ctx =>
            {
                if (string.IsNullOrEmpty(ctx.Source.ParentId))
                {
                    return(null);
                }

                var loader = dataLoaderContextAccessor.Context
                             .GetOrAddBatchLoader <string, ContentPage>("ContentPage.LookupByIdAsync", contentPageLookupService.LookupByIdAsync);
                return(loader.LoadAsync(ctx.Source.ParentId));
            });
            Connection <ContentPageGraphType>()
            .Name("Children")
            .Unidirectional()
            .Argument <StringGraphType>("query", "The search query to filter children by")
            .Argument <ContentPageSortKeyGraphType>("sortKey", "The key to sort the underlying list by")
            .Argument <StringGraphType>("reverse", "Reverse the order of the underlying list")
            .ResolveAsync(async ctx =>
            {
                var userContext = (StorefrontGraphUserContext)ctx.UserContext;

                var result = await contentPageService.GetBySearchAsync(
                    ctx.GetArgument <string>("query"),
                    userContext.LanguageCode,
                    ctx.Source.Id,
                    ctx.GetArgument <string>("after"),
                    ctx.GetArgument <int>("first", 24),
                    ctx.GetArgument <ContentPageSortKey>("sortKey"),
                    ctx.GetArgument <bool>("reverse"));

                return(result.ToGraphConnection());
            });

            #endregion Content pages
        }
        public StorefrontGraphQuery(
            IDataLoaderContextAccessor dataLoaderContextAccessor,
            ILanguageProvider languageProvider,
            ICurrencyProvider currencyProvider,
            IContentPageServiceProvider contentPageServiceProvider,
            INavigationServiceProvider navigationServiceProvider,
            ICategoryServiceProvider categoryServiceProvider,
            IProductServiceProvider productServiceProvider,
            IContentPageService contentPageService,
            IContentPageLookupService contentPageLookupService,
            INavigationLookupService navigationLookupService,
            ICategoryService categoryService,
            ICategoryLookupService categoryLookupService,
            IProductService productService,
            IProductLookupService productLookupService)
        {
            Name = "Query";

            #region Languages

            Field <ListGraphType <LanguageGraphType>, IList <string> >()
            .Name("Languages")
            .Description("The languages supported")
            .Resolve(ctx => languageProvider.Languages);

            #endregion Languages

            #region Currencies

            Field <ListGraphType <CurrencyGraphType>, IList <NodaMoney.Currency> >()
            .Name("Currencies")
            .Description("The currencies supported")
            .Resolve(ctx => currencyProvider.Currencies
                     .Select(NodaMoney.Currency.FromCode)
                     .ToList());

            #endregion Currencies

            #region Content Pages

            Field <ContentPageGraphType, ContentPage>()
            .Name("ContentPage")
            .Argument <StringGraphType>("id", "Id of the content page")
            .Argument <StringGraphType>("handle", "Handle of the content page")
            .ResolveAsync(ctx =>
            {
                if (!contentPageServiceProvider.IsEnabled)
                {
                    throw new ExecutionError("Content pages not supported.");
                }

                if (ctx.HasArgument("id"))
                {
                    var loader = dataLoaderContextAccessor.Context
                                 .GetOrAddBatchLoader <string, ContentPage>("ContentPage.LookupByIdAsync", contentPageLookupService.LookupByIdAsync);
                    return(loader.LoadAsync(ctx.GetArgument <string>("id")));
                }

                if (ctx.HasArgument("handle"))
                {
                    var userContext = (StorefrontGraphUserContext)ctx.UserContext;

                    var loader = dataLoaderContextAccessor.Context
                                 .GetOrAddBatchLoader <string, ContentPage>("ContentPage.LookupByHandleAsync", handles =>
                                                                            contentPageLookupService.LookupByHandleAsync(handles, userContext.LanguageCode));
                    return(loader.LoadAsync(ctx.GetArgument <string>("handle")));
                }

                return(null);
            });

            Connection <ContentPageGraphType>()
            .Name("ContentPages")
            .Unidirectional()
            .Argument <StringGraphType>("query", "The search query to filter results by")
            .Argument <ContentPageSortKeyGraphType>("sortKey", "The key to sort the underlying list by")
            .Argument <StringGraphType>("reverse", "Reverse the order of the underlying list")
            .ResolveAsync(async ctx =>
            {
                if (!contentPageServiceProvider.IsEnabled)
                {
                    throw new ExecutionError("Content pages not supported.");
                }

                var userContext = (StorefrontGraphUserContext)ctx.UserContext;

                var result = await contentPageService.GetBySearchAsync(
                    ctx.GetArgument <string>("query"),
                    userContext.LanguageCode,
                    null,
                    ctx.GetArgument <string>("after"),
                    ctx.GetArgument <int>("first", 24),
                    ctx.GetArgument <ContentPageSortKey>("sortKey"),
                    ctx.GetArgument <bool>("reverse"));

                return(result.ToGraphConnection());
            });

            #endregion Content Pages

            #region Navigations

            Field <NavigationGraphType, Navigation>()
            .Name("Navigation")
            .Argument <StringGraphType>("id", "Id of the navigation")
            .Argument <StringGraphType>("handle", "Handle of the navigation")
            .ResolveAsync(ctx =>
            {
                if (!navigationServiceProvider.IsEnabled)
                {
                    throw new ExecutionError("Navigations not supported.");
                }

                if (ctx.HasArgument("id"))
                {
                    var loader = dataLoaderContextAccessor.Context
                                 .GetOrAddBatchLoader <string, Navigation>("Navigation.LookupByIdAsync", navigationLookupService.LookupByIdAsync);
                    return(loader.LoadAsync(ctx.GetArgument <string>("id")));
                }

                if (ctx.HasArgument("handle"))
                {
                    var userContext = (StorefrontGraphUserContext)ctx.UserContext;

                    var loader = dataLoaderContextAccessor.Context
                                 .GetOrAddBatchLoader <string, Navigation>("Navigation.LookupByHandleAsync", handles =>
                                                                           navigationLookupService.LookupByHandleAsync(handles, userContext.LanguageCode));
                    return(loader.LoadAsync(ctx.GetArgument <string>("handle")));
                }

                return(null);
            });

            #endregion Navigations

            #region Categories

            Field <CategoryGraphType, Category>()
            .Name("Category")
            .Argument <StringGraphType>("id", "Id of the category")
            .Argument <StringGraphType>("handle", "Handle of the category")
            .ResolveAsync(ctx =>
            {
                if (!categoryServiceProvider.IsEnabled)
                {
                    throw new ExecutionError("Categories not supported.");
                }

                if (ctx.HasArgument("id"))
                {
                    var loader = dataLoaderContextAccessor.Context
                                 .GetOrAddBatchLoader <string, Category>("Category.LookupByIdAsync", categoryLookupService.LookupByIdAsync);
                    return(loader.LoadAsync(ctx.GetArgument <string>("id")));
                }

                if (ctx.HasArgument("handle"))
                {
                    var userContext = (StorefrontGraphUserContext)ctx.UserContext;

                    var loader = dataLoaderContextAccessor.Context
                                 .GetOrAddBatchLoader <string, Category>("Category.LookupByHandleAsync", handles =>
                                                                         categoryLookupService.LookupByHandleAsync(handles, userContext.LanguageCode));
                    return(loader.LoadAsync(ctx.GetArgument <string>("handle")));
                }

                return(null);
            });

            Connection <CategoryGraphType>()
            .Name("Categories")
            .Unidirectional()
            .Argument <StringGraphType>("query", "The search query to filter results by")
            .Argument <CategorySortKeyGraphType>("sortKey", "The key to sort the underlying list by")
            .Argument <StringGraphType>("reverse", "Reverse the order of the underlying list")
            .ResolveAsync(async ctx =>
            {
                if (!categoryServiceProvider.IsEnabled)
                {
                    throw new ExecutionError("Categories not supported.");
                }

                var userContext = (StorefrontGraphUserContext)ctx.UserContext;

                var result = await categoryService.GetBySearchAsync(
                    ctx.GetArgument <string>("query"),
                    userContext.LanguageCode,
                    null,
                    ctx.GetArgument <string>("after"),
                    ctx.GetArgument <int>("first", 24),
                    ctx.GetArgument <CategorySortKey>("sortKey"),
                    ctx.GetArgument <bool>("reverse"));

                return(result.ToGraphConnection());
            });

            #endregion Categories

            #region Products

            Field <ProductGraphType, Product>()
            .Name("Product")
            .Argument <StringGraphType>("id", "Id of the category")
            .Argument <StringGraphType>("handle", "Handle of the category")
            .ResolveAsync(ctx =>
            {
                if (!productServiceProvider.IsEnabled)
                {
                    throw new ExecutionError("Products not supported.");
                }

                if (ctx.HasArgument("id"))
                {
                    var loader = dataLoaderContextAccessor.Context
                                 .GetOrAddBatchLoader <string, Product>("Product.LookupByIdAsync", productLookupService.LookupByIdAsync);
                    return(loader.LoadAsync(ctx.GetArgument <string>("id")));
                }

                if (ctx.HasArgument("handle"))
                {
                    var userContext = (StorefrontGraphUserContext)ctx.UserContext;

                    var loader = dataLoaderContextAccessor.Context
                                 .GetOrAddBatchLoader <string, Product>("Product.LookupByHandleAsync", handles =>
                                                                        productLookupService.LookupByHandleAsync(handles, userContext.LanguageCode));
                    return(loader.LoadAsync(ctx.GetArgument <string>("handle")));
                }

                return(null);
            });

            Connection <ProductGraphType>()
            .Name("Products")
            .Unidirectional()
            .Argument <StringGraphType>("query", "The search query to filter results by")
            .Argument <ProductSortKeyGraphType>("sortKey", "The key to sort the underlying list by")
            .Argument <StringGraphType>("reverse", "Reverse the order of the underlying list")
            .ResolveAsync(async ctx =>
            {
                if (!productServiceProvider.IsEnabled)
                {
                    throw new ExecutionError("Products not supported.");
                }

                var userContext = (StorefrontGraphUserContext)ctx.UserContext;

                var result = await productService.GetBySearchAsync(
                    ctx.GetArgument <string>("query"),
                    userContext.LanguageCode,
                    null,
                    ctx.GetArgument <string>("after"),
                    ctx.GetArgument <int>("first", 24),
                    ctx.GetArgument <ProductSortKey>("sortKey"),
                    ctx.GetArgument <bool>("reverse"),
                    userContext.CurrencyCode);

                return(result.ToGraphConnection());
            });

            #endregion Products
        }