Repository: JosephWoodward/graphiql-dotnet Branch: master Commit: 183de6e51968 Files: 38 Total size: 84.6 KB Directory structure: gitextract_vo8n6p94/ ├── .github/ │ └── workflows/ │ └── dotnetcore.yml ├── .gitignore ├── GraphiQl.sln ├── LICENSE ├── README.md ├── assets/ │ └── logo.psd ├── package.sh ├── src/ │ └── GraphiQl/ │ ├── AssetProvider.cs │ ├── GraphiQl.csproj │ ├── GraphiQlExtensions.cs │ ├── GraphiQlMiddleware.cs │ ├── GraphiQlOptions.cs │ └── assets/ │ ├── graphiql.css │ └── index.html └── tests/ ├── GraphiQl.Demo/ │ ├── .bowerrc │ ├── Controllers/ │ │ └── GraphQlController.cs │ ├── GraphQl/ │ │ ├── Droid.cs │ │ ├── GraphQlQuery.cs │ │ └── Models/ │ │ ├── DroidType.cs │ │ └── StarWarsQuery.cs │ ├── GraphiQl.Demo.csproj │ ├── Program.cs │ ├── Properties/ │ │ └── launchSettings.json │ ├── Startup.cs │ ├── appsettings.Development.json │ └── appsettings.json └── GraphiQl.Tests/ ├── AuthenticationTest/ │ ├── ConfigureOptionsSetup.cs │ └── DelegateSetup.cs ├── BasicTest.cs ├── Fixtures/ │ ├── GraphQlFixture.cs │ └── HostFixture.cs ├── GraphiQl.Tests.csproj ├── OverrideGraphQlPathTests/ │ ├── ConfigureOptionsSetup.cs │ └── DelegateSetup.cs ├── OverrideGraphiQlPathTests/ │ ├── ConfigureOptionsSetup.cs │ └── DelegateSetup.cs ├── SeleniumTest.cs └── xunit.runner.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/dotnetcore.yml ================================================ name: .NET Core on: [push, pull_request] jobs: build: name: Github Actions Build # runs-on: ubuntu-latest runs-on: windows-latest steps: - uses: actions/checkout@v1 - name: Setup .NET Core uses: actions/setup-dotnet@v1 with: dotnet-version: 3.1.100 - name: Build Reason run: echo ${{github.ref}} and ${{github.event_name}} - name: Build with dotnet run: dotnet build --configuration Release --nologo - name: Run tests run: dotnet test tests/GraphiQl.Tests/ --configuration Release --nologo # - if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || contains(github.ref, 'tags')) # name: Push CI # run: ./push.sh ${{ secrets.FEEDZ_KEY }} env: DOTNET_CLI_TELEMETRY_OPTOUT: 1 ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # User-specific files *.suo *.user *.sln.docstates .vs .vscode .idea # Build results [Dd]ebug/ [Rr]elease/ x64/ build/ package/ [Bb]in/ [Oo]bj/ #dist/ # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets !packages/*/build/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* *_i.c *_p.c *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.log *.scc # Visual C++ cache files ipch/ *.aps *.ncb *.opensdf *.sdf *.cachefile # Visual Studio profiler *.psess *.vsp *.vspx # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # NCrunch *.ncrunch* .*crunch*.local.xml # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.Publish.xml # NuGet Packages Directory packages/ !packages/repositories.config # Windows Azure Build Output csx *.build.csdef # Windows Store app package directory AppPackages/ # Others sql/ *.Cache ClientBin/ [Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.[Pp]ublish.xml *.pfx *.publishsettings node_modules/ bower_components/ tmp/ # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file to a newer # Visual Studio version. Backup files are not needed, because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files App_Data/*.mdf App_Data/*.ldf #LightSwitch generated files GeneratedArtifacts/ _Pvt_Extensions/ ModelManifest.xml # ========================= # Windows detritus # ========================= # Windows image file caches Thumbs.db ehthumbs.db # Folder config file Desktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Mac desktop service store files .DS_Store bower_components/ node_modules/ **/wwwroot/lib/ **/wwwroot/img/media/ .settings **/PublishProfiles/ **/PublishScripts/ # Angular 2 # compiled output AdminApp/dist AdminApp/tmp # dependencies AdminApp/node_modules AdminApp/bower_components # IDEs and editors AdminApp/.idea # misc AdminApp/.sass-cache AdminApp/connect.lock AdminApp/coverage/* AdminApp/libpeerconnection.log AdminApp/AdminApp/npm-debug.log AdminApp/testem.log AdminApp/typings # e2e AdminApp/e2e/*.js AdminApp/e2e/*.map #System Files .DS_Store Thumbs.db ================================================ FILE: GraphiQl.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26124.0 MinimumVisualStudioVersion = 15.0.26124.0 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphiQl", "src\GraphiQl\GraphiQl.csproj", "{7DECE8E2-141A-4A14-A460-A7985C3AA2E8}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphiQl.Tests", "tests\GraphiQl.Tests\GraphiQl.Tests.csproj", "{0C2E2EB0-8FA2-4AB9-8642-72E50A830DBA}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GraphiQl.Demo", "tests\GraphiQl.Demo\GraphiQl.Demo.csproj", "{6482AE1A-896B-45A4-93D2-1DEA62120365}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7DECE8E2-141A-4A14-A460-A7985C3AA2E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7DECE8E2-141A-4A14-A460-A7985C3AA2E8}.Debug|Any CPU.Build.0 = Debug|Any CPU {7DECE8E2-141A-4A14-A460-A7985C3AA2E8}.Debug|x64.ActiveCfg = Debug|Any CPU {7DECE8E2-141A-4A14-A460-A7985C3AA2E8}.Debug|x64.Build.0 = Debug|Any CPU {7DECE8E2-141A-4A14-A460-A7985C3AA2E8}.Debug|x86.ActiveCfg = Debug|Any CPU {7DECE8E2-141A-4A14-A460-A7985C3AA2E8}.Debug|x86.Build.0 = Debug|Any CPU {7DECE8E2-141A-4A14-A460-A7985C3AA2E8}.Release|Any CPU.ActiveCfg = Release|Any CPU {7DECE8E2-141A-4A14-A460-A7985C3AA2E8}.Release|Any CPU.Build.0 = Release|Any CPU {7DECE8E2-141A-4A14-A460-A7985C3AA2E8}.Release|x64.ActiveCfg = Release|Any CPU {7DECE8E2-141A-4A14-A460-A7985C3AA2E8}.Release|x64.Build.0 = Release|Any CPU {7DECE8E2-141A-4A14-A460-A7985C3AA2E8}.Release|x86.ActiveCfg = Release|Any CPU {7DECE8E2-141A-4A14-A460-A7985C3AA2E8}.Release|x86.Build.0 = Release|Any CPU {0C2E2EB0-8FA2-4AB9-8642-72E50A830DBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0C2E2EB0-8FA2-4AB9-8642-72E50A830DBA}.Debug|Any CPU.Build.0 = Debug|Any CPU {0C2E2EB0-8FA2-4AB9-8642-72E50A830DBA}.Debug|x64.ActiveCfg = Debug|Any CPU {0C2E2EB0-8FA2-4AB9-8642-72E50A830DBA}.Debug|x64.Build.0 = Debug|Any CPU {0C2E2EB0-8FA2-4AB9-8642-72E50A830DBA}.Debug|x86.ActiveCfg = Debug|Any CPU {0C2E2EB0-8FA2-4AB9-8642-72E50A830DBA}.Debug|x86.Build.0 = Debug|Any CPU {0C2E2EB0-8FA2-4AB9-8642-72E50A830DBA}.Release|Any CPU.ActiveCfg = Release|Any CPU {0C2E2EB0-8FA2-4AB9-8642-72E50A830DBA}.Release|Any CPU.Build.0 = Release|Any CPU {0C2E2EB0-8FA2-4AB9-8642-72E50A830DBA}.Release|x64.ActiveCfg = Release|Any CPU {0C2E2EB0-8FA2-4AB9-8642-72E50A830DBA}.Release|x64.Build.0 = Release|Any CPU {0C2E2EB0-8FA2-4AB9-8642-72E50A830DBA}.Release|x86.ActiveCfg = Release|Any CPU {0C2E2EB0-8FA2-4AB9-8642-72E50A830DBA}.Release|x86.Build.0 = Release|Any CPU {6482AE1A-896B-45A4-93D2-1DEA62120365}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6482AE1A-896B-45A4-93D2-1DEA62120365}.Debug|Any CPU.Build.0 = Debug|Any CPU {6482AE1A-896B-45A4-93D2-1DEA62120365}.Debug|x64.ActiveCfg = Debug|Any CPU {6482AE1A-896B-45A4-93D2-1DEA62120365}.Debug|x64.Build.0 = Debug|Any CPU {6482AE1A-896B-45A4-93D2-1DEA62120365}.Debug|x86.ActiveCfg = Debug|Any CPU {6482AE1A-896B-45A4-93D2-1DEA62120365}.Debug|x86.Build.0 = Debug|Any CPU {6482AE1A-896B-45A4-93D2-1DEA62120365}.Release|Any CPU.ActiveCfg = Release|Any CPU {6482AE1A-896B-45A4-93D2-1DEA62120365}.Release|Any CPU.Build.0 = Release|Any CPU {6482AE1A-896B-45A4-93D2-1DEA62120365}.Release|x64.ActiveCfg = Release|Any CPU {6482AE1A-896B-45A4-93D2-1DEA62120365}.Release|x64.Build.0 = Release|Any CPU {6482AE1A-896B-45A4-93D2-1DEA62120365}.Release|x86.ActiveCfg = Release|Any CPU {6482AE1A-896B-45A4-93D2-1DEA62120365}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2017 Joseph Woodward Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ ![](https://raw.githubusercontent.com/JosephWoodward/graphiql-dotnet/master/assets/logo_128_128.png) # GraphiQL.NET ![.NET Core](https://github.com/JosephWoodward/graphiql-dotnet/workflows/.NET%20Core/badge.svg) GraphiQL middleware for ASP.NET Core - try the [live demo here](http://graphql.org/swapi-graphql/). ## What is GraphiQL.NET? GraphiQL.NET is a piece of .NET Core middleware that bundles graphiql into it saving you from managing additional frontend dependencies, whilst also giving you control over the routes it's avaialble on any provide you with a means of authentication. GraphiQL.NET features include: - The full GraphiQl experience - Customisation of GraphiQL routes - Authentication ![GraphiQL for ASP.NET Core](https://raw.githubusercontent.com/JosephWoodward/graphiql-dotnet/master/assets/screenshot.png) ## Setup The GraphiQL.NET middleware can be [found on NuGet here](https://www.nuget.org/packages/graphiql/) You can install GraphiQL.NET by copying and pasting the following command into your Package Manager Console within Visual Studio (Tools > NuGet Package Manager > Package Manager Console). ``` Install-Package graphiql ``` Alternatively you can install it using the .NET Core CLI using the following command: ``` dotnet add package graphiql ``` ## Getting Started Once installed you can add GraphiQL.NET to your ASP.NET Core application by adding the `app.UseGraphiQl()` middleware to the `Configure` method within your `Startup.cs` file. **Note: Be sure to call `UseGraphiQl()` before `UseMvc()`.** ```csharp public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { // Adding this makes graphiql UI available at /graphql app.UseGraphiQl(); app.UseMvc(); } ``` ## Configuration --- ### Configure Graphiql route By default GraphiQL lives on the `/graphql` endpoint, however this can be changed by passing your chosen path to the `app.UseGraphiQl();` entry point method: ```csharp app.UseGraphiQl('/whatever/graphiql'); ``` ### Configure Graphql API address You can also specify GraphiQl endpoint independent of your GraphQL API, this is especially useful if you're hosting in IIS in a virtual application (ie `myapp.com/1.0/...`) or hosting API and documentation separately. ```csharp public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { app.UseGraphiQl("/graphql", "/v1/yourapi"); app.UseMvc(); } ``` Now navigating to `/graphql` will display the GraphiQL UI, but your GraphQL API will live under the `/v1/yourapi` route. ### Configuration via `IServiceCollection` Alternatively you can configure the aforementioned routes via `IServiceCollection` within `ConfigureServices` or your `Startup.cs` file: ```csharp //Startup.cs public void ConfigureServices(IServiceCollection services) { ... services.AddGraphiQl(x => { x.GraphiQlPath = "/graphiql-ui"; x.GraphQlApiPath = "graphql"; }); ... } public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) { app.UseGraphiQl(); ... } ``` ### Configuration via `ConfigureOptions` You can also use the `IConfigureOptions` interface: ```csharp // GraphiQlTestOptionsSetup.cs internal class GraphiQlTestOptionsSetup : IConfigureOptions { public void Configure(GraphiQlOptions options) { options.GraphiQlPath = "/graphiql-ui"; options.GraphQlApiPath = "graphql"; } } ``` Then you just have to register it with your Ioc Container: ```csharp //Startup.cs public void ConfigureServices(IServiceCollection services) { ... services.AddTransient, GraphiQlTestOptionsSetup>();z ... } --- ================================================ FILE: package.sh ================================================ #!/usr/bin/env bash rm -rf ./package dotnet pack ./src/GraphiQl/GraphiQl.csproj -o ./package/ -c release ================================================ FILE: src/GraphiQl/AssetProvider.cs ================================================ using System; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Primitives; namespace GraphiQl { internal class AssetProvider : IFileProvider { private readonly EmbeddedFileProvider _provider; public AssetProvider(EmbeddedFileProvider provider) => _provider = provider; public IFileInfo GetFileInfo(string subpath) { // Embedded resource renames hypphens in directory to underscore, this tells file provider where to find it. if (subpath.Equals("/es6-promise/es6-promise.min.js", StringComparison.OrdinalIgnoreCase)) { subpath = "/es6_promise/es6-promise.min.js"; } return _provider.GetFileInfo(subpath); } public IDirectoryContents GetDirectoryContents(string subpath) => _provider.GetDirectoryContents(subpath); public IChangeToken Watch(string filter) => _provider.Watch(filter); } } ================================================ FILE: src/GraphiQl/GraphiQl.csproj ================================================  GraphiQL GraphiQL graphql,graphiql,asp.net core,.net core GraphiQL ASP.NET Core Middleware netstandard2.0;netcoreapp3.0;netcoreapp3.1 JosephWoodward GraphiQL available as middleware for ASP.NET Core https://raw.githubusercontent.com/JosephWoodward/graphiql-dotnet/master/assets/logo_128_128.png https://github.com/JosephWoodward/graphiql-dotnet MIT $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb logo_128_128.png true true diag v Library true all runtime; build; native; contentfiles; analyzers ================================================ FILE: src/GraphiQl/GraphiQlExtensions.cs ================================================ using System; using System.Reflection; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.StaticFiles; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.FileProviders; using Microsoft.Extensions.Options; namespace GraphiQl { public static class GraphiQlExtensions { /* public static IServiceCollection AddGraphiQl(this IServiceCollection services) => services.AddGraphiQl(null); */ public static IServiceCollection AddGraphiQl(this IServiceCollection services, Action configure) { if (configure != null) { services.Configure(configure); } services.TryAdd(ServiceDescriptor.Transient, GraphiQlOptionsSetup>()); return services; } public static IApplicationBuilder UseGraphiQl(this IApplicationBuilder app) => app.UseGraphiQl(null); public static IApplicationBuilder UseGraphiQl(this IApplicationBuilder app, string path) => app.UseGraphiQl(path, null); public static IApplicationBuilder UseGraphiQl(this IApplicationBuilder app, string path, string apiPath) => app.UseGraphiQlImp(path, apiPath); private static IApplicationBuilder UseGraphiQlImp(this IApplicationBuilder app, string path, string apiPath ) { if (app == null) { throw new ArgumentNullException(nameof(app)); } var options = app.ApplicationServices.GetService>().Value; var graphiQlPath = !string.IsNullOrWhiteSpace(path) ? path : options.GraphiQlPath; var graphQlApiPath = !string.IsNullOrWhiteSpace(apiPath) ? apiPath : options.GraphQlApiPath; var fileServerOptions = new FileServerOptions { RequestPath = graphiQlPath, FileProvider = new AssetProvider(new EmbeddedFileProvider(typeof(GraphiQlExtensions).GetTypeInfo().Assembly, "GraphiQl.assets")), EnableDefaultFiles = true, StaticFileOptions = {ContentTypeProvider = new FileExtensionContentTypeProvider()} }; app.UseMiddleware(); app.Map($"{graphiQlPath.TrimEnd('/')}/graphql-path.js", x => WritePathJavaScript(x, graphQlApiPath)); app.UseFileServer(fileServerOptions); return app; } private static void WritePathJavaScript(IApplicationBuilder app, string path) => app.Run(h => { h.Response.ContentType = "application/javascript"; return h.Response.WriteAsync($"var graphqlPath='{path}';"); }); } } ================================================ FILE: src/GraphiQl/GraphiQlMiddleware.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; namespace GraphiQl { public class GraphiQlMiddleware { private readonly RequestDelegate _next; private readonly GraphiQlOptions _options; public GraphiQlMiddleware(RequestDelegate next, IOptions options) { _next = next; _options = options.Value; } public async Task Invoke(HttpContext context) { if (context.Request.Path.Equals(_options.GraphiQlPath, StringComparison.OrdinalIgnoreCase) && _options.IsAuthenticated != null && !await _options.IsAuthenticated.Invoke(context)) { return; } await _next(context); } } } ================================================ FILE: src/GraphiQl/GraphiQlOptions.cs ================================================ using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; namespace GraphiQl { public class GraphiQlOptions { public string GraphiQlPath { get; set; } public string GraphQlApiPath { get; set; } public Func> IsAuthenticated { get; set; } public GraphiQlOptions() { GraphiQlPath = "/graphql"; GraphQlApiPath = "/graphql"; } } public class GraphiQlOptionsSetup : IConfigureOptions { public void Configure(GraphiQlOptions options) { if (options.GraphiQlPath == null) { options.GraphiQlPath = "/graphql"; } if (options.GraphQlApiPath == null) { options.GraphQlApiPath = "/graphql"; } } } } ================================================ FILE: src/GraphiQl/assets/graphiql.css ================================================ .graphiql-container, .graphiql-container button, .graphiql-container input { color: #141823; font-family: system, -apple-system, 'San Francisco', '.SFNSDisplay-Regular', 'Segoe UI', Segoe, 'Segoe WP', 'Helvetica Neue', helvetica, 'Lucida Grande', arial, sans-serif; font-size: 14px; } .graphiql-container { display: flex; flex-direction: row; height: 100%; margin: 0; overflow: hidden; width: 100%; } .graphiql-container .editorWrap { display: flex; flex-direction: column; flex: 1; overflow-x: hidden; } .graphiql-container .title { font-size: 18px; } .graphiql-container .title em { font-family: georgia; font-size: 19px; } .graphiql-container .topBarWrap { display: flex; flex-direction: row; } .graphiql-container .topBar { align-items: center; background: linear-gradient(#f7f7f7, #e2e2e2); border-bottom: 1px solid #d0d0d0; cursor: default; display: flex; flex-direction: row; flex: 1; height: 34px; overflow-y: visible; padding: 7px 14px 6px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .graphiql-container .toolbar { overflow-x: visible; display: flex; } .graphiql-container .docExplorerShow, .graphiql-container .historyShow { background: linear-gradient(#f7f7f7, #e2e2e2); border-radius: 0; border-bottom: 1px solid #d0d0d0; border-right: none; border-top: none; color: #3B5998; cursor: pointer; font-size: 14px; margin: 0; padding: 2px 20px 0 18px; } .graphiql-container .docExplorerShow { border-left: 1px solid rgba(0, 0, 0, 0.2); } .graphiql-container .historyShow { border-right: 1px solid rgba(0, 0, 0, 0.2); border-left: 0; } .graphiql-container .docExplorerShow:before { border-left: 2px solid #3B5998; border-top: 2px solid #3B5998; content: ''; display: inline-block; height: 9px; margin: 0 3px -1px 0; position: relative; transform: rotate(-45deg); width: 9px; } .graphiql-container .editorBar { display: flex; flex-direction: row; flex: 1; } .graphiql-container .queryWrap { display: flex; flex-direction: column; flex: 1; } .graphiql-container .resultWrap { border-left: solid 1px #e0e0e0; display: flex; flex-direction: column; flex: 1; flex-basis: 1em; position: relative; } .graphiql-container .docExplorerWrap, .graphiql-container .historyPaneWrap { background: white; box-shadow: 0 0 8px rgba(0, 0, 0, 0.15); position: relative; z-index: 3; } .graphiql-container .historyPaneWrap { min-width: 230px; z-index: 5; } .graphiql-container .docExplorerResizer { cursor: col-resize; height: 100%; left: -5px; position: absolute; top: 0; width: 10px; z-index: 10; } .graphiql-container .docExplorerHide { cursor: pointer; font-size: 18px; margin: -7px -8px -6px 0; padding: 18px 16px 15px 12px; background: 0; border: 0; line-height: 14px; } .graphiql-container div .query-editor { flex: 1; position: relative; } .graphiql-container .variable-editor { display: flex; flex-direction: column; height: 30px; position: relative; } .graphiql-container .variable-editor-title { background: #eeeeee; border-bottom: 1px solid #d6d6d6; border-top: 1px solid #e0e0e0; color: #777; font-variant: small-caps; font-weight: bold; letter-spacing: 1px; line-height: 14px; padding: 6px 0 8px 43px; text-transform: lowercase; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .graphiql-container .codemirrorWrap { flex: 1; height: 100%; position: relative; } .graphiql-container .result-window { flex: 1; height: 100%; position: relative; } .graphiql-container .footer { background: #f6f7f8; border-left: 1px solid #e0e0e0; border-top: 1px solid #e0e0e0; margin-left: 12px; position: relative; } .graphiql-container .footer:before { background: #eeeeee; bottom: 0; content: " "; left: -13px; position: absolute; top: -1px; width: 12px; } /* No `.graphiql-container` here so themes can overwrite */ .result-window .CodeMirror { background: #f6f7f8; } .graphiql-container .result-window .CodeMirror-gutters { background-color: #eeeeee; border-color: #e0e0e0; cursor: col-resize; } .graphiql-container .result-window .CodeMirror-foldgutter, .graphiql-container .result-window .CodeMirror-foldgutter-open:after, .graphiql-container .result-window .CodeMirror-foldgutter-folded:after { padding-left: 3px; } .graphiql-container .toolbar-button { background: #fdfdfd; background: linear-gradient(#f9f9f9, #ececec); border: 0; border-radius: 3px; box-shadow: inset 0 0 0 1px rgba(0,0,0,0.20), 0 1px 0 rgba(255,255,255, 0.7), inset 0 1px #fff; color: #555; cursor: pointer; display: inline-block; margin: 0 5px; padding: 3px 11px 5px; text-decoration: none; text-overflow: ellipsis; white-space: nowrap; max-width: 150px; } .graphiql-container .toolbar-button:active { background: linear-gradient(#ececec, #d5d5d5); box-shadow: 0 1px 0 rgba(255, 255, 255, 0.7), inset 0 0 0 1px rgba(0,0,0,0.10), inset 0 1px 1px 1px rgba(0, 0, 0, 0.12), inset 0 0 5px rgba(0, 0, 0, 0.1); } .graphiql-container .toolbar-button.error { background: linear-gradient(#fdf3f3, #e6d6d7); color: #b00; } .graphiql-container .toolbar-button-group { margin: 0 5px; white-space: nowrap; } .graphiql-container .toolbar-button-group > * { margin: 0; } .graphiql-container .toolbar-button-group > *:not(:last-child) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .graphiql-container .toolbar-button-group > *:not(:first-child) { border-top-left-radius: 0; border-bottom-left-radius: 0; margin-left: -1px; } .graphiql-container .execute-button-wrap { height: 34px; margin: 0 14px 0 28px; position: relative; } .graphiql-container .execute-button { background: linear-gradient(#fdfdfd, #d2d3d6); border-radius: 17px; border: 1px solid rgba(0,0,0,0.25); box-shadow: 0 1px 0 #fff; cursor: pointer; fill: #444; height: 34px; margin: 0; padding: 0; width: 34px; } .graphiql-container .execute-button svg { pointer-events: none; } .graphiql-container .execute-button:active { background: linear-gradient(#e6e6e6, #c3c3c3); box-shadow: 0 1px 0 #fff, inset 0 0 2px rgba(0, 0, 0, 0.2), inset 0 0 6px rgba(0, 0, 0, 0.1); } .graphiql-container .toolbar-menu, .graphiql-container .toolbar-select { position: relative; } .graphiql-container .execute-options, .graphiql-container .toolbar-menu-items, .graphiql-container .toolbar-select-options { background: #fff; box-shadow: 0 0 0 1px rgba(0,0,0,0.1), 0 2px 4px rgba(0,0,0,0.25); margin: 0; padding: 6px 0; position: absolute; z-index: 100; } .graphiql-container .execute-options { min-width: 100px; top: 37px; left: -1px; } .graphiql-container .toolbar-menu-items { left: 1px; margin-top: -1px; min-width: 110%; top: 100%; visibility: hidden; } .graphiql-container .toolbar-menu-items.open { visibility: visible; } .graphiql-container .toolbar-select-options { left: 0; min-width: 100%; top: -5px; visibility: hidden; } .graphiql-container .toolbar-select-options.open { visibility: visible; } .graphiql-container .execute-options > li, .graphiql-container .toolbar-menu-items > li, .graphiql-container .toolbar-select-options > li { cursor: pointer; display: block; margin: none; max-width: 300px; overflow: hidden; padding: 2px 20px 4px 11px; white-space: nowrap; } .graphiql-container .execute-options > li.selected, .graphiql-container .toolbar-menu-items > li.hover, .graphiql-container .toolbar-menu-items > li:active, .graphiql-container .toolbar-menu-items > li:hover, .graphiql-container .toolbar-select-options > li.hover, .graphiql-container .toolbar-select-options > li:active, .graphiql-container .toolbar-select-options > li:hover, .graphiql-container .history-contents > li:hover, .graphiql-container .history-contents > li:active { background: #e10098; color: #fff; } .graphiql-container .toolbar-select-options > li > svg { display: inline; fill: #666; margin: 0 -6px 0 6px; pointer-events: none; vertical-align: middle; } .graphiql-container .toolbar-select-options > li.hover > svg, .graphiql-container .toolbar-select-options > li:active > svg, .graphiql-container .toolbar-select-options > li:hover > svg { fill: #fff; } .graphiql-container .CodeMirror-scroll { overflow-scrolling: touch; } .graphiql-container .CodeMirror { color: #141823; font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace; font-size: 13px; height: 100%; left: 0; position: absolute; top: 0; width: 100%; } .graphiql-container .CodeMirror-lines { padding: 20px 0; } .CodeMirror-hint-information .content { box-orient: vertical; color: #141823; display: flex; font-family: system, -apple-system, 'San Francisco', '.SFNSDisplay-Regular', 'Segoe UI', Segoe, 'Segoe WP', 'Helvetica Neue', helvetica, 'Lucida Grande', arial, sans-serif; font-size: 13px; line-clamp: 3; line-height: 16px; max-height: 48px; overflow: hidden; text-overflow: -o-ellipsis-lastline; } .CodeMirror-hint-information .content p:first-child { margin-top: 0; } .CodeMirror-hint-information .content p:last-child { margin-bottom: 0; } .CodeMirror-hint-information .infoType { color: #CA9800; cursor: pointer; display: inline; margin-right: 0.5em; } .autoInsertedLeaf.cm-property { animation-duration: 6s; animation-name: insertionFade; border-bottom: 2px solid rgba(255, 255, 255, 0); border-radius: 2px; margin: -2px -4px -1px; padding: 2px 4px 1px; } @keyframes insertionFade { from, to { background: rgba(255, 255, 255, 0); border-color: rgba(255, 255, 255, 0); } 15%, 85% { background: #fbffc9; border-color: #f0f3c0; } } div.CodeMirror-lint-tooltip { background-color: white; border-radius: 2px; border: 0; color: #141823; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); font-size: 13px; line-height: 16px; max-width: 430px; opacity: 0; padding: 8px 10px; transition: opacity 0.15s; white-space: pre-wrap; } div.CodeMirror-lint-tooltip > * { padding-left: 23px; } div.CodeMirror-lint-tooltip > * + * { margin-top: 12px; } /* COLORS */ .graphiql-container .CodeMirror-foldmarker { border-radius: 4px; background: #08f; background: linear-gradient(#43A8FF, #0F83E8); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), inset 0 0 0 1px rgba(0, 0, 0, 0.1); color: white; font-family: arial; font-size: 12px; line-height: 0; margin: 0 3px; padding: 0px 4px 1px; text-shadow: 0 -1px rgba(0, 0, 0, 0.1); } .graphiql-container div.CodeMirror span.CodeMirror-matchingbracket { color: #555; text-decoration: underline; } .graphiql-container div.CodeMirror span.CodeMirror-nonmatchingbracket { color: #f00; } /* Comment */ .cm-comment { color: #999; } /* Punctuation */ .cm-punctuation { color: #555; } /* Keyword */ .cm-keyword { color: #B11A04; } /* OperationName, FragmentName */ .cm-def { color: #D2054E; } /* FieldName */ .cm-property { color: #1F61A0; } /* FieldAlias */ .cm-qualifier { color: #1C92A9; } /* ArgumentName and ObjectFieldName */ .cm-attribute { color: #8B2BB9; } /* Number */ .cm-number { color: #2882F9; } /* String */ .cm-string { color: #D64292; } /* Boolean */ .cm-builtin { color: #D47509; } /* EnumValue */ .cm-string-2 { color: #0B7FC7; } /* Variable */ .cm-variable { color: #397D13; } /* Directive */ .cm-meta { color: #B33086; } /* Type */ .cm-atom { color: #CA9800; } /* BASICS */ .CodeMirror { /* Set height, width, borders, and global font properties here */ color: black; font-family: monospace; height: 300px; } /* PADDING */ .CodeMirror-lines { padding: 4px 0; /* Vertical padding around content */ } .CodeMirror pre { padding: 0 4px; /* Horizontal padding of content */ } .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { background-color: white; /* The little square between H and V scrollbars */ } /* GUTTER */ .CodeMirror-gutters { border-right: 1px solid #ddd; background-color: #f7f7f7; white-space: nowrap; } .CodeMirror-linenumbers {} .CodeMirror-linenumber { color: #999; min-width: 20px; padding: 0 3px 0 5px; text-align: right; white-space: nowrap; } .CodeMirror-guttermarker { color: black; } .CodeMirror-guttermarker-subtle { color: #999; } /* CURSOR */ .CodeMirror .CodeMirror-cursor { border-left: 1px solid black; } /* Shown when moving in bi-directional text */ .CodeMirror div.CodeMirror-secondarycursor { border-left: 1px solid silver; } .CodeMirror.cm-fat-cursor div.CodeMirror-cursor { background: #7e7; border: 0; width: auto; } .CodeMirror.cm-fat-cursor div.CodeMirror-cursors { z-index: 1; } .cm-animate-fat-cursor { animation: blink 1.06s steps(1) infinite; border: 0; width: auto; } @keyframes blink { 0% { background: #7e7; } 50% { background: none; } 100% { background: #7e7; } } /* Can style cursor different in overwrite (non-insert) mode */ div.CodeMirror-overwrite div.CodeMirror-cursor {} .cm-tab { display: inline-block; text-decoration: inherit; } .CodeMirror-ruler { border-left: 1px solid #ccc; position: absolute; } /* DEFAULT THEME */ .cm-s-default .cm-keyword {color: #708;} .cm-s-default .cm-atom {color: #219;} .cm-s-default .cm-number {color: #164;} .cm-s-default .cm-def {color: #00f;} .cm-s-default .cm-variable, .cm-s-default .cm-punctuation, .cm-s-default .cm-property, .cm-s-default .cm-operator {} .cm-s-default .cm-variable-2 {color: #05a;} .cm-s-default .cm-variable-3 {color: #085;} .cm-s-default .cm-comment {color: #a50;} .cm-s-default .cm-string {color: #a11;} .cm-s-default .cm-string-2 {color: #f50;} .cm-s-default .cm-meta {color: #555;} .cm-s-default .cm-qualifier {color: #555;} .cm-s-default .cm-builtin {color: #30a;} .cm-s-default .cm-bracket {color: #997;} .cm-s-default .cm-tag {color: #170;} .cm-s-default .cm-attribute {color: #00c;} .cm-s-default .cm-header {color: blue;} .cm-s-default .cm-quote {color: #090;} .cm-s-default .cm-hr {color: #999;} .cm-s-default .cm-link {color: #00c;} .cm-negative {color: #d44;} .cm-positive {color: #292;} .cm-header, .cm-strong {font-weight: bold;} .cm-em {font-style: italic;} .cm-link {text-decoration: underline;} .cm-strikethrough {text-decoration: line-through;} .cm-s-default .cm-error {color: #f00;} .cm-invalidchar {color: #f00;} .CodeMirror-composing { border-bottom: 2px solid; } /* Default styles for common addons */ div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } .CodeMirror-activeline-background {background: #e8f2ff;} /* STOP */ /* The rest of this file contains styles related to the mechanics of the editor. You probably shouldn't touch them. */ .CodeMirror { background: white; overflow: hidden; position: relative; } .CodeMirror-scroll { height: 100%; /* 30px is the magic margin used to hide the element's real scrollbars */ /* See overflow: hidden in .CodeMirror */ margin-bottom: -30px; margin-right: -30px; outline: none; /* Prevent dragging from highlighting the element */ overflow: scroll !important; /* Things will break if this is overridden */ padding-bottom: 30px; position: relative; } .CodeMirror-sizer { border-right: 30px solid transparent; position: relative; } /* The fake, visible scrollbars. Used to force redraw during scrolling before actual scrolling happens, thus preventing shaking and flickering artifacts. */ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { display: none; position: absolute; z-index: 6; } .CodeMirror-vscrollbar { overflow-x: hidden; overflow-y: scroll; right: 0; top: 0; } .CodeMirror-hscrollbar { bottom: 0; left: 0; overflow-x: scroll; overflow-y: hidden; } .CodeMirror-scrollbar-filler { right: 0; bottom: 0; } .CodeMirror-gutter-filler { left: 0; bottom: 0; } .CodeMirror-gutters { min-height: 100%; position: absolute; left: 0; top: 0; z-index: 3; } .CodeMirror-gutter { display: inline-block; height: 100%; margin-bottom: -30px; vertical-align: top; white-space: normal; /* Hack to make IE7 behave */ *zoom:1; *display:inline; } .CodeMirror-gutter-wrapper { background: none !important; border: none !important; position: absolute; z-index: 4; } .CodeMirror-gutter-background { position: absolute; top: 0; bottom: 0; z-index: 4; } .CodeMirror-gutter-elt { cursor: default; position: absolute; z-index: 4; } .CodeMirror-gutter-wrapper { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .CodeMirror-lines { cursor: text; min-height: 1px; /* prevents collapsing before first draw */ } .CodeMirror pre { -webkit-tap-highlight-color: transparent; /* Reset some styles that the rest of the page might have set */ background: transparent; border-radius: 0; border-width: 0; color: inherit; font-family: inherit; font-size: inherit; font-variant-ligatures: none; line-height: inherit; margin: 0; overflow: visible; position: relative; white-space: pre; word-wrap: normal; z-index: 2; } .CodeMirror-wrap pre { word-wrap: break-word; white-space: pre-wrap; word-break: normal; } .CodeMirror-linebackground { position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 0; } .CodeMirror-linewidget { overflow: auto; position: relative; z-index: 2; } .CodeMirror-widget {} .CodeMirror-code { outline: none; } /* Force content-box sizing for the elements where we expect it */ .CodeMirror-scroll, .CodeMirror-sizer, .CodeMirror-gutter, .CodeMirror-gutters, .CodeMirror-linenumber { box-sizing: content-box; } .CodeMirror-measure { height: 0; overflow: hidden; position: absolute; visibility: hidden; width: 100%; } .CodeMirror-cursor { position: absolute; } .CodeMirror-measure pre { position: static; } div.CodeMirror-cursors { position: relative; visibility: hidden; z-index: 3; } div.CodeMirror-dragcursors { visibility: visible; } .CodeMirror-focused div.CodeMirror-cursors { visibility: visible; } .CodeMirror-selected { background: #d9d9d9; } .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } .CodeMirror-crosshair { cursor: crosshair; } .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } .cm-searching { background: #ffa; background: rgba(255, 255, 0, .4); } /* IE7 hack to prevent it from returning funny offsetTops on the spans */ .CodeMirror span { *vertical-align: text-bottom; } /* Used to force a border model for a node */ .cm-force-border { padding-right: .1px; } @media print { /* Hide the cursor when printing */ .CodeMirror div.CodeMirror-cursors { visibility: hidden; } } /* See issue #2901 */ .cm-tab-wrap-hack:after { content: ''; } /* Help users use markselection to safely style text background */ span.CodeMirror-selectedtext { background: none; } .CodeMirror-dialog { background: inherit; color: inherit; left: 0; right: 0; overflow: hidden; padding: .1em .8em; position: absolute; z-index: 15; } .CodeMirror-dialog-top { border-bottom: 1px solid #eee; top: 0; } .CodeMirror-dialog-bottom { border-top: 1px solid #eee; bottom: 0; } .CodeMirror-dialog input { background: transparent; border: 1px solid #d3d6db; color: inherit; font-family: monospace; outline: none; width: 20em; } .CodeMirror-dialog button { font-size: 70%; } .graphiql-container .doc-explorer { background: white; } .graphiql-container .doc-explorer-title-bar, .graphiql-container .history-title-bar { cursor: default; display: flex; height: 34px; line-height: 14px; padding: 8px 8px 5px; position: relative; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .graphiql-container .doc-explorer-title, .graphiql-container .history-title { flex: 1; font-weight: bold; overflow-x: hidden; padding: 10px 0 10px 10px; text-align: center; text-overflow: ellipsis; -webkit-user-select: text; -moz-user-select: text; -ms-user-select: text; user-select: text; white-space: nowrap; } .graphiql-container .doc-explorer-back { color: #3B5998; cursor: pointer; margin: -7px 0 -6px -8px; overflow-x: hidden; padding: 17px 12px 16px 16px; text-overflow: ellipsis; white-space: nowrap; background: 0; border: 0; line-height: 14px; } .doc-explorer-narrow .doc-explorer-back { width: 0; } .graphiql-container .doc-explorer-back:before { border-left: 2px solid #3B5998; border-top: 2px solid #3B5998; content: ''; display: inline-block; height: 9px; margin: 0 3px -1px 0; position: relative; transform: rotate(-45deg); width: 9px; } .graphiql-container .doc-explorer-rhs { position: relative; } .graphiql-container .doc-explorer-contents, .graphiql-container .history-contents { background-color: #ffffff; border-top: 1px solid #d6d6d6; bottom: 0; left: 0; overflow-y: auto; padding: 20px 15px; position: absolute; right: 0; top: 47px; } .graphiql-container .doc-explorer-contents { min-width: 300px; } .graphiql-container .doc-type-description p:first-child , .graphiql-container .doc-type-description blockquote:first-child { margin-top: 0; } .graphiql-container .doc-explorer-contents a { cursor: pointer; text-decoration: none; } .graphiql-container .doc-explorer-contents a:hover { text-decoration: underline; } .graphiql-container .doc-value-description > :first-child { margin-top: 4px; } .graphiql-container .doc-value-description > :last-child { margin-bottom: 4px; } .graphiql-container .doc-type-description code, .graphiql-container .doc-type-description pre, .graphiql-container .doc-category code, .graphiql-container .doc-category pre { --saf-0: rgba(var(--sk_foreground_low,29,28,29),0.13); font-size: 12px; line-height: 1.50001; font-variant-ligatures: none; white-space: pre; white-space: pre-wrap; word-wrap: break-word; word-break: normal; -webkit-tab-size: 4; -moz-tab-size: 4; tab-size: 4; } .graphiql-container .doc-type-description code, .graphiql-container .doc-category code { padding: 2px 3px 1px; border: 1px solid var(--saf-0); border-radius: 3px; background-color: rgba(var(--sk_foreground_min,29,28,29),.04); color: #e01e5a; background-color: white; } .graphiql-container .doc-category { margin: 20px 0; } .graphiql-container .doc-category-title { border-bottom: 1px solid #e0e0e0; color: #777; cursor: default; font-size: 14px; font-variant: small-caps; font-weight: bold; letter-spacing: 1px; margin: 0 -15px 10px 0; padding: 10px 0; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .graphiql-container .doc-category-item { margin: 12px 0; color: #555; } .graphiql-container .keyword { color: #B11A04; } .graphiql-container .type-name { color: #CA9800; } .graphiql-container .field-name { color: #1F61A0; } .graphiql-container .field-short-description { color: #999; margin-left: 5px; overflow: hidden; text-overflow: ellipsis; } .graphiql-container .enum-value { color: #0B7FC7; } .graphiql-container .arg-name { color: #8B2BB9; } .graphiql-container .arg { display: block; margin-left: 1em; } .graphiql-container .arg:first-child:last-child, .graphiql-container .arg:first-child:nth-last-child(2), .graphiql-container .arg:first-child:nth-last-child(2) ~ .arg { display: inherit; margin: inherit; } .graphiql-container .arg:first-child:nth-last-child(2):after { content: ', '; } .graphiql-container .arg-default-value { color: #43A047; } .graphiql-container .doc-deprecation { background: #fffae8; box-shadow: inset 0 0 1px #bfb063; color: #867F70; line-height: 16px; margin: 8px -8px; max-height: 80px; overflow: hidden; padding: 8px; border-radius: 3px; } .graphiql-container .doc-deprecation:before { content: 'Deprecated:'; color: #c79b2e; cursor: default; display: block; font-size: 9px; font-weight: bold; letter-spacing: 1px; line-height: 1; padding-bottom: 5px; text-transform: uppercase; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .graphiql-container .doc-deprecation > :first-child { margin-top: 0; } .graphiql-container .doc-deprecation > :last-child { margin-bottom: 0; } .graphiql-container .show-btn { -webkit-appearance: initial; display: block; border-radius: 3px; border: solid 1px #ccc; text-align: center; padding: 8px 12px 10px; width: 100%; box-sizing: border-box; background: #fbfcfc; color: #555; cursor: pointer; } .graphiql-container .search-box { border-bottom: 1px solid #d3d6db; display: block; font-size: 14px; margin: -15px -15px 12px 0; position: relative; } .graphiql-container .search-box-icon { cursor: pointer; display: block; font-size: 24px; position: absolute; top: -2px; transform: rotate(-45deg); -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .graphiql-container .search-box .search-box-clear { background-color: #d0d0d0; border-radius: 12px; color: #fff; cursor: pointer; font-size: 11px; padding: 1px 5px 2px; position: absolute; right: 3px; top: 8px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; border: 0; } .graphiql-container .search-box .search-box-clear:hover { background-color: #b9b9b9; } .graphiql-container .search-box > input { border: none; box-sizing: border-box; font-size: 14px; outline: none; padding: 6px 24px 8px 20px; width: 100%; } .graphiql-container .error-container { font-weight: bold; left: 0; letter-spacing: 1px; opacity: 0.5; position: absolute; right: 0; text-align: center; text-transform: uppercase; top: 50%; transform: translate(0, -50%); } .CodeMirror-foldmarker { color: blue; cursor: pointer; font-family: arial; line-height: .3; text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px; } .CodeMirror-foldgutter { width: .7em; } .CodeMirror-foldgutter-open, .CodeMirror-foldgutter-folded { cursor: pointer; } .CodeMirror-foldgutter-open:after { content: "\25BE"; } .CodeMirror-foldgutter-folded:after { content: "\25B8"; } .graphiql-container .history-contents { font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace; } .graphiql-container .history-contents { margin: 0; padding: 0; } .graphiql-container .history-contents li { align-items: center; display: flex; font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin: 0; padding: 8px; border-bottom: 1px solid #e0e0e0; } .graphiql-container .history-contents li button:not(.history-label) { display: none; margin-left: 10px; } .graphiql-container .history-contents li:hover button:not(.history-label), .graphiql-container .history-contents li:focus-within button:not(.history-label) { display: inline-block; } .graphiql-container .history-contents input, .graphiql-container .history-contents button { padding: 0; background: 0; border: 0; font-size: inherit; font-family: inherit; line-height: 14px; color: inherit; } .graphiql-container .history-contents input { flex-grow: 1; } .graphiql-container .history-contents input::-webkit-input-placeholder { color: inherit; } .graphiql-container .history-contents input:-ms-input-placeholder { color: inherit; } .graphiql-container .history-contents input::-ms-input-placeholder { color: inherit; } .graphiql-container .history-contents input::placeholder { color: inherit; } .graphiql-container .history-contents button { cursor: pointer; text-align: left; } .graphiql-container .history-contents .history-label { flex-grow: 1; overflow: hidden; text-overflow: ellipsis; } .CodeMirror-info { background: white; border-radius: 2px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); box-sizing: border-box; color: #555; font-family: system, -apple-system, 'San Francisco', '.SFNSDisplay-Regular', 'Segoe UI', Segoe, 'Segoe WP', 'Helvetica Neue', helvetica, 'Lucida Grande', arial, sans-serif; font-size: 13px; line-height: 16px; margin: 8px -8px; max-width: 400px; opacity: 0; overflow: hidden; padding: 8px 8px; position: fixed; transition: opacity 0.15s; z-index: 50; } .CodeMirror-info :first-child { margin-top: 0; } .CodeMirror-info :last-child { margin-bottom: 0; } .CodeMirror-info p { margin: 1em 0; } .CodeMirror-info .info-description { color: #777; line-height: 16px; margin-top: 1em; max-height: 80px; overflow: hidden; } .CodeMirror-info .info-deprecation { background: #fffae8; box-shadow: inset 0 1px 1px -1px #bfb063; color: #867F70; line-height: 16px; margin: -8px; margin-top: 8px; max-height: 80px; overflow: hidden; padding: 8px; } .CodeMirror-info .info-deprecation-label { color: #c79b2e; cursor: default; display: block; font-size: 9px; font-weight: bold; letter-spacing: 1px; line-height: 1; padding-bottom: 5px; text-transform: uppercase; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .CodeMirror-info .info-deprecation-label + * { margin-top: 0; } .CodeMirror-info a { text-decoration: none; } .CodeMirror-info a:hover { text-decoration: underline; } .CodeMirror-info .type-name { color: #CA9800; } .CodeMirror-info .field-name { color: #1F61A0; } .CodeMirror-info .enum-value { color: #0B7FC7; } .CodeMirror-info .arg-name { color: #8B2BB9; } .CodeMirror-info .directive-name { color: #B33086; } .CodeMirror-jump-token { text-decoration: underline; cursor: pointer; } /* The lint marker gutter */ .CodeMirror-lint-markers { width: 16px; } .CodeMirror-lint-tooltip { background-color: infobackground; border-radius: 4px 4px 4px 4px; border: 1px solid black; color: infotext; font-family: monospace; font-size: 10pt; max-width: 600px; opacity: 0; overflow: hidden; padding: 2px 5px; position: fixed; transition: opacity .4s; white-space: pre-wrap; z-index: 100; } .CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning { background-position: left bottom; background-repeat: repeat-x; } .CodeMirror-lint-mark-error { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==") ; } .CodeMirror-lint-mark-warning { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII="); } .CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning { background-position: center center; background-repeat: no-repeat; cursor: pointer; display: inline-block; height: 16px; position: relative; vertical-align: middle; width: 16px; } .CodeMirror-lint-message-error, .CodeMirror-lint-message-warning { background-position: top left; background-repeat: no-repeat; padding-left: 18px; } .CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII="); } .CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII="); } .CodeMirror-lint-marker-multiple { background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC"); background-position: right bottom; background-repeat: no-repeat; width: 100%; height: 100%; } .graphiql-container .spinner-container { height: 36px; left: 50%; position: absolute; top: 50%; transform: translate(-50%, -50%); width: 36px; z-index: 10; } .graphiql-container .spinner { animation: rotation .6s infinite linear; border-bottom: 6px solid rgba(150, 150, 150, .15); border-left: 6px solid rgba(150, 150, 150, .15); border-radius: 100%; border-right: 6px solid rgba(150, 150, 150, .15); border-top: 6px solid rgba(150, 150, 150, .8); display: inline-block; height: 24px; position: absolute; vertical-align: middle; width: 24px; } @keyframes rotation { from { transform: rotate(0deg); } to { transform: rotate(359deg); } } .CodeMirror-hints { background: white; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); font-family: 'Consolas', 'Inconsolata', 'Droid Sans Mono', 'Monaco', monospace; font-size: 13px; list-style: none; margin-left: -6px; margin: 0; max-height: 14.5em; overflow-y: auto; overflow: hidden; padding: 0; position: absolute; z-index: 10; } .CodeMirror-hint { border-top: solid 1px #f7f7f7; color: #141823; cursor: pointer; margin: 0; max-width: 300px; overflow: hidden; padding: 2px 6px; white-space: pre; } li.CodeMirror-hint-active { background-color: #08f; border-top-color: white; color: white; } .CodeMirror-hint-information { border-top: solid 1px #c0c0c0; max-width: 300px; padding: 4px 6px; position: relative; z-index: 1; } .CodeMirror-hint-information:first-child { border-bottom: solid 1px #c0c0c0; border-top: none; margin-bottom: -1px; } .CodeMirror-hint-deprecation { background: #fffae8; box-shadow: inset 0 1px 1px -1px #bfb063; color: #867F70; font-family: system, -apple-system, 'San Francisco', '.SFNSDisplay-Regular', 'Segoe UI', Segoe, 'Segoe WP', 'Helvetica Neue', helvetica, 'Lucida Grande', arial, sans-serif; font-size: 13px; line-height: 16px; margin-top: 4px; max-height: 80px; overflow: hidden; padding: 6px; } .CodeMirror-hint-deprecation .deprecation-label { color: #c79b2e; cursor: default; display: block; font-size: 9px; font-weight: bold; letter-spacing: 1px; line-height: 1; padding-bottom: 5px; text-transform: uppercase; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .CodeMirror-hint-deprecation .deprecation-label + * { margin-top: 0; } .CodeMirror-hint-deprecation :last-child { margin-bottom: 0; } ================================================ FILE: src/GraphiQl/assets/index.html ================================================ GraphQL API
Loading…
================================================ FILE: tests/GraphiQl.Demo/.bowerrc ================================================ { "directory": "wwwroot/lib" } ================================================ FILE: tests/GraphiQl.Demo/Controllers/GraphQlController.cs ================================================ using System.Threading.Tasks; using GraphiQl.Demo.GraphQl; using GraphiQl.Demo.GraphQl.Models; using GraphQL; using GraphQL.Types; using Microsoft.AspNetCore.Mvc; namespace GraphiQl.Demo.Controllers { [Route(Startup.GraphQlPath)] [Route(Startup.CustomGraphQlPath)] public class GraphQlController : Controller { [HttpPost] public async Task Post([FromBody] GraphQlQuery query) { var schema = new Schema {Query = new StarWarsQuery()}; var result = await new DocumentExecuter().ExecuteAsync(x => { x.Schema = schema; x.Query = query.Query; x.Inputs = query.Variables; }); if (result.Errors?.Count > 0) { return BadRequest(); } return Ok(result); } } } ================================================ FILE: tests/GraphiQl.Demo/GraphQl/Droid.cs ================================================ namespace GraphiQl.Demo.GraphQl { public class Droid { public int Id { get; set; } public string Name { get; set; } } public class Person { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } } } ================================================ FILE: tests/GraphiQl.Demo/GraphQl/GraphQlQuery.cs ================================================ using GraphQL; namespace GraphiQl.Demo.GraphQl { public class GraphQlQuery { public string OperationName { get; set; } public string NamedQuery { get; set; } public string Query { get; set; } public Inputs Variables { get; set; } } } ================================================ FILE: tests/GraphiQl.Demo/GraphQl/Models/DroidType.cs ================================================ using GraphQL.Types; namespace GraphiQl.Demo.GraphQl.Models { public class DroidType : ObjectGraphType { public DroidType() { Field(x => x.Id).Description("The Id of the Droid."); Field(x => x.Name, nullable: true).Description("The name of the Droid."); } } public class PersonType : ObjectGraphType { public PersonType() { // Field(x => x.FirstName).Resolve((x) => { // return "Hello!"; // }).Description("Perso's First Name"); Field(x => x.Id).Description("Peron's Id"); Field(x => x.Name).Description("Person's name"); Field(x => x.Age).Resolve(x => { return 25; }).Description("A person's age"); } } } ================================================ FILE: tests/GraphiQl.Demo/GraphQl/Models/StarWarsQuery.cs ================================================ using GraphQL.Types; namespace GraphiQl.Demo.GraphQl.Models { public class StarWarsQuery : ObjectGraphType { public StarWarsQuery() { Field("hero", resolve: context => new Droid { Id = 1, Name = "R2-D2" }); Field("person", resolve: context => new Person { Id = 1, Name = "Mr Happy", Age = 32 }); // Field("person", resolve: context => new Person { Id = 5, FirstName = "Joe", Surname = "Woodward", Age = 32 }); } } } ================================================ FILE: tests/GraphiQl.Demo/GraphiQl.Demo.csproj ================================================  netcoreapp3.1 ================================================ FILE: tests/GraphiQl.Demo/Program.cs ================================================ using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; namespace GraphiQl.Demo { public class Program { public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() .Build(); } } ================================================ FILE: tests/GraphiQl.Demo/Properties/launchSettings.json ================================================ { "profiles": { "GraphiQl.Demp": { "commandName": "Project", "launchBrowser": true, "launchUrl": "graphql", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:5000" } } } ================================================ FILE: tests/GraphiQl.Demo/Startup.cs ================================================ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Newtonsoft.Json; namespace GraphiQl.Demo { public class Startup { public const string GraphQlPath = "/graphql"; public const string CustomGraphQlPath = "/custom-path"; public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public virtual void ConfigureGraphQl(IServiceCollection services) {} public void ConfigureServices(IServiceCollection services) { services .AddMvc() .AddNewtonsoftJson( options => options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore ); ConfigureGraphQl(services); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory) { app.UseGraphiQl(); app.UseRouting().UseEndpoints( routing => routing.MapControllers() ); } } } ================================================ FILE: tests/GraphiQl.Demo/appsettings.Development.json ================================================ { "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } } ================================================ FILE: tests/GraphiQl.Demo/appsettings.json ================================================ { "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Warning" } } } ================================================ FILE: tests/GraphiQl.Tests/AuthenticationTest/ConfigureOptionsSetup.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using GraphiQl.Demo; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Shouldly; using Xunit; namespace GraphiQl.Tests.AuthenticationTest { public class ConfigureOptionsSetup : SeleniumTest, IAsyncLifetime { private readonly IWebHost _host; public ConfigureOptionsSetup() { _host = WebHost.CreateDefaultBuilder() .ConfigureServices(x => { x.AddTransient,GraphiQlTestOptionsSetup>(); }) .UseStartup() .UseKestrel() .UseUrls("http://*:5001") .Build(); } [Fact] public void RequiresAuthentication() { // Arrange + Act var result = string.Empty; RunTest(driver => { driver.Navigate().GoToUrl("http://localhost:5001/graphql"); driver.Manage() .Timeouts() .ImplicitWait = TimeSpan.FromSeconds(2); result = driver.PageSource; }); // Assert result.ShouldContain("This page requires authentication"); } public async Task InitializeAsync() => await _host.StartAsync().ConfigureAwait(false); public Task DisposeAsync() { _host.Dispose(); return Task.CompletedTask; } internal class GraphiQlTestOptionsSetup : IConfigureOptions { public void Configure(GraphiQlOptions options) { options.IsAuthenticated = context => { context.Response.Clear(); context.Response.StatusCode = 400; context.Response.WriteAsync("This page requires authentication"); return Task.FromResult(false); }; } } } } ================================================ FILE: tests/GraphiQl.Tests/AuthenticationTest/DelegateSetup.cs ================================================ using System; using System.Threading; using System.Threading.Tasks; using GraphiQl.Demo; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Xunit; namespace GraphiQl.Tests.AuthenticationTest { public class CustomStartup : Startup { public CustomStartup(IConfiguration configuration) : base(configuration) {} public override void ConfigureGraphQl(IServiceCollection services) => services.AddGraphiQl(x => x.IsAuthenticated = context => { context.Response.Clear(); context.Response.StatusCode = 400; context.Response.WriteAsync("This page requires authentication"); return Task.FromResult(false); }); } public class DelegateSetup : SeleniumTest, IAsyncLifetime { private readonly IWebHost _host; public DelegateSetup() { _host = WebHost.CreateDefaultBuilder() .UseStartup() .UseKestrel() .UseUrls("http://*:5001") .Build(); } [Fact] public void RequiresAuthentication() { // Arrange + Act var result = string.Empty; RunTest(driver => { driver.Navigate().GoToUrl("http://localhost:5001/graphql"); driver.Manage() .Timeouts() .ImplicitWait = TimeSpan.FromSeconds(2); result = driver.PageSource; }); // Assert result.ShouldContain("This page requires authentication"); } public async Task InitializeAsync() => await _host.StartAsync().ConfigureAwait(false); public Task DisposeAsync() { _host.Dispose(); return Task.CompletedTask; } } } ================================================ FILE: tests/GraphiQl.Tests/BasicTest.cs ================================================ using System; using System.Text.Json; using System.Threading; using GraphiQl.Tests.Fixtures; using Shouldly; using Xunit; namespace GraphiQl.Tests { public class BasicTest : SeleniumTest, IClassFixture { private readonly HostFixture _fixture; public BasicTest(HostFixture fixture) { _fixture = fixture; } [Fact] public void CanQueryGraphQl() { // TODO: Use PageModel // Arrange var result = string.Empty; var query = @"{hero{id,name}}"; // Act RunTest( driver => { driver.Navigate().GoToUrl(_fixture.GraphiQlUri + Uri.EscapeDataString(query)); var button = driver.FindElementByClassName("execute-button"); button?.Click(); //TODO: https://www.selenium.dev/documentation/en/webdriver/waits/ Thread.Sleep(2000); // UGH! result = driver.FindElementByClassName("result-window").Text .Replace("\n", "") .Replace(" ", ""); }); // Assert using var channelResponse = JsonDocument.Parse(result); var data = channelResponse.RootElement.GetProperty("data"); data.GetProperty("hero").GetProperty("name").GetString().ShouldBe("R2-D2"); } } } ================================================ FILE: tests/GraphiQl.Tests/Fixtures/GraphQlFixture.cs ================================================ using System.Threading.Tasks; using GraphiQl; using GraphiQl.Demo; using GraphiQl.Demo.Controllers; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.ApplicationParts; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Newtonsoft.Json; namespace graphiql.Tests.Fixtures { public class GraphQlFixture { public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseKestrel(); webBuilder.UseUrls("http://*:5000"); webBuilder.UseStartup(); }); public async Task CreateKestrel() { var x = new WebHostBuilder(); var host = CreateHostBuilder(null).Build(); await host.RunAsync(); } public IWebHost CreateWebHostOld() { var config = new ConfigurationBuilder().Build(); var host = new WebHostBuilder() .UseConfiguration(config) .ConfigureServices(s => { s.AddMvc() .AddNewtonsoftJson(o => o.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore); s.AddControllers() .PartManager.ApplicationParts.Add(new AssemblyPart(typeof(GraphQlController).Assembly)); }) .Configure(app => { app.UseGraphiQl(); app.UseRouting().UseEndpoints(routing => routing.MapControllers()); }); return host.Build(); } } } ================================================ FILE: tests/GraphiQl.Tests/Fixtures/HostFixture.cs ================================================ using System.Threading.Tasks; using GraphiQl.Demo; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Xunit; namespace GraphiQl.Tests.Fixtures { public class HostFixture : IAsyncLifetime { private readonly IWebHost _host; public string GraphiQlUri { get; } = "http://localhost:5001/graphql?query="; public HostFixture() { _host = WebHost.CreateDefaultBuilder() .UseStartup() .UseKestrel() .UseUrls("http://*:5001") .Build(); } public async Task InitializeAsync() => await _host.StartAsync().ConfigureAwait(false); public async Task DisposeAsync() { await _host.StopAsync().ConfigureAwait(false); _host.Dispose(); } } } ================================================ FILE: tests/GraphiQl.Tests/GraphiQl.Tests.csproj ================================================ netcoreapp3.1 false GraphiQl.Tests GraphiQl.Tests ================================================ FILE: tests/GraphiQl.Tests/OverrideGraphQlPathTests/ConfigureOptionsSetup.cs ================================================ using System; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using GraphiQl.Demo; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Shouldly; using Xunit; namespace GraphiQl.Tests.OverrideGraphQlPathTests { public class ConfigureOptionsSetup : SeleniumTest, IAsyncLifetime { private readonly IWebHost _host; public ConfigureOptionsSetup() { _host = WebHost.CreateDefaultBuilder() .ConfigureServices(serviceCollection => { serviceCollection.AddTransient, GraphiQlTestOptionsSetup>(); }) .UseStartup() .UseKestrel() .UseUrls("http://*:5001") .Build(); } public async Task InitializeAsync() => await _host.StartAsync().ConfigureAwait(false); [Fact] public void CanOverrideGraphQlPath() { // TODO: Use PageModel // Arrange var result = string.Empty; var query = @"{hero{id,name}}"; // Act RunTest( driver => { driver.Navigate().GoToUrl($"http://localhost:5001/graphql?query=" + Uri.EscapeDataString(query)); var button = driver.FindElementByClassName("execute-button"); button?.Click(); Thread.Sleep(2000); /* var x = Driver.FindElement(B) var foo = new WebDriverWait(driver, TimeSpan.FromSeconds(3)) .Until(drv => drv.FindElement(By.Name("q"))); */ // UGH! result = driver .FindElementByClassName("result-window").Text .Replace("\n", "") .Replace(" ", ""); }); // Assert using var channelResponse = JsonDocument.Parse(result); var data = channelResponse.RootElement.GetProperty("data"); data.GetProperty("hero").GetProperty("name").GetString().ShouldBe("R2-D2"); } public async Task DisposeAsync() { await _host.StopAsync(); _host.Dispose(); } internal class GraphiQlTestOptionsSetup : IConfigureOptions { public void Configure(GraphiQlOptions options) { options.GraphQlApiPath = Startup.CustomGraphQlPath; } } } } ================================================ FILE: tests/GraphiQl.Tests/OverrideGraphQlPathTests/DelegateSetup.cs ================================================ using System; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using GraphiQl.Demo; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Shouldly; using Xunit; namespace GraphiQl.Tests.OverrideGraphQlPathTests { public class CustomStartup : Startup { public CustomStartup(IConfiguration configuration) : base(configuration) {} public override void ConfigureGraphQl(IServiceCollection services) => services.AddGraphiQl(x => x.GraphQlApiPath = CustomGraphQlPath); } public class DelegateSetup : SeleniumTest, IAsyncLifetime { private readonly IWebHost _host; public DelegateSetup() { _host = WebHost.CreateDefaultBuilder() .UseStartup() .UseKestrel() .UseUrls("http://*:5001") .Build(); } public async Task InitializeAsync() => await _host.StartAsync().ConfigureAwait(false); [Fact] public void CanOverrideGraphQlPath() { // TODO: Use PageModel // Arrange var result = string.Empty; var query = @"{hero{id,name}}"; // Act RunTest( driver => { driver.Navigate().GoToUrl("http://localhost:5001/graphql?query=" + Uri.EscapeDataString(query)); var button = driver.FindElementByClassName("execute-button"); button?.Click(); Thread.Sleep(2000); // UGH! result = driver .FindElementByClassName("result-window").Text .Replace("\n", "") .Replace(" ", ""); }); // Assert using var channelResponse = JsonDocument.Parse(result); var data = channelResponse.RootElement.GetProperty("data"); data.GetProperty("hero").GetProperty("name").GetString().ShouldBe("R2-D2"); } public async Task DisposeAsync() { await _host.StopAsync(); _host.Dispose(); } } } ================================================ FILE: tests/GraphiQl.Tests/OverrideGraphiQlPathTests/ConfigureOptionsSetup.cs ================================================ using System; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using GraphiQl.Demo; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Shouldly; using Xunit; namespace GraphiQl.Tests.OverrideGraphiQlPathTests { public class ConfigureOptionsSetup : SeleniumTest, IAsyncLifetime { private readonly IWebHost _host; public ConfigureOptionsSetup() { _host = WebHost.CreateDefaultBuilder() .ConfigureServices(serviceCollection => { serviceCollection.AddTransient, GraphiQlTestOptionsSetup>(); }) .UseStartup() .UseKestrel() .UseUrls("http://*:5001") .Build(); } public async Task InitializeAsync() => await _host.StartAsync().ConfigureAwait(false); [Fact] public void CanOverrideGraphiQlPath() { // TODO: Use PageModel // Arrange var result = string.Empty; var query = @"{hero{id,name}}"; // Act RunTest( driver => { driver.Navigate().GoToUrl($"http://localhost:5001{Startup.CustomGraphQlPath}?query=" + Uri.EscapeDataString(query)); var button = driver.FindElementByClassName("execute-button"); button?.Click(); Thread.Sleep(2000); // UGH! result = driver .FindElementByClassName("result-window").Text .Replace("\n", "") .Replace(" ", ""); }); // Assert using var channelResponse = JsonDocument.Parse(result); var data = channelResponse.RootElement.GetProperty("data"); data.GetProperty("hero").GetProperty("name").GetString().ShouldBe("R2-D2"); } public async Task DisposeAsync() { await _host.StopAsync(); _host.Dispose(); } internal class GraphiQlTestOptionsSetup : IConfigureOptions { public void Configure(GraphiQlOptions options) { options.GraphiQlPath = Startup.CustomGraphQlPath; } } } } ================================================ FILE: tests/GraphiQl.Tests/OverrideGraphiQlPathTests/DelegateSetup.cs ================================================ using System; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using GraphiQl.Demo; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Shouldly; using Xunit; namespace GraphiQl.Tests.OverrideGraphiQlPathTests { public class CustomStartup : Startup { public CustomStartup(IConfiguration configuration) : base(configuration) {} public override void ConfigureGraphQl(IServiceCollection services) => services.AddGraphiQl(x => x.GraphiQlPath = CustomGraphQlPath); } public class DelegateSetup : SeleniumTest, IAsyncLifetime { private readonly IWebHost _host; public DelegateSetup() { _host = WebHost.CreateDefaultBuilder() .UseStartup() .UseKestrel() .UseUrls("http://*:5001") .Build(); } public async Task InitializeAsync() => await _host.StartAsync().ConfigureAwait(false); [Fact] public void CanOverrideGraphiQlPath() { // TODO: Use PageModel // Arrange var result = string.Empty; var query = @"{hero{id,name}}"; // Act RunTest( driver => { driver.Navigate().GoToUrl($"http://localhost:5001{Startup.CustomGraphQlPath}?query=" + Uri.EscapeDataString(query)); var button = driver.FindElementByClassName("execute-button"); button?.Click(); driver.Manage() .Timeouts() .ImplicitWait = TimeSpan.FromSeconds(2); // UGH! result = driver .FindElementByClassName("result-window").Text .Replace("\n", "") .Replace(" ", ""); }); // Assert using var channelResponse = JsonDocument.Parse(result); var data = channelResponse.RootElement.GetProperty("data"); data.GetProperty("hero").GetProperty("name").GetString().ShouldBe("R2-D2"); } public async Task DisposeAsync() { await _host.StopAsync(); _host.Dispose(); } internal class GraphiQlTestOptionsSetup : IConfigureOptions { public void Configure(GraphiQlOptions options) { options.GraphiQlPath = Startup.CustomGraphQlPath; } } } } ================================================ FILE: tests/GraphiQl.Tests/SeleniumTest.cs ================================================ using System; using OpenQA.Selenium; using OpenQA.Selenium.Chrome; namespace GraphiQl.Tests { public abstract class SeleniumTest { private ChromeDriver Driver { get; } protected bool RunHeadless { get; set; } = false; protected SeleniumTest() { var options = new ChromeOptions(); options.AddArgument("--disable-dev-shm-usage"); options.AddArgument("--no-sandbox"); if (RunHeadless) options.AddArgument("--headless"); Driver = new ChromeDriver(options); } protected void RunTest(Action execute) { try { execute(Driver); } catch (Exception ex) { throw ex; } finally { Driver.Quit(); } } } } ================================================ FILE: tests/GraphiQl.Tests/xunit.runner.json ================================================ { "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", "parallelizeTestCollections": false }