Skip to content

Commit cf63ae3

Browse files
committed
Added full support for ?SDL download of the Schema (?SDL) and functioning Playground when configured correctly in the AzureFunction HttpTrigger and route bindings + Options!
1 parent b7f467e commit cf63ae3

14 files changed

Lines changed: 345 additions & 52 deletions

GraphQL.AzureFunctionsProxy.Tests/AzureFunctionGraphQLTestBase.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ public HttpRequest CreateGraphQLQueryRequest(AzureFunctionTestContext functionTe
2020

2121
return httpRequest;
2222
}
23-
24-
23+
2524
public void WriteLine(string text)
2625
{
2726
TestContext?.WriteLine(text);

GraphQL.AzureFunctionsProxy.Tests/AzureFunctions/HelloWorldFunctionsStartup.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ public override void Configure(IFunctionsHostBuilder builder)
3131
//Finally Initialize AzureFunctions Executor Proxy here...
3232
//You man Provide a specific SchemaName for multiple Functions (e.g. endpoints).
3333
//TODO: Test multiple SchemaNames...
34-
services.AddAzureFunctionsGraphQL();
34+
services.AddAzureFunctionsGraphQL((options) =>
35+
{
36+
options.AzureFunctionsGraphQLRoutePath = "/api/graphql";
37+
});
3538
}
3639
}
3740
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using GraphQL.AzureFunctionsProxy.Tests.AzureFunctionsTestFramework;
7+
using Microsoft.VisualStudio.TestTools.UnitTesting;
8+
using Newtonsoft.Json.Linq;
9+
using StarWars.AzureFunctions;
10+
11+
namespace GraphQL.AzureFunctionsProxy.Tests
12+
{
13+
[TestClass]
14+
public class GraphQLSdlAndPlaygroundTests : AzureFunctionGraphQLTestBase
15+
{
16+
[TestMethod]
17+
public async Task TestSDLRetrieval()
18+
{ //arrange
19+
using var functionTestCtx = AzureFunctionTestFactory.CreateFunctionTestContext<HelloWorldFunction>(new HelloWorldFunctionsStartup());
20+
21+
var queryStringValues = new List<KeyValuePair<string, string>>()
22+
{
23+
new KeyValuePair<string, string>("SDL", "")
24+
};
25+
26+
var httpRequest = functionTestCtx.CreateHttpGetRequest("", "", queryStringValues);
27+
28+
var helloWorldFunction = functionTestCtx.ResolveFunctionWithDependencies<HelloWorldFunction>();
29+
30+
//act
31+
var functionResult = await helloWorldFunction
32+
.Run(httpRequest, functionTestCtx.Logger, CancellationToken.None)
33+
.ConfigureAwait(false);
34+
35+
var sdlResult = functionTestCtx.ReadResponseContentAsString();
36+
37+
//assert
38+
Assert.AreEqual(functionTestCtx.HttpContext.Response.StatusCode, 200);
39+
Assert.IsNotNull(functionResult);
40+
Assert.IsNotNull(sdlResult, "Query Execution Failed");
41+
Assert.IsFalse(string.IsNullOrWhiteSpace(sdlResult));
42+
43+
Assert.IsTrue(sdlResult.Contains("schema {\r\n query: Query\r\n}"));
44+
Assert.IsTrue(sdlResult.Contains("type HelloWorldTranslation {\r\n language: String\r\n translation: String\r\n}"));
45+
}
46+
47+
[TestMethod]
48+
public async Task TestStaticFileRetrievalForManifestJson()
49+
{ //arrange
50+
using var functionTestCtx = AzureFunctionTestFactory.CreateFunctionTestContext<HelloWorldFunction>(new HelloWorldFunctionsStartup());
51+
52+
var httpRequest = functionTestCtx.CreateHttpGetRequest("/api/graphql/manifest.json", "application/json");
53+
54+
var helloWorldFunction = functionTestCtx.ResolveFunctionWithDependencies<HelloWorldFunction>();
55+
56+
//act
57+
var functionResult = await helloWorldFunction
58+
.Run(httpRequest, functionTestCtx.Logger, CancellationToken.None)
59+
.ConfigureAwait(false);
60+
61+
var responseBody = functionTestCtx.ReadResponseContentAsString();
62+
63+
//assert
64+
Assert.AreEqual(200, functionTestCtx.HttpContext.Response.StatusCode);
65+
Assert.IsNotNull(functionResult);
66+
Assert.IsNotNull(responseBody, "Query Execution Failed");
67+
Assert.IsFalse(string.IsNullOrWhiteSpace(responseBody));
68+
69+
Assert.IsTrue(responseBody.Contains("\"short_name\": \"Banana Cake Pop\""));
70+
Assert.IsTrue(responseBody.Contains("\"name\": \"Banana Cake Pop\""));
71+
}
72+
}
73+
}

GraphQL.AzureFunctionsProxy.Tests/_TestFrameworks/AzureFunctionsTestFramework/AzureFunctionTestContext.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,30 @@ public TFunction ResolveFunctionWithDependencies<TFunction>()
3535
return function;
3636
}
3737

