Skip to content

Commit 4c2d1db

Browse files
Add BananaCakePop Package (#6198)
Co-authored-by: Michael Staib <[email protected]>
1 parent f162c41 commit 4c2d1db

27 files changed

+479
-606
lines changed

.build/Build.BananaCakePop.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
using System.Linq;
3+
using System.Net.Http;
4+
using System.Text.Json;
5+
using System.Threading.Tasks;
6+
using Microsoft.Build.Definition;
7+
using Microsoft.Build.Evaluation;
8+
using NuGet.Versioning;
9+
using Nuke.Common;
10+
using Serilog;
11+
12+
partial class Build
13+
{
14+
Target UpdateBananaCakePop => _ => _
15+
.Executes(async () =>
16+
{
17+
var latestVersion = await GetLatestVersion("BananaCakePop.Middleware");
18+
Log.Information($"Latest BCP Version: {latestVersion}");
19+
20+
var project = Project.FromFile(HotChocolateDirectoryBuildProps, new ProjectOptions());
21+
project.SetProperty("BananaCakePopVersion", latestVersion);
22+
project.Save();
23+
});
24+
25+
static async Task<string> GetLatestVersion(string packageName)
26+
{
27+
using var client = new HttpClient();
28+
29+
var url = $"https://api.nuget.org/v3-flatcontainer/{packageName.ToLower()}/index.json";
30+
31+
var response = await client.GetAsync(url);
32+
33+
if (response.IsSuccessStatusCode)
34+
{
35+
var jsonString = await response.Content.ReadAsStringAsync();
36+
return JsonDocument.Parse(jsonString)
37+
.RootElement.GetProperty("versions")
38+
.EnumerateArray()
39+
.Select(x => x.GetString())
40+
.Where(x => !x.Contains("insider"))
41+
.OrderByDescending(x => SemanticVersion.Parse(x))
42+
.First();
43+
}
44+
45+
throw new Exception($"Failed to retrieve package data: {response.StatusCode}");
46+
}
47+
}

.build/Build.Environment.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ partial class Build
2222

2323
AbsolutePath StarWarsTemplateNuSpec => RootDirectory / "templates" / "StarWars" / "HotChocolate.Templates.StarWars.nuspec";
2424

25+
AbsolutePath HotChocolateDirectoryBuildProps => SourceDirectory / "HotChocolate" / "Directory.Build.Props";
26+
2527
AbsolutePath StarWarsProj => RootDirectory / "templates" / "StarWars" / "content" / "StarWars.csproj";
2628
AbsolutePath EmptyServerTemplateNuSpec => RootDirectory / "templates" / "Server" / "HotChocolate.Templates.Server.nuspec";
2729
AbsolutePath EmptyServerProj => RootDirectory / "templates" / "Server" / "content" / "HotChocolate.Server.Template.csproj";

src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/EndpointRouteBuilderExtensions.cs

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
using Microsoft.AspNetCore.Http;
22
using Microsoft.AspNetCore.Routing;
33
using Microsoft.AspNetCore.Routing.Patterns;
4-
using Microsoft.Extensions.FileProviders;
54
using HotChocolate.AspNetCore;
65
using HotChocolate.AspNetCore.Extensions;
6+
using BananaCakePop.Middleware;
77
using static HotChocolate.AspNetCore.MiddlewareRoutingType;
88
using static Microsoft.AspNetCore.Routing.Patterns.RoutePatternFactory;
99

@@ -77,18 +77,15 @@ public static GraphQLEndpointConventionBuilder MapGraphQL(
7777
var pattern = Parse(path + "/{**slug}");
7878
var requestPipeline = endpointRouteBuilder.CreateApplicationBuilder();
7979
var schemaNameOrDefault = schemaName ?? Schema.DefaultName;
80-
var fileProvider = CreateFileProvider();
8180

8281
requestPipeline
8382
.UseCancellation()
8483
.UseMiddleware<WebSocketSubscriptionMiddleware>(schemaNameOrDefault)
8584
.UseMiddleware<HttpPostMiddleware>(schemaNameOrDefault)
8685
.UseMiddleware<HttpMultipartMiddleware>(schemaNameOrDefault)
87-
.UseMiddleware<HttpGetSchemaMiddleware>(schemaNameOrDefault, Integrated)
88-
.UseMiddleware<ToolDefaultFileMiddleware>(fileProvider, path)
89-
.UseMiddleware<ToolOptionsFileMiddleware>(path)
90-
.UseMiddleware<ToolStaticFileMiddleware>(fileProvider, path)
9186
.UseMiddleware<HttpGetMiddleware>(schemaNameOrDefault, path)
87+
.UseMiddleware<HttpGetSchemaMiddleware>(schemaNameOrDefault, Integrated)
88+
.UseBananaCakePop(path)
9289
.Use(_ => context =>
9390
{
9491
context.Response.StatusCode = 404;
@@ -323,9 +320,7 @@ public static IEndpointConventionBuilder MapGraphQLSchema(
323320

324321
requestPipeline
325322
.UseCancellation()
326-
.UseMiddleware<HttpGetSchemaMiddleware>(
327-
schemaNameOrDefault,
328-
Explicit)
323+
.UseMiddleware<HttpGetSchemaMiddleware>(schemaNameOrDefault, Explicit)
329324
.Use(_ => context =>
330325
{
331326
context.Response.StatusCode = 404;
@@ -391,13 +386,9 @@ public static BananaCakePopEndpointConventionBuilder MapBananaCakePop(
391386

392387
var pattern = Parse(toolPath + "/{**slug}");
393388
var requestPipeline = endpointRouteBuilder.CreateApplicationBuilder();
394-
var fileProvider = CreateFileProvider();
395389

396390
requestPipeline
397-
.UseCancellation()
398-
.UseMiddleware<ToolDefaultFileMiddleware>(fileProvider, toolPath)
399-
.UseMiddleware<ToolOptionsFileMiddleware>(toolPath)
400-
.UseMiddleware<ToolStaticFileMiddleware>(fileProvider, toolPath)
391+
.UseBananaCakePop(toolPath)
401392
.Use(_ => context =>
402393
{
403394
context.Response.StatusCode = 404;
@@ -407,7 +398,7 @@ public static BananaCakePopEndpointConventionBuilder MapBananaCakePop(
407398
var builder = endpointRouteBuilder
408399
.Map(pattern, requestPipeline.Build())
409400
.WithDisplayName("Banana Cake Pop Pipeline")
410-
.WithMetadata(new GraphQLEndpointOptions { GraphQLEndpoint = relativeRequestPath });
401+
.WithMetadata(new BananaCakePopOptions { GraphQLEndpoint = relativeRequestPath });
411402

412403
return new BananaCakePopEndpointConventionBuilder(builder);
413404
}
@@ -427,8 +418,10 @@ public static BananaCakePopEndpointConventionBuilder MapBananaCakePop(
427418
/// </returns>
428419
public static GraphQLEndpointConventionBuilder WithOptions(
429420
this GraphQLEndpointConventionBuilder builder,
430-
GraphQLServerOptions serverOptions) =>
431-
builder.WithMetadata(serverOptions);
421+
GraphQLServerOptions serverOptions)
422+
=> builder
423+
.WithMetadata(serverOptions)
424+
.WithMetadata(serverOptions.Tool.ToBcpOptions());
432425

433426
/// <summary>
434427
/// Specifies the GraphQL HTTP request options.
@@ -468,8 +461,11 @@ public static GraphQLHttpEndpointConventionBuilder WithOptions(
468461
/// </returns>
469462
public static BananaCakePopEndpointConventionBuilder WithOptions(
470463
this BananaCakePopEndpointConventionBuilder builder,
471-
GraphQLToolOptions toolOptions) =>
472-
builder.WithMetadata(new GraphQLServerOptions { Tool = toolOptions });
464+
GraphQLToolOptions toolOptions)
465+
{
466+
builder.Add(c => c.Metadata.Add(toolOptions.ToBcpOptions()));
467+
return builder;
468+
}
473469

474470
/// <summary>
475471
/// Specifies the GraphQL over Websocket options.
@@ -489,13 +485,6 @@ public static WebSocketEndpointConventionBuilder WithOptions(
489485
GraphQLSocketOptions socketOptions) =>
490486
builder.WithMetadata(new GraphQLServerOptions { Sockets = socketOptions });
491487

492-
private static IFileProvider CreateFileProvider()
493-
{
494-
var type = typeof(EndpointRouteBuilderExtensions);
495-
var resourceNamespace = typeof(MiddlewareBase).Namespace + ".Resources";
496-
return new EmbeddedFileProvider(type.Assembly, resourceNamespace);
497-
}
498-
499488
private static IApplicationBuilder UseCancellation(this IApplicationBuilder builder)
500489
=> builder.Use(next => async context =>
501490
{
@@ -508,4 +497,20 @@ private static IApplicationBuilder UseCancellation(this IApplicationBuilder buil
508497
// we just catch cancellations here and do nothing.
509498
}
510499
});
500+
501+
internal static BananaCakePopOptions ToBcpOptions(this GraphQLToolOptions options)
502+
=> new()
503+
{
504+
ServeMode = ServeMode.Version(options.ServeMode.Mode),
505+
Title = options.Title,
506+
Document = options.Document,
507+
UseBrowserUrlAsGraphQLEndpoint = options.UseBrowserUrlAsGraphQLEndpoint,
508+
GraphQLEndpoint = options.GraphQLEndpoint,
509+
IncludeCookies = options.IncludeCookies,
510+
HttpHeaders = options.HttpHeaders,
511+
UseGet = options.HttpMethod == DefaultHttpMethod.Get,
512+
Enable = options.Enable,
513+
GaTrackingId = options.GaTrackingId,
514+
DisableTelemetry = options.DisableTelemetry,
515+
};
511516
}

src/HotChocolate/AspNetCore/src/AspNetCore/Extensions/HttpContextExtensions.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,9 @@ o is GraphQLServerOptions options
1111
? options
1212
: null);
1313

14-
public static GraphQLToolOptions? GetGraphQLToolOptions(this HttpContext context)
15-
=> GetGraphQLServerOptions(context)?.Tool;
16-
1714
public static GraphQLSocketOptions? GetGraphQLSocketOptions(this HttpContext context)
1815
=> GetGraphQLServerOptions(context)?.Sockets;
1916

20-
public static GraphQLEndpointOptions? GetGraphQLEndpointOptions(this HttpContext context)
21-
=> context.GetEndpoint()?.Metadata.GetMetadata<GraphQLEndpointOptions>() ??
22-
(context.Items.TryGetValue(nameof(GraphQLEndpointOptions), out var o) &&
23-
o is GraphQLEndpointOptions options
24-
? options
25-
: null);
26-
2717
public static bool IsTracingEnabled(this HttpContext context)
2818
{
2919
var headers = context.Request.Headers;

src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLEndpointOptions.cs

Lines changed: 0 additions & 12 deletions
This file was deleted.

src/HotChocolate/AspNetCore/src/AspNetCore/GraphQLToolOptions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ namespace HotChocolate.AspNetCore;
77
/// </summary>
88
public sealed class GraphQLToolOptions
99
{
10+
/// <inheritdoc cref="GraphQLToolServeMode"/>
11+
public GraphQLToolServeMode ServeMode { get; set; } = GraphQLToolServeMode.Latest;
12+
1013
/// <summary>
1114
/// Gets or sets the website title.
1215
/// </summary>
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using BananaCakePop.Middleware;
2+
3+
namespace HotChocolate.AspNetCore;
4+
5+
/// <summary>
6+
/// Represents the different modes of serving the Banana Cake Pop GraphQL tool. This class enables
7+
/// serving the tool in a variety of predefined ways:
8+
/// <list type="bullet">
9+
/// <item>
10+
/// <description>
11+
/// <see cref="GraphQLToolServeMode.Latest"/>: Uses the latest version of the tool, served over the
12+
/// cdn.
13+
/// </description>
14+
/// </item>
15+
/// <item>
16+
/// <description>
17+
/// <see cref="GraphQLToolServeMode.Insider"/>: Uses the insider version of the tool, served over
18+
/// the CDN.
19+
/// </description>
20+
/// </item>
21+
/// <item>
22+
/// <description>
23+
/// <see cref="GraphQLToolServeMode.Embedded"/>: Uses the tool's embedded files in the package.
24+
/// </description>
25+
/// </item>
26+
/// </list>
27+
/// In addition, a specific version of the tool can be served over the CDN using the
28+
/// <see cref="GraphQLToolServeMode.Version(string)"/> method.
29+
/// <example>
30+
/// <para>
31+
/// The following example shows how to serve the embedded version of the tool:
32+
/// </para>
33+
/// <code>
34+
/// endpoints
35+
/// .MapGraphQL()
36+
/// .WithOptions(new GraphQLServerOptions
37+
/// {
38+
/// Tool = { ServeMode = GraphQLToolServeMode.Embedded }
39+
/// });
40+
/// </code>
41+
/// <para>
42+
/// Or when you want to serve the insider version of the tool:
43+
/// </para>
44+
/// <code>
45+
/// endpoints
46+
/// .MapGraphQL()
47+
/// .WithOptions(new GraphQLServerOptions
48+
/// {
49+
/// Tool = { ServeMode = GraphQLToolServeMode.Insider }
50+
/// });
51+
/// </code>
52+
/// <para>
53+
/// Or when you want to serve a specific version of the tool:
54+
/// </para>
55+
/// <code>
56+
/// endpoints
57+
/// .MapGraphQL()
58+
/// .WithOptions(new GraphQLServerOptions
59+
/// {
60+
/// Tool = { ServeMode = GraphQLToolServeMode.Version("5.0.8") }
61+
/// });
62+
/// </code>
63+
/// </example>
64+
/// </summary>
65+
public sealed class GraphQLToolServeMode
66+
{
67+
/// <summary>
68+
/// Initializes a new instance of the <see cref="GraphQLToolServeMode"/> class using the
69+
/// provided mode.
70+
/// </summary>
71+
/// <param name="mode">The mode to serve the GraphQL tool.</param>
72+
private GraphQLToolServeMode(string mode) { Mode = mode; }
73+
74+
/// <summary>
75+
/// Gets the current mode of serving the GraphQL tool.
76+
/// </summary>
77+
internal string Mode { get; }
78+
79+
/// <summary>
80+
/// Serves the GraphQL tool using the latest version available over the CDN.
81+
/// </summary>
82+
public static readonly GraphQLToolServeMode Latest = new(ServeMode.Constants.Latest);
83+
84+
/// <summary>
85+
/// Serves the GraphQL tool using the insider version available over the CDN.
86+
/// </summary>
87+
public static readonly GraphQLToolServeMode Insider = new(ServeMode.Constants.Insider);
88+
89+
/// <summary>
90+
/// Serves the GraphQL tool using the embedded files from the package.
91+
/// </summary>
92+
public static readonly GraphQLToolServeMode Embedded = new(ServeMode.Constants.Embedded);
93+
94+
/// <summary>
95+
/// Serves the GraphQL tool from a specific version available over the CDN.
96+
/// </summary>
97+
/// <param name="version">The version of the tool to serve.</param>
98+
/// <returns>
99+
/// A new <see cref="GraphQLToolServeMode"/> object for serving the specific version.
100+
/// </returns>
101+
public static GraphQLToolServeMode Version(string version) => new(version);
102+
}

src/HotChocolate/AspNetCore/src/AspNetCore/HotChocolate.AspNetCore.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<ItemGroup>
1212
<InternalsVisibleTo Include="HotChocolate.AspNetCore.Tests" />
1313
<InternalsVisibleTo Include="HotChocolate.AspNetCore.Tests.Utilities" />
14+
<InternalsVisibleTo Include="HotChocolate.AzureFunctions" />
1415
<InternalsVisibleTo Include="HotChocolate.AzureFunctions.IsolatedProcess" />
1516
</ItemGroup>
1617

@@ -21,6 +22,10 @@
2122
<ProjectReference Include="..\Transport.Sockets\HotChocolate.Transport.Sockets.csproj" />
2223
</ItemGroup>
2324

25+
<ItemGroup>
26+
<PackageReference Include="BananaCakePop.Middleware" Version="$(BananaCakePopVersion)" />
27+
</ItemGroup>
28+
2429
<ItemGroup>
2530
<FrameworkReference Include="Microsoft.AspNetCore.App" />
2631
</ItemGroup>

src/HotChocolate/AspNetCore/src/AspNetCore/HttpGetMiddleware.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,4 +241,4 @@ await WriteResultAsync(
241241
}
242242
}
243243
}
244-
}
244+
}

src/HotChocolate/AspNetCore/src/AspNetCore/Serialization/DefaultHttpRequestParser.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,4 @@ private IReadOnlyList<GraphQLRequest> ParseQuery(
211211

212212
return new[] { new GraphQLRequest(document, queryHash) };
213213
}
214-
}
214+
}

0 commit comments

Comments
 (0)