Skip to content

Commit

Permalink
Fixed Apollo Active Query Persistence Flow (#1049)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelstaib authored Aug 30, 2019
1 parent afd4f59 commit 304cb8d
Show file tree
Hide file tree
Showing 35 changed files with 881 additions and 146 deletions.
330 changes: 328 additions & 2 deletions src/Core/Core.Tests/Execution/PersistedQueriesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Threading.Tasks;
using HotChocolate.Language;
using Microsoft.Extensions.DependencyInjection;
using Snapshooter;
using Snapshooter.Xunit;
using Xunit;

Expand Down Expand Up @@ -305,8 +306,236 @@ public async Task ActivePersistedQueries_SaveQuery_And_Execute()
result.ToJson().MatchSnapshot();
}

[InlineData(HashFormat.Base64)]
[InlineData(HashFormat.Hex)]
[Theory]
public async Task ActivePersistedQueries_SaveQuery_InvalidHash_Sha1(
HashFormat format)
{
// arrange
var serviceCollection = new ServiceCollection();

// configure presistence
serviceCollection.AddGraphQLSchema(b => b
.AddDocumentFromString("type Query { foo: String }")
.AddResolver("Query", "foo", "bar"));
serviceCollection.AddQueryExecutor(b => b
.AddSha1DocumentHashProvider(format)
.UseActivePersistedQueryPipeline());

// add in-memory query storage
serviceCollection.AddSingleton<InMemoryQueryStorage>();
serviceCollection.AddSingleton<IReadStoredQueries>(sp =>
sp.GetRequiredService<InMemoryQueryStorage>());
serviceCollection.AddSingleton<IWriteStoredQueries>(sp =>
sp.GetRequiredService<InMemoryQueryStorage>());

IServiceProvider services =
serviceCollection.BuildServiceProvider();

IQueryExecutor executor =
services.GetRequiredService<IQueryExecutor>();

var hashProvider =
services.GetRequiredService<IDocumentHashProvider>();
var storage = services.GetRequiredService<InMemoryQueryStorage>();

var query = new QuerySourceText("{ foo }");
string hash = hashProvider.ComputeHash(query.ToSpan());

// act and assert
IExecutionResult result = await executor.ExecuteAsync(
QueryRequestBuilder.New()
.SetQueryName(hash)
.AddExtension("persistedQuery",
new Dictionary<string, object>
{
{ hashProvider.Name, hash }
})
.Create());
result.MatchSnapshot(new SnapshotNameExtension(
"Query_Not_Found_" + format));

result = await executor.ExecuteAsync(
QueryRequestBuilder.New()
.SetQueryName(hash)
.SetQuery("{ foo }")
.AddExtension("persistedQuery",
new Dictionary<string, object>
{
{ hashProvider.Name, hash }
})
.Create());
result.MatchSnapshot(new SnapshotNameExtension(
"Query_Stored_" + format));

result = await executor.ExecuteAsync(
QueryRequestBuilder.New()
.SetQueryName(hash)
.AddExtension("persistedQuery",
new Dictionary<string, object>
{
{ hashProvider.Name, hash }
})
.Create());
result.MatchSnapshot(new SnapshotNameExtension(
"Query_Loaded_From_Cache_" + format));
}

