From 735b49d80bd81b986ff4679b9a0f7b28744e6b30 Mon Sep 17 00:00:00 2001 From: stanislav Date: Sun, 23 Jun 2024 12:29:41 +0300 Subject: [PATCH 1/2] Add support for C# constants. --- .../Resolver/GraphQLExtensions.cs | 36 ++++++++++++++++ .../Resolver/GraphQLQueryResolver.cs | 5 +++ src/ZeroQL.Tests/Core/TestExtensions.cs | 19 ++++++++- ...ueryTests.SupportForConstants.verified.txt | 6 +++ ...Tests.SupportForEnumConstants.verified.txt | 4 ++ .../SourceGeneration/QueryTests.cs | 42 +++++++++++++++---- 6 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 src/ZeroQL.SourceGenerators/Resolver/GraphQLExtensions.cs create mode 100644 src/ZeroQL.Tests/SourceGeneration/QueryTests.SupportForConstants.verified.txt create mode 100644 src/ZeroQL.Tests/SourceGeneration/QueryTests.SupportForEnumConstants.verified.txt diff --git a/src/ZeroQL.SourceGenerators/Resolver/GraphQLExtensions.cs b/src/ZeroQL.SourceGenerators/Resolver/GraphQLExtensions.cs new file mode 100644 index 0000000..5e26162 --- /dev/null +++ b/src/ZeroQL.SourceGenerators/Resolver/GraphQLExtensions.cs @@ -0,0 +1,36 @@ +using System.Linq; +using Microsoft.CodeAnalysis; + +namespace ZeroQL.SourceGenerators.Resolver; + +public static class GraphQLConstantResolver +{ + public static string ToGraphQL(ITypeSymbol symbol, object value) => + symbol switch + { + { SpecialType: SpecialType.System_String } => $@"""{value}""", + { TypeKind: TypeKind.Enum } => MaterializeEnum(symbol, value), + _ => value.ToString(), + }; + + private static string MaterializeEnum(ITypeSymbol symbol, object value) + { + if (symbol is not INamedTypeSymbol enumType) + { + return value.ToString(); + } + + var graphQLName = enumType + .GetMembers() + .OfType() + .FirstOrDefault(o => o.ConstantValue?.Equals(value) ?? false) + ?.GetAttributes() + .FirstOrDefault(o => o.AttributeClass?.Name == "GraphQLNameAttribute") + ?.ConstructorArguments + .FirstOrDefault() + .Value? + .ToString(); + + return graphQLName!; + } +} \ No newline at end of file diff --git a/src/ZeroQL.SourceGenerators/Resolver/GraphQLQueryResolver.cs b/src/ZeroQL.SourceGenerators/Resolver/GraphQLQueryResolver.cs index c653afb..d234a3f 100644 --- a/src/ZeroQL.SourceGenerators/Resolver/GraphQLQueryResolver.cs +++ b/src/ZeroQL.SourceGenerators/Resolver/GraphQLQueryResolver.cs @@ -434,6 +434,11 @@ private static Result HandleArgumentAsVariable(GraphQLResolveContext con return Failed(identifierName, Descriptors.GraphQLVariableShouldBeLocal); } + case ILocalSymbol { HasConstantValue: true } localSymbol: + { + var constantValue = localSymbol.ConstantValue!; + return GraphQLConstantResolver.ToGraphQL(localSymbol.Type, constantValue); + } case ILocalSymbol localSymbol: { var graphQLType = parameter.ToGraphQLType(); diff --git a/src/ZeroQL.Tests/Core/TestExtensions.cs b/src/ZeroQL.Tests/Core/TestExtensions.cs index 6122714..59e76b8 100644 --- a/src/ZeroQL.Tests/Core/TestExtensions.cs +++ b/src/ZeroQL.Tests/Core/TestExtensions.cs @@ -98,7 +98,9 @@ public static async Task CompileToRealAssemblyAsBytes(this Project proje if (error != null) { - throw new Exception(error.GetMessage()); + var message = error.GetMessage(); + var preview = error.Location.Preview(); + throw new Exception($"{message} {preview}"); } using var memoryStream = new MemoryStream(); @@ -177,4 +179,19 @@ public static SettingsTask Track(this SettingsTask settingsTask, string value, [ settingsTask.AddScrubber(o => o.Replace(value, name)); return settingsTask; } + + public static string Preview(this Location location) + { + var sourceTree = location.SourceTree!; + var span = location.GetLineSpan(); + var line = span.StartLinePosition.Line; + var character = span.StartLinePosition.Character; + var source = sourceTree + .ToString() + .Split('\r', '\n'); + + var lineWithPreview = source[line].Insert(character, "^"); + + return lineWithPreview; + } } \ No newline at end of file diff --git a/src/ZeroQL.Tests/SourceGeneration/QueryTests.SupportForConstants.verified.txt b/src/ZeroQL.Tests/SourceGeneration/QueryTests.SupportForConstants.verified.txt new file mode 100644 index 0000000..f211639 --- /dev/null +++ b/src/ZeroQL.Tests/SourceGeneration/QueryTests.SupportForConstants.verified.txt @@ -0,0 +1,6 @@ +{ + Query: mutation { addUser(firstName: "John", lastName: "Smith") { id } }, + Data: { + Value: 10 + } +} \ No newline at end of file diff --git a/src/ZeroQL.Tests/SourceGeneration/QueryTests.SupportForEnumConstants.verified.txt b/src/ZeroQL.Tests/SourceGeneration/QueryTests.SupportForEnumConstants.verified.txt new file mode 100644 index 0000000..f16ec0a --- /dev/null +++ b/src/ZeroQL.Tests/SourceGeneration/QueryTests.SupportForEnumConstants.verified.txt @@ -0,0 +1,4 @@ +{ + Query: mutation { addUserKindPascal(userKindPascal: Good)}, + Data: 12 +} \ No newline at end of file diff --git a/src/ZeroQL.Tests/SourceGeneration/QueryTests.cs b/src/ZeroQL.Tests/SourceGeneration/QueryTests.cs index f27f1b8..fdebe6e 100644 --- a/src/ZeroQL.Tests/SourceGeneration/QueryTests.cs +++ b/src/ZeroQL.Tests/SourceGeneration/QueryTests.cs @@ -180,14 +180,7 @@ public async Task QueryPreviewGenerated() .Where(o => o.Location.SourceTree!.ToString().Contains("Program")) .First(o => o.Id == Descriptors.GraphQLQueryPreview.Id); - var startLinePosition = queryPreview.Location.GetLineSpan().StartLinePosition; - var line = startLinePosition.Line; - var character = startLinePosition.Character; - var source = queryPreview.Location.SourceTree! - .ToString() - .Split('\r', '\n'); - - var lineWithPreview = source[line].Insert(character, "^"); + var lineWithPreview = queryPreview.Location.Preview(); await Verify(lineWithPreview); } @@ -432,6 +425,39 @@ public async Task SupportForVariablesPassedViaClosure() await Verify(response); } + + [Fact] + public async Task SupportForConstants() + { + var csharpQuery = """ + const string firstName = "John"; + const string lastName = "Smith"; + var response = await qlClient.Mutation(m => m.AddUser(firstName, lastName, o => o.Id)); + """; + + var project = await TestProject.Project + .ReplacePartOfDocumentAsync("Program.cs", (TestProject.FullLine, csharpQuery)); + + var response = await project.Execute(); + + await Verify(response); + } + + [Fact] + public async Task SupportForEnumConstants() + { + var csharpQuery = """ + const UserKindPascal kind = UserKindPascal.Good; + var response = await qlClient.Mutation(m => m.AddUserKindPascal(kind)); + """; + + var project = await TestProject.Project + .ReplacePartOfDocumentAsync("Program.cs", (TestProject.FullLine, csharpQuery)); + + var response = await project.Execute(); + + await Verify(response); + } [Fact] public async Task NamedArgumentsAreSupported() From 3db51be84b9fac5d8c165d44d2323d1f3483a532 Mon Sep 17 00:00:00 2001 From: stanislav Date: Sun, 23 Jun 2024 12:35:13 +0300 Subject: [PATCH 2/2] Cleanup. --- .../Resolver/{GraphQLExtensions.cs => GraphQLConstantResolver.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/ZeroQL.SourceGenerators/Resolver/{GraphQLExtensions.cs => GraphQLConstantResolver.cs} (100%) diff --git a/src/ZeroQL.SourceGenerators/Resolver/GraphQLExtensions.cs b/src/ZeroQL.SourceGenerators/Resolver/GraphQLConstantResolver.cs similarity index 100% rename from src/ZeroQL.SourceGenerators/Resolver/GraphQLExtensions.cs rename to src/ZeroQL.SourceGenerators/Resolver/GraphQLConstantResolver.cs