Skip to content

Commit d42d56d

Browse files
imolorhesungam3r
authored andcommitted
Added Altair GraphQL UI. (#301)
1 parent 6425d43 commit d42d56d

File tree

10 files changed

+250
-0
lines changed

10 files changed

+250
-0
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Provides the following packages:
1616
| GraphQL.Server.Transports.WebSockets | [![Nuget](https://img.shields.io/nuget/dt/GraphQL.Server.Transports.WebSockets)](https://www.nuget.org/packages/GraphQL.Server.Transports.WebSockets/) |
1717
| GraphQL.Server.Ui.Playground | [![Nuget](https://img.shields.io/nuget/dt/GraphQL.Server.Ui.Playground)](https://www.nuget.org/packages/GraphQL.Server.Ui.Playground/) |
1818
| GraphQL.Server.Ui.GraphiQL | [![Nuget](https://img.shields.io/nuget/dt/GraphQL.Server.Ui.GraphiQL)](https://www.nuget.org/packages/GraphQL.Server.Ui.GraphiQL/) |
19+
| GraphQL.Server.Ui.Altair | [![Nuget](https://img.shields.io/nuget/dt/GraphQL.Server.Ui.Altair)](https://www.nuget.org/packages/GraphQL.Server.Ui.Altair/) |
1920
| GraphQL.Server.Ui.Voyager | [![Nuget](https://img.shields.io/nuget/dt/GraphQL.Server.Ui.Voyager)](https://www.nuget.org/packages/GraphQL.Server.Ui.Voyager/) |
2021
| GraphQL.Server.Authorization.AspNetCore | [![Nuget](https://img.shields.io/nuget/dt/GraphQL.Server.Authorization.AspNetCore)](https://www.nuget.org/packages/GraphQL.Server.Authorization.AspNetCore/) |
2122

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

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

6971
// use graphql-playground middleware at default url /ui/playground
7072
app.UseGraphQLPlayground(new GraphQLPlaygroundOptions());
73+
74+
// use altair middleware at default url /ui/altair
75+
app.UseGraphQLAltair(new GraphQLAltairOptions());
7176

7277
// use voyager middleware at default url /ui/voyager
7378
app.UseGraphQLVoyager(new GraphQLVoyagerOptions());

build.cake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ var projectFiles = new [] {
1313
"./src/Transports.Subscriptions.WebSockets/Transports.Subscriptions.WebSockets.csproj",
1414
"./src/Ui.Playground/Ui.Playground.csproj",
1515
"./src/Ui.GraphiQL/Ui.GraphiQL.csproj",
16+
"./src/Ui.Altair/Ui.Altair.csproj",
1617
"./src/Ui.Voyager/Ui.Voyager.csproj",
1718
"./src/Authorization.AspNetCore/Authorization.AspNetCore.csproj"
1819
};

samples/Samples.Server/Samples.Server.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<ProjectReference Include="..\..\src\Transports.Subscriptions.WebSockets\Transports.Subscriptions.WebSockets.csproj" />
2323
<ProjectReference Include="..\..\src\Ui.GraphiQL\Ui.GraphiQL.csproj" />
2424
<ProjectReference Include="..\..\src\Ui.Playground\Ui.Playground.csproj" />
25+
<ProjectReference Include="..\..\src\Ui.Altair\Ui.Altair.csproj" />
2526
<ProjectReference Include="..\..\src\Ui.Voyager\Ui.Voyager.csproj" />
2627
<ProjectReference Include="..\Samples.Schemas.Chat\Samples.Schemas.Chat.csproj" />
2728
</ItemGroup>

samples/Samples.Server/Startup.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using GraphQL.Server;
33
using GraphQL.Server.Ui.GraphiQL;
44
using GraphQL.Server.Ui.Playground;
5+
using GraphQL.Server.Ui.Altair;
56
using GraphQL.Server.Ui.Voyager;
67
using Microsoft.AspNetCore.Builder;
78
using Microsoft.AspNetCore.Hosting;
@@ -98,6 +99,16 @@ public void Configure(IApplicationBuilder app)
9899
GraphQLEndPoint = "/graphql",
99100
});
100101

102+
app.UseGraphQLAltair(new GraphQLAltairOptions
103+
{
104+
Path = "/ui/altair",
105+
GraphQLEndPoint = "/graphql",
106+
Headers = new Dictionary<string, string>
107+
{
108+
["X-api-token"] = "130fh9823bd023hd892d0j238dh",
109+
}
110+
});
111+
101112
app.UseGraphQLVoyager(new GraphQLVoyagerOptions
102113
{
103114
Path = "/ui/voyager",

src/Ui.Altair/AltairExtensions.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using GraphQL.Server.Ui.Altair;
2+
3+
namespace Microsoft.AspNetCore.Builder
4+
{
5+
/// <summary>
6+
/// Extension methods for <see cref="IApplicationBuilder"/>
7+
/// </summary>
8+
public static class AltairExtensions
9+
{
10+
/// <summary> Adds middleware for Altair GraphQL using the specified options. </summary>
11+
/// <param name="app"> <see cref="IApplicationBuilder"/> to configure an application's request pipeline. </param>
12+
/// <param name="options"> Options to customize <see cref="AltairMiddleware"/>. If not set, then the default values will be used. </param>
13+
/// <returns> The reference to provided <paramref name="app"/> instance. </returns>
14+
public static IApplicationBuilder UseGraphQLAltair(this IApplicationBuilder app, GraphQLAltairOptions options = null)
15+
{
16+
return app.UseMiddleware<AltairMiddleware>(options ?? new GraphQLAltairOptions());
17+
}
18+
}
19+
}

src/Ui.Altair/AltairMiddleware.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using GraphQL.Server.Ui.Altair.Internal;
2+
using Microsoft.AspNetCore.Http;
3+
using System;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace GraphQL.Server.Ui.Altair
8+
{
9+
/// <summary>
10+
/// A middleware for Altair GraphQL
11+
/// </summary>
12+
public class AltairMiddleware
13+
{
14+
private readonly GraphQLAltairOptions _options;
15+
16+
/// <summary>
17+
/// The next middleware
18+
/// </summary>
19+
private readonly RequestDelegate _nextMiddleware;
20+
21+
/// <summary>
22+
/// The page model used to render Altair
23+
/// </summary>
24+
private AltairPageModel _pageModel;
25+
26+
/// <summary>
27+
/// Create a new <see cref="AltairMiddleware"/>
28+
/// </summary>
29+
/// <param name="nextMiddleware">The next middleware</param>
30+
/// <param name="options">Options to customize middleware</param>
31+
public AltairMiddleware(RequestDelegate nextMiddleware, GraphQLAltairOptions options)
32+
{
33+
_nextMiddleware = nextMiddleware ?? throw new ArgumentNullException(nameof(nextMiddleware));
34+
_options = options ?? throw new ArgumentNullException(nameof(options));
35+
}
36+
37+
/// <summary>
38+
/// Try to execute the logic of the middleware
39+
/// </summary>
40+
/// <param name="httpContext">The HttpContext</param>
41+
public Task Invoke(HttpContext httpContext)
42+
{
43+
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
44+
45+
return IsAltairRequest(httpContext.Request)
46+
? InvokeAltair(httpContext.Response)
47+
: _nextMiddleware(httpContext);
48+
}
49+
50+
private bool IsAltairRequest(HttpRequest httpRequest)
51+
{
52+
return HttpMethods.IsGet(httpRequest.Method) && httpRequest.Path.StartsWithSegments(_options.Path);
53+
}
54+
55+
private Task InvokeAltair(HttpResponse httpResponse)
56+
{
57+
httpResponse.ContentType = "text/html";
58+
httpResponse.StatusCode = 200;
59+
60+
// Initialize page model if null
61+
if (_pageModel == null)
62+
_pageModel = new AltairPageModel(_options);
63+
64+
var data = Encoding.UTF8.GetBytes(_pageModel.Render());
65+
return httpResponse.Body.WriteAsync(data, 0, data.Length);
66+
}
67+
}
68+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Microsoft.AspNetCore.Http;
2+
using System.Collections.Generic;
3+
4+
namespace GraphQL.Server.Ui.Altair
5+
{
6+
/// <summary>
7+
/// Options to customize <see cref="AltairMiddleware"/>
8+
/// </summary>
9+
public class GraphQLAltairOptions
10+
{
11+
/// <summary>
12+
/// The Altair GraphQL EndPoint to listen
13+
/// </summary>
14+
public PathString Path { get; set; } = "/ui/altair";
15+
16+
/// <summary>
17+
/// The GraphQL EndPoint
18+
/// </summary>
19+
public PathString GraphQLEndPoint { get; set; } = "/graphql";
20+
21+
/// <summary>
22+
/// Altair Headers Config
23+
/// </summary>
24+
public Dictionary<string, string> Headers { get; set; }
25+
}
26+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using Newtonsoft.Json;
2+
using System.IO;
3+
using System.Text;
4+
5+
namespace GraphQL.Server.Ui.Altair.Internal
6+
{
7+
// https://docs.microsoft.com/en-us/aspnet/core/mvc/razor-pages/?tabs=netcore-cli
8+
internal class AltairPageModel
9+
{
10+
private string _altairCSHtml;
11+
12+
private readonly GraphQLAltairOptions _options;
13+
14+
public AltairPageModel(GraphQLAltairOptions options)
15+
{
16+
_options = options;
17+
}
18+
19+
public string Render()
20+
{
21+
if (_altairCSHtml == null)
22+
{
23+
using (var manifestResourceStream = typeof(AltairPageModel).Assembly.GetManifestResourceStream("GraphQL.Server.Ui.Altair.Internal.altair.cshtml"))
24+
{
25+
using (var streamReader = new StreamReader(manifestResourceStream))
26+
{
27+
var builder = new StringBuilder(streamReader.ReadToEnd());
28+
29+
builder.Replace("@Model.GraphQLEndPoint", _options.GraphQLEndPoint);
30+
builder.Replace("@Model.AltairHeaders", JsonConvert.SerializeObject(_options.Headers));
31+
32+
_altairCSHtml = builder.ToString();
33+
}
34+
}
35+
}
36+
37+
return _altairCSHtml;
38+
}
39+
}
40+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<!doctype html>
2+
<html>
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<title>Altair</title>
7+
<base href="//cdn.jsdelivr.net/npm/altair-static/build/dist/">
8+
<meta name="viewport" content="width=device-width,initial-scale=1">
9+
<link rel="icon" type="image/x-icon" href="//cdn.jsdelivr.net/npm/altair-static/build/dist/favicon.ico">
10+
<link href="//cdn.jsdelivr.net/npm/altair-static/build/dist/styles.css" rel="stylesheet" />
11+
</head>
12+
13+
<body>
14+
<app-root>
15+
<style>
16+
.loading-screen {
17+
display: none;
18+
}
19+
20+
</style>
21+
<div class="loading-screen styled">
22+
<div class="loading-screen-inner">
23+
<div class="loading-screen-logo-container">
24+
<img src="assets/img/logo_350.svg" alt="Altair">
25+
</div>
26+
<div class="loading-screen-loading-indicator">
27+
<span class="loading-indicator-dot"></span>
28+
<span class="loading-indicator-dot"></span>
29+
<span class="loading-indicator-dot"></span>
30+
</div>
31+
</div>
32+
</div>
33+
</app-root>
34+
<script rel="preload" as="script" type="text/javascript" src="//cdn.jsdelivr.net/npm/altair-static/build/dist/runtime.js"></script>
35+
<script rel="preload" as="script" type="text/javascript" src="//cdn.jsdelivr.net/npm/altair-static/build/dist/polyfills.js"></script>
36+
<script rel="preload" as="script" type="text/javascript" src="//cdn.jsdelivr.net/npm/altair-static/build/dist/main.js"></script>
37+
38+
<script>
39+
var altairOptions = {
40+
endpointURL: window.location.protocol + "//" + window.location.host + "@Model.GraphQLEndPoint",
41+
subscriptionsEndpoint: (window.location.protocol === "http:" ? "ws://" : "wss://") + window.location.host + "@Model.GraphQLEndPoint",
42+
initialHeaders: @Model.AltairHeaders,
43+
};
44+
45+
window.addEventListener("load", function() {
46+
AltairGraphQL.init(altairOptions);
47+
});
48+
</script>
49+
</body>
50+
51+
</html>

src/Ui.Altair/Ui.Altair.csproj

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>netcoreapp3.0;netstandard2.0</TargetFrameworks>
5+
<AssemblyName>GraphQL.Server.Ui.Altair</AssemblyName>
6+
<RootNamespace>GraphQL.Server.Ui.Altair</RootNamespace>
7+
<Description>Altair GraphQL extension</Description>
8+
<PackageTags>Altair GraphQL</PackageTags>
9+
<PackageId>GraphQL.Server.Ui.Altair</PackageId>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<EmbeddedResource Include="Internal\altair.cshtml" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
18+
</ItemGroup>
19+
20+
<ItemGroup Condition="'$(IsNetCore30OnwardsTarget)' == 'True'">
21+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
22+
</ItemGroup>
23+
24+
<ItemGroup Condition="'$(IsNetCore30OnwardsTarget)' == 'False'">
25+
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="$(MicrosoftAspNetCoreHttpAbstractionsVersion)" />
26+
</ItemGroup>
27+
28+
</Project>

0 commit comments

Comments
 (0)