38+
public HttpRequest CreateHttpGetRequest(string path, string contentType, List<KeyValuePair<string, string>> queryStringValues = null)
39+
{
40+
var httpRequest = CreateHttpRequest(
41+
HttpMethod.Get,
42+
requestPath: path,
43+
requestContentType: contentType,
44+
queryStringValues: queryStringValues
45+
);
46+
return httpRequest;
47+
}
48+
3849
public HttpRequest CreateHttpJsonPostRequest<TRequestPayload>(
3950
TRequestPayload requestPayload
4051
)
4152
{
4253
var requestBody = JsonConvert.SerializeObject(requestPayload);
43-
var httpRequest = CreateHttpRequest(HttpMethod.Post, requestBody, "application/json");
54+
var httpRequest = CreateHttpRequest(HttpMethod.Post, requestBody: requestBody, requestContentType: "application/json");
4455
return httpRequest;
4556
}
4657

4758
public HttpRequest CreateHttpRequest(
4859
HttpMethod httpMethod = null,
4960
string requestBody = null,
61+
string requestPath = "",
5062
string requestContentType = "application/json",
5163
List<KeyValuePair<string, string>> queryStringValues = null
5264
)
@@ -60,6 +72,7 @@ public HttpRequest CreateHttpRequest(
6072
//Initialize the Http Request
6173
//NOTE: Default Body is a NullStream...which ignores all Reads/Writes.
6274
var httpRequest = this.HttpContext.Request;
75+
httpRequest.Path = new PathString(requestPath);
6376
httpRequest.Method = httpMethod?.Method ?? HttpMethod.Get.Method;
6477
httpRequest.Query = CreateQueryCollection(queryStringValues);
6578

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2-
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=QL/@EntryIndexedValue">QL</s:String></wpf:ResourceDictionary>
2+
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=QL/@EntryIndexedValue">QL</s:String>
3+
<s:Boolean x:Key="/Default/UserDictionary/Words/=GRAPHQL/@EntryIndexedValue">True</s:Boolean>
4+
<s:Boolean x:Key="/Default/UserDictionary/Words/=Serverless/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

GraphQL.AzureFunctionsProxy/GraphQL.AzureFunctionsProxy.csproj

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,21 @@
22

33
<PropertyGroup>
44
<TargetFramework>netcoreapp3.1</TargetFramework>
5-
<Version>11.0.4.1</Version>
5+
<Version>11.0.4.2</Version>
66
<Authors>BBernard / CajunCoding</Authors>
77
<Company>CajunCoding</Company>
88
<Description>This is a extension package for HotChocolate GraphQL framework to enable execution within AzureFunctions using the new v11 API. Provides very easy integration with Azure Functions with maximum support for out-of-the-box HotChocolate functionality.</Description>
99
<PackageLicenseExpression>MIT</PackageLicenseExpression>
1010
<PackageProjectUrl>https://github.com/cajuncoding/HotChocolate.AzureFunctionsProxy</PackageProjectUrl>
1111
<RepositoryUrl>https://github.com/cajuncoding/HotChocolate.AzureFunctionsProxy</RepositoryUrl>
1212
<PackageTags>graphql, graph-ql, hotchocolate, azure, functions, serverless</PackageTags>
13-
<PackageReleaseNotes>- Added ConfigureAwait(false) to all awaits for performance.
14-
- Bumped to HC v11.0.4
13+
<PackageReleaseNotes>- Added support for download of the Schema (?SDL)
14+
- Added support for functioning Playground (when configured correctly iin the AzureFunction HttpTrigger)
15+
- Maintained current version compatibility with HC v11.0.4
1516

1617
Prior Releases Notes:
18+
- Added ConfigureAwait(false) to all awaits for performance.
19+
- Bumped to HC v11.0.4
1720
- Bump to HotChocolate v11.0.1 which now resolves a bug that we helped identify with interfaces in the initial release of v11.0.0.
1821
- Updated to support namespace changes in HotChocolate v11 rc.03 with synced version here as v11.0.0.3. Bumped HotChocolate version to v11-rc.03.
1922
- Prior release Changed Repo &amp; Package names to eliminate conflict risks with the core HotChocolate packages.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace HotChocolate.AzureFunctionsProxy
6+
{
7+
public class GraphQLAzureFunctionsConfigOptions
8+
{
9+
public string AzureFunctionsGraphQLRoutePath { get; set; } = "/api/graphql";
10+
public bool EnableSchemaDefinitionMiddleware { get; set; } = true;
11+
public bool EnablePlayground { get; set; } = true;
12+
public bool EnableGetRequestMiddleware { get; set; } = true;
13+
}
14+
}

GraphQL.AzureFunctionsProxy/GraphQLAzureFunctionsExecutorProxyV11.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,19 @@ public class GraphQLAzureFunctionsExecutorProxyV11 : IGraphQLAzureFunctionsExecu
2323
protected GraphQLAzureFunctionsMiddlewareProxy AzureFunctionsMiddlewareProxy { get; }
2424

2525
public GraphQLAzureFunctionsExecutorProxyV11(
26-
IRequestExecutorResolver graphqlExecutorResolver,
27-
IHttpResultSerializer graphqlResultSerializer,
28-
IHttpRequestParser graphqlRequestParser,
29-
NameString schemaName = default
26+
IRequestExecutorResolver graphQLExecutorResolver,
27+
IHttpResultSerializer graphQLResultSerializer,
28+
IHttpRequestParser graphQLRequestParser,
29+
NameString schemaName = default,
30+
GraphQLAzureFunctionsConfigOptions options = null
3031
)
3132
{
3233
this.AzureFunctionsMiddlewareProxy = new GraphQLAzureFunctionsMiddlewareProxy(
33-
graphqlExecutorResolver,
34-
graphqlResultSerializer,
35-
graphqlRequestParser,
36-
schemaName
34+
graphQLExecutorResolver,
35+
graphQLResultSerializer,
36+
graphQLRequestParser,
37+
schemaName,
38+
options
3739
);
3840
}
3941

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
using HotChocolate.AspNetCore.Serialization;
1+
using System;
2+
using HotChocolate.AspNetCore;
3+
using HotChocolate.AspNetCore.Serialization;
24
using HotChocolate.AzureFunctionsProxy;
35
using HotChocolate.Execution;
46
using Microsoft.Extensions.DependencyInjection;
@@ -7,26 +9,55 @@ namespace HotChocolate.AzureFunctionsProxy
79
{
810
public static class AzureFunctionsMiddlewareExtensions
911
{
12+
/// <summary>
13+
/// Initialize an AzureFunctionsGraphQL Middleware for the optionally specified SchemaName.
14+
/// Default Schema name will be used if not specified.
15+
/// </summary>
16+
/// <param name="serviceCollection"></param>
17+
/// <param name="configureOptions"></param>
18+
/// <returns></returns>
19+
public static IServiceCollection AddAzureFunctionsGraphQL(this IServiceCollection serviceCollection,
20+
Action<GraphQLAzureFunctionsConfigOptions> configureOptions
21+
)
22+
{
23+
return serviceCollection.AddAzureFunctionsGraphQL(default, configureOptions: configureOptions);
24+
}
25+
1026
/// <summary>
1127
/// Initialize an AzureFunctionsGraphQL Middleware for the optionally specified SchemaName.
1228
/// Default Schema name will be used if not specified.
1329
/// </summary>
1430
/// <param name="serviceCollection"></param>
1531
/// <param name="schemaName"></param>
32+
/// <param name="configureOptions"></param>
1633
/// <returns></returns>
17-
public static IServiceCollection AddAzureFunctionsGraphQL(this IServiceCollection serviceCollection, NameString schemaName = default)
34+
public static IServiceCollection AddAzureFunctionsGraphQL(this IServiceCollection serviceCollection,
35+
NameString schemaName = default,
36+
Action<GraphQLAzureFunctionsConfigOptions> configureOptions = null
37+
)
1838
{
1939
//serviceCollection.AddAzureFunctionsGraphQL(new AzureFunctionsMiddlewareOptions());
2040
serviceCollection.AddSingleton<IGraphQLAzureFunctionsExecutorProxy, GraphQLAzureFunctionsExecutorProxyV11>(
2141
provider => new GraphQLAzureFunctionsExecutorProxyV11(
2242
provider.GetService<IRequestExecutorResolver>(),
2343
provider.GetService<IHttpResultSerializer>(),
2444
provider.GetService<IHttpRequestParser>(),
25-
schemaName
45+
schemaName,
46+
GetConfiguredOptions(configureOptions)
2647
)
2748
);
2849

2950
return serviceCollection;
3051
}
52+
53+
private static GraphQLAzureFunctionsConfigOptions GetConfiguredOptions(Action<GraphQLAzureFunctionsConfigOptions> optionsAction)
54+
{
55+
if (optionsAction == null)
56+
return null;
57+
58+
var options = new GraphQLAzureFunctionsConfigOptions();
59+
optionsAction?.Invoke(options);
60+
return options;
61+
}
3162
}
3263
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//using System;
2+
//using System.Collections.Generic;
3+
//using System.Text;
4+
//using HotChocolate.AspNetCore;
5+
6+
//namespace GraphQL.AzureFunctionsProxy
7+
//{
8+
// public class GraphQLAzureFunctionsMiddlewarePipelineShim
9+
// {
10+
// public void RegisterMiddleware(MiddlewareBase middleware)
11+
// {
12+
13+
// }
14+
15+
// }
16+
17+
// public class MiddlewareShim
18+
// {
19+
// public MiddlewareBase Next { get; set; }
20+
21+
// protected HttpMiddleware(HttpMiddleware next)
22+
// {
23+
// this.Next = next;
24+
// }
25+
26+
// protected HttpMiddleware()
27+
// {
28+
29+
// }
30+
31+
// public abstract Task InvokeAsync(IHttpFunctionContext context);
32+
// }
33+
//}

0 commit comments

Comments
 (0)