Skip to content

Added Altair GraphQL UI. #301

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Provides the following packages:
| GraphQL.Server.Transports.WebSockets | [![Nuget](https://img.shields.io/nuget/dt/GraphQL.Server.Transports.WebSockets)](https://www.nuget.org/packages/GraphQL.Server.Transports.WebSockets/) |
| GraphQL.Server.Ui.Playground | [![Nuget](https://img.shields.io/nuget/dt/GraphQL.Server.Ui.Playground)](https://www.nuget.org/packages/GraphQL.Server.Ui.Playground/) |
| GraphQL.Server.Ui.GraphiQL | [![Nuget](https://img.shields.io/nuget/dt/GraphQL.Server.Ui.GraphiQL)](https://www.nuget.org/packages/GraphQL.Server.Ui.GraphiQL/) |
| GraphQL.Server.Ui.Altair | [![Nuget](https://img.shields.io/nuget/dt/GraphQL.Server.Ui.Altair)](https://www.nuget.org/packages/GraphQL.Server.Ui.Altair/) |
| GraphQL.Server.Ui.Voyager | [![Nuget](https://img.shields.io/nuget/dt/GraphQL.Server.Ui.Voyager)](https://www.nuget.org/packages/GraphQL.Server.Ui.Voyager/) |
| GraphQL.Server.Authorization.AspNetCore | [![Nuget](https://img.shields.io/nuget/dt/GraphQL.Server.Authorization.AspNetCore)](https://www.nuget.org/packages/GraphQL.Server.Authorization.AspNetCore/) |

Expand All @@ -32,6 +33,7 @@ For the WebSocket subscription protocol (depends on above) middleware:
For the UI middleware/s:
>`dotnet add package GraphQL.Server.Ui.GraphiQL`
>`dotnet add package GraphQL.Server.Ui.Playground`
>`dotnet add package GraphQL.Server.Ui.Altair`
>`dotnet add package GraphQL.Server.Ui.Voyager`


Expand Down Expand Up @@ -68,6 +70,9 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)

// use graphql-playground middleware at default url /ui/playground
app.UseGraphQLPlayground(new GraphQLPlaygroundOptions());

// use altair middleware at default url /ui/altair
app.UseGraphQLAltair(new GraphQLAltairOptions());

// use voyager middleware at default url /ui/voyager
app.UseGraphQLVoyager(new GraphQLVoyagerOptions());
Expand Down
1 change: 1 addition & 0 deletions build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var projectFiles = new [] {
"./src/Transports.Subscriptions.WebSockets/Transports.Subscriptions.WebSockets.csproj",
"./src/Ui.Playground/Ui.Playground.csproj",
"./src/Ui.GraphiQL/Ui.GraphiQL.csproj",
"./src/Ui.Altair/Ui.Altair.csproj",
"./src/Ui.Voyager/Ui.Voyager.csproj",
"./src/Authorization.AspNetCore/Authorization.AspNetCore.csproj"
};
Expand Down
1 change: 1 addition & 0 deletions samples/Samples.Server/Samples.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<ProjectReference Include="..\..\src\Transports.Subscriptions.WebSockets\Transports.Subscriptions.WebSockets.csproj" />
<ProjectReference Include="..\..\src\Ui.GraphiQL\Ui.GraphiQL.csproj" />
<ProjectReference Include="..\..\src\Ui.Playground\Ui.Playground.csproj" />
<ProjectReference Include="..\..\src\Ui.Altair\Ui.Altair.csproj" />
<ProjectReference Include="..\..\src\Ui.Voyager\Ui.Voyager.csproj" />
<ProjectReference Include="..\Samples.Schemas.Chat\Samples.Schemas.Chat.csproj" />
</ItemGroup>
Expand Down
11 changes: 11 additions & 0 deletions samples/Samples.Server/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using GraphQL.Server;
using GraphQL.Server.Ui.GraphiQL;
using GraphQL.Server.Ui.Playground;
using GraphQL.Server.Ui.Altair;
using GraphQL.Server.Ui.Voyager;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
Expand Down Expand Up @@ -98,6 +99,16 @@ public void Configure(IApplicationBuilder app)
GraphQLEndPoint = "/graphql",
});

app.UseGraphQLAltair(new GraphQLAltairOptions
{
Path = "/ui/altair",
GraphQLEndPoint = "/graphql",
Headers = new Dictionary<string, string>
{
["X-api-token"] = "130fh9823bd023hd892d0j238dh",
}
});

app.UseGraphQLVoyager(new GraphQLVoyagerOptions
{
Path = "/ui/voyager",
Expand Down
19 changes: 19 additions & 0 deletions src/Ui.Altair/AltairExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using GraphQL.Server.Ui.Altair;

namespace Microsoft.AspNetCore.Builder
{
/// <summary>
/// Extension methods for <see cref="IApplicationBuilder"/>
/// </summary>
public static class AltairExtensions
{
/// <summary> Adds middleware for Altair GraphQL using the specified options. </summary>
/// <param name="app"> <see cref="IApplicationBuilder"/> to configure an application's request pipeline. </param>
/// <param name="options"> Options to customize <see cref="AltairMiddleware"/>. If not set, then the default values will be used. </param>
/// <returns> The reference to provided <paramref name="app"/> instance. </returns>
public static IApplicationBuilder UseGraphQLAltair(this IApplicationBuilder app, GraphQLAltairOptions options = null)
{
return app.UseMiddleware<AltairMiddleware>(options ?? new GraphQLAltairOptions());
}
}
}
68 changes: 68 additions & 0 deletions src/Ui.Altair/AltairMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using GraphQL.Server.Ui.Altair.Internal;
using Microsoft.AspNetCore.Http;
using System;
using System.Text;
using System.Threading.Tasks;

namespace GraphQL.Server.Ui.Altair
{
/// <summary>
/// A middleware for Altair GraphQL
/// </summary>
public class AltairMiddleware
{
private readonly GraphQLAltairOptions _options;

/// <summary>
/// The next middleware
/// </summary>
private readonly RequestDelegate _nextMiddleware;

/// <summary>
/// The page model used to render Altair
/// </summary>
private AltairPageModel _pageModel;

/// <summary>
/// Create a new <see cref="AltairMiddleware"/>
/// </summary>
/// <param name="nextMiddleware">The next middleware</param>
/// <param name="options">Options to customize middleware</param>
public AltairMiddleware(RequestDelegate nextMiddleware, GraphQLAltairOptions options)
{
_nextMiddleware = nextMiddleware ?? throw new ArgumentNullException(nameof(nextMiddleware));
_options = options ?? throw new ArgumentNullException(nameof(options));
}

/// <summary>
/// Try to execute the logic of the middleware
/// </summary>
/// <param name="httpContext">The HttpContext</param>
public Task Invoke(HttpContext httpContext)
{
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));

return IsAltairRequest(httpContext.Request)
? InvokeAltair(httpContext.Response)
: _nextMiddleware(httpContext);
}

private bool IsAltairRequest(HttpRequest httpRequest)
{
return HttpMethods.IsGet(httpRequest.Method) && httpRequest.Path.StartsWithSegments(_options.Path);
}

private Task InvokeAltair(HttpResponse httpResponse)
{
httpResponse.ContentType = "text/html";
httpResponse.StatusCode = 200;

// Initialize page model if null
if (_pageModel == null)
_pageModel = new AltairPageModel(_options);

var data = Encoding.UTF8.GetBytes(_pageModel.Render());
return httpResponse.Body.WriteAsync(data, 0, data.Length);
}
}
}
26 changes: 26 additions & 0 deletions src/Ui.Altair/GraphQLAltairOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Http;
using System.Collections.Generic;

namespace GraphQL.Server.Ui.Altair
{
/// <summary>
/// Options to customize <see cref="AltairMiddleware"/>
/// </summary>
public class GraphQLAltairOptions
{
/// <summary>
/// The Altair GraphQL EndPoint to listen
/// </summary>
public PathString Path { get; set; } = "/ui/altair";

/// <summary>
/// The GraphQL EndPoint
/// </summary>
public PathString GraphQLEndPoint { get; set; } = "/graphql";

/// <summary>
/// Altair Headers Config
/// </summary>
public Dictionary<string, string> Headers { get; set; }
}
}
40 changes: 40 additions & 0 deletions src/Ui.Altair/Internal/AltairPageModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Newtonsoft.Json;
using System.IO;
using System.Text;

namespace GraphQL.Server.Ui.Altair.Internal
{
// https://docs.microsoft.com/en-us/aspnet/core/mvc/razor-pages/?tabs=netcore-cli
internal class AltairPageModel
{
private string _altairCSHtml;

private readonly GraphQLAltairOptions _options;

public AltairPageModel(GraphQLAltairOptions options)
{
_options = options;
}

public string Render()
{
if (_altairCSHtml == null)
{
using (var manifestResourceStream = typeof(AltairPageModel).Assembly.GetManifestResourceStream("GraphQL.Server.Ui.Altair.Internal.altair.cshtml"))
{
using (var streamReader = new StreamReader(manifestResourceStream))
{
var builder = new StringBuilder(streamReader.ReadToEnd());

builder.Replace("@Model.GraphQLEndPoint", _options.GraphQLEndPoint);
builder.Replace("@Model.AltairHeaders", JsonConvert.SerializeObject(_options.Headers));

_altairCSHtml = builder.ToString();
}
}
}

return _altairCSHtml;
}
}
}
51 changes: 51 additions & 0 deletions src/Ui.Altair/Internal/altair.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!doctype html>
<html>

<head>
<meta charset="utf-8">
<title>Altair</title>
<base href="//cdn.jsdelivr.net/npm/altair-static/build/dist/">
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="icon" type="image/x-icon" href="//cdn.jsdelivr.net/npm/altair-static/build/dist/favicon.ico">
<link href="//cdn.jsdelivr.net/npm/altair-static/build/dist/styles.css" rel="stylesheet" />
</head>

<body>
<app-root>
<style>
.loading-screen {
display: none;
}

</style>
<div class="loading-screen styled">
<div class="loading-screen-inner">
<div class="loading-screen-logo-container">
<img src="assets/img/logo_350.svg" alt="Altair">
</div>
<div class="loading-screen-loading-indicator">
<span class="loading-indicator-dot"></span>
<span class="loading-indicator-dot"></span>
<span class="loading-indicator-dot"></span>
</div>
</div>
</div>
</app-root>
<script rel="preload" as="script" type="text/javascript" src="//cdn.jsdelivr.net/npm/altair-static/build/dist/runtime.js"></script>
<script rel="preload" as="script" type="text/javascript" src="//cdn.jsdelivr.net/npm/altair-static/build/dist/polyfills.js"></script>
<script rel="preload" as="script" type="text/javascript" src="//cdn.jsdelivr.net/npm/altair-static/build/dist/main.js"></script>

<script>
var altairOptions = {
endpointURL: window.location.protocol + "//" + window.location.host + "@Model.GraphQLEndPoint",
subscriptionsEndpoint: (window.location.protocol === "http:" ? "ws://" : "wss://") + window.location.host + "@Model.GraphQLEndPoint",
initialHeaders: @Model.AltairHeaders,
};

window.addEventListener("load", function() {
AltairGraphQL.init(altairOptions);
});
</script>
</body>

</html>
28 changes: 28 additions & 0 deletions src/Ui.Altair/Ui.Altair.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;netstandard2.0</TargetFrameworks>
<AssemblyName>GraphQL.Server.Ui.Altair</AssemblyName>
<RootNamespace>GraphQL.Server.Ui.Altair</RootNamespace>
<Description>Altair GraphQL extension</Description>
<PackageTags>Altair GraphQL</PackageTags>
<PackageId>GraphQL.Server.Ui.Altair</PackageId>
</PropertyGroup>

<ItemGroup>
<EmbeddedResource Include="Internal\altair.cshtml" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
</ItemGroup>

<ItemGroup Condition="'$(IsNetCore30OnwardsTarget)' == 'True'">
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

<ItemGroup Condition="'$(IsNetCore30OnwardsTarget)' == 'False'">
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="$(MicrosoftAspNetCoreHttpAbstractionsVersion)" />
</ItemGroup>

</Project>