-
-
Notifications
You must be signed in to change notification settings - Fork 756
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support directives declared stitched schemas (#936)
- Loading branch information
1 parent
4e4c3a7
commit 795e4c0
Showing
15 changed files
with
421 additions
and
82 deletions.
There are no files selected for viewing
147 changes: 147 additions & 0 deletions
147
src/Stitching/Stitching.Tests/Merge/Handlers/DirectiveTypeMergeHandlerTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using HotChocolate.Language; | ||
using Snapshooter.Xunit; | ||
using Xunit; | ||
|
||
namespace HotChocolate.Stitching.Merge.Handlers | ||
{ | ||
public class DirectiveTypeMergeHandlerTests | ||
{ | ||
[Fact] | ||
public void Merge_SimpleIdenticalDirectives_TypeMerges() | ||
{ | ||
// arrange | ||
DocumentNode schema_a = | ||
Parser.Default.Parse("directive @test(arg: String) on OBJECT"); | ||
DocumentNode schema_b = | ||
Parser.Default.Parse("directive @test(arg: String) on OBJECT"); | ||
|
||
var types = new List<IDirectiveTypeInfo> | ||
{ | ||
new DirectiveTypeInfo(schema_a.Definitions.OfType<DirectiveDefinitionNode>().First(), | ||
new SchemaInfo("Schema_A", schema_a)), | ||
new DirectiveTypeInfo(schema_b.Definitions.OfType<DirectiveDefinitionNode>().First(), | ||
new SchemaInfo("Schema_B", schema_b)), | ||
}; | ||
|
||
var context = new SchemaMergeContext(); | ||
|
||
// act | ||
var typeMerger = new DirectiveTypeMergeHandler((c, t) => { }); | ||
typeMerger.Merge(context, types); | ||
|
||
// assert | ||
SchemaSyntaxSerializer.Serialize(context.CreateSchema()) | ||
.MatchSnapshot(); | ||
} | ||
|
||
[Fact] | ||
public void Merge_DifferentArguments_ThrowsException() | ||
{ | ||
// arrange | ||
DocumentNode schema_a = | ||
Parser.Default.Parse("directive @test(arg: Int) on OBJECT"); | ||
DocumentNode schema_b = | ||
Parser.Default.Parse("directive @test(arg: String) on OBJECT"); | ||
|
||
var types = new List<IDirectiveTypeInfo> | ||
{ | ||
new DirectiveTypeInfo(schema_a.Definitions.OfType<DirectiveDefinitionNode>().First(), | ||
new SchemaInfo("Schema_A", schema_a)), | ||
new DirectiveTypeInfo(schema_b.Definitions.OfType<DirectiveDefinitionNode>().First(), | ||
new SchemaInfo("Schema_B", schema_b)) | ||
}; | ||
|
||
var context = new SchemaMergeContext(); | ||
|
||
// act | ||
var typeMerger = new DirectiveTypeMergeHandler((c, t) => { }); | ||
|
||
Assert.Throws<InvalidOperationException>(() => typeMerger.Merge(context, types)); | ||
} | ||
|
||
[Fact] | ||
public void Merge_DifferentLocations_ThrowsException() | ||
{ | ||
// arrange | ||
DocumentNode schema_a = | ||
Parser.Default.Parse("directive @test(arg: String) on OBJECT | INTERFACE"); | ||
DocumentNode schema_b = | ||
Parser.Default.Parse("directive @test(arg: String) on OBJECT"); | ||
|
||
var types = new List<IDirectiveTypeInfo> | ||
{ | ||
new DirectiveTypeInfo(schema_a.Definitions.OfType<DirectiveDefinitionNode>().First(), | ||
new SchemaInfo("Schema_A", schema_a)), | ||
new DirectiveTypeInfo(schema_b.Definitions.OfType<DirectiveDefinitionNode>().First(), | ||
new SchemaInfo("Schema_B", schema_b)) | ||
}; | ||
|
||
var context = new SchemaMergeContext(); | ||
|
||
// act | ||
var typeMerger = new DirectiveTypeMergeHandler((c, t) => { }); | ||
|
||
Assert.Throws<InvalidOperationException>(() => typeMerger.Merge(context, types)); | ||
} | ||
|
||
[Fact] | ||
public void Merge_DifferentRepeatable_ThrowsException() | ||
{ | ||
// arrange | ||
DocumentNode schema_a = | ||
Parser.Default.Parse("directive @test(arg: String) repeatable on OBJECT"); | ||
DocumentNode schema_b = | ||
Parser.Default.Parse("directive @test(arg: String) on OBJECT"); | ||
|
||
var types = new List<IDirectiveTypeInfo> | ||
{ | ||
new DirectiveTypeInfo(schema_a.Definitions.OfType<DirectiveDefinitionNode>().First(), | ||
new SchemaInfo("Schema_A", schema_a)), | ||
new DirectiveTypeInfo(schema_b.Definitions.OfType<DirectiveDefinitionNode>().First(), | ||
new SchemaInfo("Schema_B", schema_b)) | ||
}; | ||
|
||
var context = new SchemaMergeContext(); | ||
|
||
// act | ||
var typeMerger = new DirectiveTypeMergeHandler((c, t) => { }); | ||
|
||
Assert.Throws<InvalidOperationException>(() => typeMerger.Merge(context, types)); | ||
} | ||
|
||
[Fact] | ||
public void Merge_ThreeDirectivessWhereTwoAreIdentical_TwoTypesAfterMerge() | ||
{ | ||
// arrange | ||
DocumentNode schema_a = | ||
Parser.Default.Parse("directive @test(arg: String) on OBJECT"); | ||
DocumentNode schema_b = | ||
Parser.Default.Parse("directive @test1(arg: String) on OBJECT"); | ||
DocumentNode schema_c = | ||
Parser.Default.Parse("directive @test(arg: String) on OBJECT"); | ||
|
||
var types = new List<IDirectiveTypeInfo> | ||
{ | ||
new DirectiveTypeInfo(schema_a.Definitions.OfType<DirectiveDefinitionNode>().First(), | ||
new SchemaInfo("Schema_A", schema_a)), | ||
new DirectiveTypeInfo(schema_b.Definitions.OfType<DirectiveDefinitionNode>().First(), | ||
new SchemaInfo("Schema_B", schema_b)), | ||
new DirectiveTypeInfo(schema_c.Definitions.OfType<DirectiveDefinitionNode>().First(), | ||
new SchemaInfo("Schema_C", schema_c)) | ||
}; | ||
|
||
var context = new SchemaMergeContext(); | ||
|
||
// act | ||
var typeMerger = new DirectiveTypeMergeHandler((c, t) => { }); | ||
typeMerger.Merge(context, types); | ||
|
||
// assert | ||
SchemaSyntaxSerializer.Serialize(context.CreateSchema()) | ||
.MatchSnapshot(); | ||
} | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
...napshots__/DirectiveTypeMergeHandlerTests.Merge_SimpleIdenticalDirectives_TypeMerges.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
directive @test(arg: String) on OBJECT |
3 changes: 3 additions & 0 deletions
3
...eTypeMergeHandlerTests.Merge_ThreeDirectivessWhereTwoAreIdentical_TwoTypesAfterMerge.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
directive @test(arg: String) on OBJECT | ||
|
||
directive @test1(arg: String) on OBJECT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
using HotChocolate.Language; | ||
|
||
namespace HotChocolate.Stitching.Merge | ||
{ | ||
public class DirectiveTypeInfo : IDirectiveTypeInfo | ||
{ | ||
public DirectiveTypeInfo( | ||
DirectiveDefinitionNode definition, | ||
ISchemaInfo schema) | ||
{ | ||
Definition = definition; | ||
Schema = schema; | ||
} | ||
|
||
public DirectiveDefinitionNode Definition { get; } | ||
|
||
public ISchemaInfo Schema { get; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
107 changes: 107 additions & 0 deletions
107
src/Stitching/Stitching/Merge/Handlers/DirectiveTypeMergeHandler.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using HotChocolate.Language; | ||
|
||
namespace HotChocolate.Stitching.Merge.Handlers | ||
{ | ||
internal class DirectiveTypeMergeHandler | ||
{ | ||
private readonly MergeDirectiveRuleDelegate _next; | ||
|
||
public DirectiveTypeMergeHandler(MergeDirectiveRuleDelegate next) | ||
{ | ||
_next = next ?? throw new ArgumentNullException(nameof(next)); | ||
} | ||
|
||
public void Merge( | ||
ISchemaMergeContext context, | ||
IReadOnlyList<IDirectiveTypeInfo> types) | ||
{ | ||
var notMerged = types.ToList(); | ||
|
||
while (notMerged.Count > 0) | ||
{ | ||
MergeNextType(context, notMerged); | ||
} | ||
} | ||
|
||
private void MergeNextType( | ||
ISchemaMergeContext context, | ||
List<IDirectiveTypeInfo> notMerged) | ||
{ | ||
IDirectiveTypeInfo left = notMerged[0]; | ||
|
||
var readyToMerge = new List<IDirectiveTypeInfo>(); | ||
readyToMerge.Add(left); | ||
|
||
for (int i = 1; i < notMerged.Count; i++) | ||
{ | ||
if (CanBeMerged(left.Definition, notMerged[i].Definition)) | ||
{ | ||
readyToMerge.Add(notMerged[i]); | ||
} | ||
} | ||
|
||
NameString name = readyToMerge[0].Definition.Name.Value; | ||
|
||
if (context.ContainsDirective(name)) | ||
{ | ||
throw new InvalidOperationException($"Unable to merge {name}, directive with this name already exists."); | ||
} | ||
|
||
MergeTypes(context, readyToMerge, name); | ||
notMerged.RemoveAll(readyToMerge.Contains); | ||
} | ||
|
||
protected void MergeTypes( | ||
ISchemaMergeContext context, | ||
IReadOnlyList<IDirectiveTypeInfo> types, | ||
NameString newTypeName) | ||
{ | ||
var definitions = types | ||
.Select(t => t.Definition) | ||
.ToList(); | ||
|
||
DirectiveDefinitionNode definition = | ||
definitions[0].Rename( | ||
newTypeName, | ||
types.Select(t => t.Schema.Name)); | ||
|
||
context.AddDirective(definition); | ||
} | ||
|
||
private static bool CanBeMerged(DirectiveDefinitionNode left, DirectiveDefinitionNode right) | ||
{ | ||
if (!left.Name.Value.Equals(right.Name.Value, StringComparison.Ordinal)) | ||
{ | ||
return false; | ||
} | ||
|
||
if (left.Locations.Count != right.Locations.Count) | ||
{ | ||
return false; | ||
} | ||
|
||
var leftLocations = left.Locations.Select(l => l.Value).OrderBy(l => l).ToList(); | ||
var rightLocations = right.Locations.Select(l => l.Value).OrderBy(l => l).ToList(); | ||
|
||
if (!leftLocations.SequenceEqual(rightLocations)) | ||
{ | ||
return false; | ||
} | ||
|
||
if (left.IsRepeatable != right.IsRepeatable) | ||
{ | ||
return false; | ||
} | ||
|
||
if (left.Arguments.Count != right.Arguments.Count) | ||
{ | ||
return false; | ||
} | ||
|
||
return ComplexTypeMergeHelpers.HasSameArguments(left.Arguments, right.Arguments); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
using HotChocolate.Language; | ||
|
||
namespace HotChocolate.Stitching.Merge | ||
{ | ||
public interface IDirectiveTypeInfo | ||
{ | ||
DirectiveDefinitionNode Definition { get; } | ||
ISchemaInfo Schema { get; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace HotChocolate.Stitching.Merge | ||
{ | ||
public delegate void MergeDirectiveRuleDelegate( | ||
ISchemaMergeContext context, | ||
IReadOnlyList<IDirectiveTypeInfo> types); | ||
} |
Oops, something went wrong.