[InlineData(HashFormat.Base64)]
[InlineData(HashFormat.Hex)]
[Theory]
public async Task ActivePersistedQueries_SaveQuery_InvalidHash_Sha256(
HashFormat format)
{
// arrange
var serviceCollection = new ServiceCollection();

// configure presistence
serviceCollection.AddGraphQLSchema(b => b
.AddDocumentFromString("type Query { foo: String }")
.AddResolver("Query", "foo", "bar"));
serviceCollection.AddQueryExecutor(b => b
.AddSha256DocumentHashProvider(format)
.UseActivePersistedQueryPipeline());

// add in-memory query storage
serviceCollection.AddSingleton<InMemoryQueryStorage>();
serviceCollection.AddSingleton<IReadStoredQueries>(sp =>
sp.GetRequiredService<InMemoryQueryStorage>());
serviceCollection.AddSingleton<IWriteStoredQueries>(sp =>
sp.GetRequiredService<InMemoryQueryStorage>());

IServiceProvider services =
serviceCollection.BuildServiceProvider();

IQueryExecutor executor =
services.GetRequiredService<IQueryExecutor>();

var hashProvider =
services.GetRequiredService<IDocumentHashProvider>();
var storage = services.GetRequiredService<InMemoryQueryStorage>();

var query = new QuerySourceText("{ foo }");
string hash = hashProvider.ComputeHash(query.ToSpan());

// act and assert
IExecutionResult result = await executor.ExecuteAsync(
QueryRequestBuilder.New()
.SetQueryName(hash)
.AddExtension("persistedQuery",
new Dictionary<string, object>
{
{ hashProvider.Name, hash }
})
.Create());
result.MatchSnapshot(new SnapshotNameExtension(
"Query_Not_Found_" + format));

result = await executor.ExecuteAsync(
QueryRequestBuilder.New()
.SetQueryName(hash)
.SetQuery("{ foo }")
.AddExtension("persistedQuery",
new Dictionary<string, object>
{
{ hashProvider.Name, hash }
})
.Create());
result.MatchSnapshot(new SnapshotNameExtension(
"Query_Stored_" + format));

result = await executor.ExecuteAsync(
QueryRequestBuilder.New()
.SetQueryName(hash)
.AddExtension("persistedQuery",
new Dictionary<string, object>
{
{ hashProvider.Name, hash }
})
.Create());
result.MatchSnapshot(new SnapshotNameExtension(
"Query_Loaded_From_Cache_" + format));
}

[InlineData(HashFormat.Base64)]
[InlineData(HashFormat.Hex)]
[Theory]
public async Task ActivePersistedQueries_SaveQuery_InvalidHash_MD5(
HashFormat format)
{
// arrange
var serviceCollection = new ServiceCollection();

// configure presistence
serviceCollection.AddGraphQLSchema(b => b
.AddDocumentFromString("type Query { foo: String }")
.AddResolver("Query", "foo", "bar"));
serviceCollection.AddQueryExecutor(b => b
.AddMD5DocumentHashProvider(format)
.UseActivePersistedQueryPipeline());

// add in-memory query storage
serviceCollection.AddSingleton<InMemoryQueryStorage>();
serviceCollection.AddSingleton<IReadStoredQueries>(sp =>
sp.GetRequiredService<InMemoryQueryStorage>());
serviceCollection.AddSingleton<IWriteStoredQueries>(sp =>
sp.GetRequiredService<InMemoryQueryStorage>());

IServiceProvider services =
serviceCollection.BuildServiceProvider();

IQueryExecutor executor =
services.GetRequiredService<IQueryExecutor>();

var hashProvider =
services.GetRequiredService<IDocumentHashProvider>();
var storage = services.GetRequiredService<InMemoryQueryStorage>();

var query = new QuerySourceText("{ foo }");
string hash = hashProvider.ComputeHash(query.ToSpan());

// act and assert
IExecutionResult result = await executor.ExecuteAsync(
QueryRequestBuilder.New()
.SetQueryName(hash)
.AddExtension("persistedQuery",
new Dictionary<string, object>
{
{ hashProvider.Name, hash }
})
.Create());
result.MatchSnapshot(new SnapshotNameExtension(
"Query_Not_Found_" + format));

result = await executor.ExecuteAsync(
QueryRequestBuilder.New()
.SetQueryName(hash)
.SetQuery("{ foo }")
.AddExtension("persistedQuery",
new Dictionary<string, object>
{
{ hashProvider.Name, hash }
})
.Create());
result.MatchSnapshot(new SnapshotNameExtension(
"Query_Stored_" + format));

result = await executor.ExecuteAsync(
QueryRequestBuilder.New()
.SetQueryName(hash)
.AddExtension("persistedQuery",
new Dictionary<string, object>
{
{ hashProvider.Name, hash }
})
.Create());
result.MatchSnapshot(new SnapshotNameExtension(
"Query_Loaded_From_Cache_" + format));
}

[Fact]
public async Task ActivePersistedQueries_SaveQuery_InvalidHash()
public async Task ActivePersistedQueries_Invalid_Hash()
{
// arrange
var serviceCollection = new ServiceCollection();
Expand All @@ -316,7 +545,7 @@ public async Task ActivePersistedQueries_SaveQuery_InvalidHash()
.AddDocumentFromString("type Query { foo: String }")
.AddResolver("Query", "foo", "bar"));
serviceCollection.AddQueryExecutor(b => b
.AddSha256DocumentHashProvider()
.AddSha256DocumentHashProvider(HashFormat.Hex)
.UseActivePersistedQueryPipeline());

// add in-memory query storage
Expand Down Expand Up @@ -356,6 +585,103 @@ public async Task ActivePersistedQueries_SaveQuery_InvalidHash()
result.ToJson().MatchSnapshot();
}

[Fact]
public async Task ActivePersistedQueries_Invalid_Hash_Type()
{
// arrange
var serviceCollection = new ServiceCollection();

// configure presistence
serviceCollection.AddGraphQLSchema(b => b
.AddDocumentFromString("type Query { foo: String }")
.AddResolver("Query", "foo", "bar"));
serviceCollection.AddQueryExecutor(b => b
.AddSha256DocumentHashProvider(HashFormat.Hex)
.UseActivePersistedQueryPipeline());

// add in-memory query storage
serviceCollection.AddSingleton<InMemoryQueryStorage>();
serviceCollection.AddSingleton<IReadStoredQueries>(sp =>
sp.GetRequiredService<InMemoryQueryStorage>());
serviceCollection.AddSingleton<IWriteStoredQueries>(sp =>
sp.GetRequiredService<InMemoryQueryStorage>());

IServiceProvider services =
serviceCollection.BuildServiceProvider();

IQueryExecutor executor =
services.GetRequiredService<IQueryExecutor>();

var hashProvider =
services.GetRequiredService<IDocumentHashProvider>();
var storage = services.GetRequiredService<InMemoryQueryStorage>();

var query = new QuerySourceText("{ foo }");
string hash = hashProvider.ComputeHash(query.ToSpan());

// act
IExecutionResult result = await executor.ExecuteAsync(
QueryRequestBuilder.New()
.SetQuery(query.Text)
.SetQueryName(hash)
.AddExtension("persistedQuery",
new Dictionary<string, object>
{
{ "sha1Hash", "123" }
})
.Create());

// assert
Assert.False(storage.WrittenQuery.HasValue);
result.ToJson().MatchSnapshot();
}

[Fact]
public async Task ActivePersistedQueries_No_PersistedQuery_Section_In_Request()
{
// arrange
var serviceCollection = new ServiceCollection();

// configure presistence
serviceCollection.AddGraphQLSchema(b => b
.AddDocumentFromString("type Query { foo: String }")
.AddResolver("Query", "foo", "bar"));
serviceCollection.AddQueryExecutor(b => b
.AddSha256DocumentHashProvider(HashFormat.Hex)
.UseActivePersistedQueryPipeline());

// add in-memory query storage
serviceCollection.AddSingleton<InMemoryQueryStorage>();
serviceCollection.AddSingleton<IReadStoredQueries>(sp =>
sp.GetRequiredService<InMemoryQueryStorage>());
serviceCollection.AddSingleton<IWriteStoredQueries>(sp =>
sp.GetRequiredService<InMemoryQueryStorage>());

IServiceProvider services =
serviceCollection.BuildServiceProvider();

IQueryExecutor executor =
services.GetRequiredService<IQueryExecutor>();

var hashProvider =
services.GetRequiredService<IDocumentHashProvider>();
var storage = services.GetRequiredService<InMemoryQueryStorage>();

var query = new QuerySourceText("{ foo }");
string hash = hashProvider.ComputeHash(query.ToSpan());

// act
IExecutionResult result = await executor.ExecuteAsync(
QueryRequestBuilder.New()
.SetQuery(query.Text)
.SetQueryName(hash)
.Create());

// assert
Assert.False(storage.WrittenQuery.HasValue);
result.ToJson().MatchSnapshot();
}

public class InMemoryQueryStorage
: IReadStoredQueries
, IWriteStoredQueries
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"data": {
"foo": "bar"
},
"extensions": {
"persistedQuery": {
"sha256Hash": "123",
"expectedHashValue": "1a4eb6a25bda520ded59f5d74567463b72620c586ac0b4656bdbd4875f5ec5e5",
"expectedHashType": "sha256Hash",
"expectedHashFormat": "Hex",
"persisted": false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"data": {
"foo": "bar"
},
"extensions": {
"persistedQuery": {
"sha256Hash": null,
"expectedHashValue": "1a4eb6a25bda520ded59f5d74567463b72620c586ac0b4656bdbd4875f5ec5e5",
"expectedHashType": "sha256Hash",
"expectedHashFormat": "Hex",
"persisted": false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"data": {
"foo": "bar"
}
}
Loading

0 comments on commit 304cb8d

Please sign in to comment.