Skip to content

Commit 243ed88

Browse files
authored
Migrate generator (#484)
* Migrate generator with integration tests
1 parent cffe88e commit 243ed88

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+4152
-0
lines changed
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System.Collections.Generic;
2+
using System.IO;
3+
using System.Linq;
4+
using System.Threading;
5+
using System.Threading.Tasks;
6+
using Microsoft.CodeAnalysis;
7+
using Microsoft.CodeAnalysis.CSharp.Syntax;
8+
using Microsoft.Extensions.DependencyInjection;
9+
using Microsoft.Extensions.DependencyInjection.Extensions;
10+
using Tanka.GraphQL.Generator.Core.Generators;
11+
using Tanka.GraphQL.SchemaBuilding;
12+
using Tanka.GraphQL.SDL;
13+
using Tanka.GraphQL.TypeSystem;
14+
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
15+
16+
namespace Tanka.GraphQL.Generator.Core
17+
{
18+
public class CodeGenerator
19+
{
20+
private readonly string _inputFile;
21+
private readonly string _targetNamespace;
22+
private readonly string _schemaName;
23+
24+
public CodeGenerator(string inputFile, string targetNamespace)
25+
{
26+
_inputFile = inputFile;
27+
_targetNamespace = targetNamespace;
28+
_schemaName = Path.GetFileNameWithoutExtension(inputFile);
29+
}
30+
31+
public async Task<CompilationUnitSyntax> Generate()
32+
{
33+
var schema = await LoadSchema();
34+
var nsName = _targetNamespace;
35+
36+
var unit = CompilationUnit()
37+
.WithUsings(List(GenerateUsings()))
38+
.WithLeadingTrivia(Comment("#nullable enable"))
39+
.WithMembers(SingletonList<MemberDeclarationSyntax>(
40+
NamespaceDeclaration(IdentifierName(nsName))
41+
.WithMembers(List(GenerateTypes(schema)))))
42+
.NormalizeWhitespace()
43+
.WithTrailingTrivia(
44+
CarriageReturnLineFeed,
45+
Comment("#nullable disable"));
46+
47+
return unit;
48+
}
49+
50+
private IEnumerable<UsingDirectiveSyntax> GenerateUsings()
51+
{
52+
return new[]
53+
{
54+
UsingDirective(ParseName("System")),
55+
UsingDirective(ParseName(typeof(IEnumerable<>).Namespace)),
56+
UsingDirective(ParseName(typeof(ValueTask<>).Namespace)),
57+
UsingDirective(ParseName(typeof(CancellationToken).Namespace)),
58+
UsingDirective(ParseName(typeof(IServiceCollection).Namespace)),
59+
UsingDirective(ParseName(typeof(ServiceCollectionDescriptorExtensions).Namespace)),
60+
UsingDirective(ParseName("Tanka.GraphQL")),
61+
UsingDirective(ParseName("Tanka.GraphQL.ValueResolution")),
62+
UsingDirective(ParseName("Tanka.GraphQL.TypeSystem")),
63+
UsingDirective(ParseName("Tanka.GraphQL.Server"))
64+
};
65+
}
66+
67+
private IEnumerable<MemberDeclarationSyntax> GenerateTypes(SchemaBuilder schema)
68+
{
69+
return schema.GetTypes<INamedType>()
70+
.SelectMany(type => GenerateType(type, schema))
71+
.Concat(GenerateSchema(schema))
72+
.Concat(GenerateServiceBuilder(schema));
73+
}
74+
75+
private IEnumerable<MemberDeclarationSyntax> GenerateServiceBuilder(SchemaBuilder schema)
76+
{
77+
yield return new ServicesBuilderGenerator(schema, _schemaName).Generate();
78+
yield return new ServiceCollectionExtensionGenerator(schema, _schemaName).Generate();
79+
}
80+
81+
private IEnumerable<MemberDeclarationSyntax> GenerateSchema(SchemaBuilder schema)
82+
{
83+
yield return new SchemaResolversGenerator(schema, _schemaName).Generate();
84+
}
85+
86+
private IEnumerable<MemberDeclarationSyntax> GenerateType(INamedType type, SchemaBuilder schema)
87+
{
88+
return new NamedTypeGenerator(type, schema).Generate().ToList();
89+
}
90+
91+
private Task<SchemaBuilder> LoadSchema()
92+
{
93+
var content = File.ReadAllText(_inputFile);
94+
var builder = new SchemaBuilder()
95+
//.Sdl(CodeDirectivesSdl)
96+
.Sdl(content);
97+
98+
return Task.FromResult(builder);
99+
}
100+
101+
/*public static string CodeDirectivesSdl = @"
102+
directive @gen(
103+
asAbstract: Boolean! = false,
104+
asProperty: Boolean! = false,
105+
clrType: String = null
106+
) on FIELD_DEFINITION
107+
";*/
108+
}
109+
}
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using Microsoft.CodeAnalysis;
5+
using Microsoft.CodeAnalysis.CSharp;
6+
using Microsoft.CodeAnalysis.CSharp.Syntax;
7+
using Tanka.GraphQL.SchemaBuilding;
8+
using Tanka.GraphQL.TypeSystem;
9+
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
10+
11+
namespace Tanka.GraphQL.Generator.Core
12+
{
13+
public class CodeModel
14+
{
15+
public static bool IsNullable(IType type)
16+
{
17+
if (type is NonNull)
18+
return false;
19+
20+
if (type is List list)
21+
return IsNullable(list.OfType);
22+
23+
return true;
24+
}
25+
26+
public static string SelectFieldTypeName(SchemaBuilder schema, ComplexType ownerType, KeyValuePair<string, IField> field)
27+
{
28+
if (schema.TryGetDirective("gen", out _))
29+
{
30+
if (field.Value.HasDirective("gen"))
31+
{
32+
var gen = field.Value.GetDirective("gen");
33+
34+
var clrType = gen.GetArgument<string>("clrType");
35+
if (!string.IsNullOrEmpty(clrType))
36+
return clrType;
37+
}
38+
}
39+
40+
return SelectTypeName(field.Value.Type);
41+
}
42+
43+
public static string SelectTypeName(IType type, bool nullable = true)
44+
{
45+
if (type is NonNull nonNull)
46+
{
47+
return SelectTypeName(nonNull.OfType, false);
48+
}
49+
50+
if (type is List list)
51+
{
52+
var ofType = SelectTypeName(list.OfType);
53+
54+
if (nullable)
55+
return $"IEnumerable<{ofType}>?";
56+
57+
return $"IEnumerable<{ofType}>";
58+
}
59+
60+
var typeName = SelectTypeName((INamedType)type);
61+
62+
if (nullable)
63+
return $"{typeName}?";
64+
65+
return typeName;
66+
}
67+
68+
public static string SelectTypeName(INamedType namedType)
69+
{
70+
return namedType switch
71+
{
72+
ScalarType scalar => SelectScalarTypeName(scalar),
73+
ObjectType objectType => SelectObjectTypeName(objectType),
74+
EnumType enumType => SelectEnumTypeName(enumType),
75+
InputObjectType inputObjectType=> SelectInputObjectTypeName(inputObjectType),
76+
InterfaceType interfaceType => SelectInterfaceTypeName(interfaceType),
77+
UnionType unionType => SelectUnionTypeName(unionType),
78+
_ => "object"
79+
};
80+
}
81+
82+
private static string SelectUnionTypeName(UnionType unionType)
83+
{
84+
return unionType.Name.ToModelInterfaceName();
85+
}
86+
87+
private static string SelectInterfaceTypeName(InterfaceType interfaceType)
88+
{
89+
return interfaceType.Name.ToModelInterfaceName();
90+
}
91+
92+
public static string SelectFieldTypeName(SchemaBuilder schema, InputObjectType inputObjectType, KeyValuePair<string, InputObjectField> fieldDefinition)
93+
{
94+
if (schema.TryGetDirective("gen", out _))
95+
{
96+
if (fieldDefinition.Value.HasDirective("gen"))
97+
{
98+
var gen = fieldDefinition.Value.GetDirective("gen");
99+
100+
var clrType = gen.GetArgument<string>("clrType");
101+
if (!string.IsNullOrEmpty(clrType))
102+
return clrType;
103+
}
104+
}
105+
106+
return SelectTypeName(fieldDefinition.Value.Type);
107+
}
108+
109+
private static string SelectEnumTypeName(EnumType objectType)
110+
{
111+
return objectType.Name.ToModelName();
112+
}
113+
114+
private static string SelectObjectTypeName(ObjectType objectType)
115+
{
116+
return objectType.Name.ToModelName();
117+
}
118+
119+
private static string SelectScalarTypeName(ScalarType scalar)
120+
{
121+
if (StandardScalarToClrType.TryGetValue(scalar.Name, out var value))
122+
{
123+
return value;
124+
}
125+
126+
return "object";
127+
}
128+
129+
private static string SelectInputObjectTypeName(InputObjectType inputObjectType)
130+
{
131+
return inputObjectType.Name.ToModelName();
132+
}
133+
134+
private static readonly Dictionary<string, string> StandardScalarToClrType = new Dictionary<string, string>()
135+
{
136+
[ScalarType.Float.Name] = "double",
137+
[ScalarType.Boolean.Name] = "bool",
138+
[ScalarType.ID.Name] = "string",
139+
[ScalarType.Int.Name] = "int",
140+
[ScalarType.String.Name] ="string"
141+
};
142+
143+
public static SyntaxTriviaList ToXmlComment(string text)
144+
{
145+
if (string.IsNullOrWhiteSpace(text))
146+
return SyntaxTriviaList.Empty;
147+
148+
var comment = $"/// <summary>{Environment.NewLine}"
149+
+ string.Join(Environment.NewLine, text.Select(line => $"/// {line}"))
150+
+ $"/// </summary>{Environment.NewLine}";
151+
return SyntaxFactory.ParseLeadingTrivia(comment);
152+
}
153+
154+
public static bool IsAbstract(SchemaBuilder schema, ComplexType ownerType, KeyValuePair<string, IField> field)
155+
{
156+
// Check for gen override directive
157+
if (schema.TryGetDirective("gen", out _))
158+
{
159+
if (field.Value.HasDirective("gen"))
160+
{
161+
var gen = field.Value.GetDirective("gen");
162+
163+
var asAbstract = gen.GetArgument<bool>("asAbstract");
164+
165+
if (asAbstract)
166+
return true;
167+
168+
var asProperty = gen.GetArgument<bool>("asProperty");
169+
170+
if (asProperty)
171+
return false;
172+
}
173+
}
174+
175+
var args = field.Value.Arguments;
176+
177+
// if field has arguments then automatically require implementation for it
178+
if (args.Any())
179+
return true;
180+
181+
var type = field.Value.Type;
182+
183+
// if complex type (Object, Interface) then requires implementation
184+
if (type.Unwrap() is ComplexType)
185+
return true;
186+
187+
// unions require implementation as they require the actual graph type to be
188+
// given
189+
if (type.Unwrap() is UnionType)
190+
return true;
191+
192+
if (schema.IsSubscriptionType(ownerType))
193+
return true;
194+
195+
return false;
196+
}
197+
198+
public static MemberDeclarationSyntax TypenameProperty(string name)
199+
{
200+
return PropertyDeclaration(
201+
PredefinedType(
202+
Token(SyntaxKind.StringKeyword)),
203+
Identifier("__Typename"))
204+
.WithModifiers(
205+
TokenList(
206+
Token(SyntaxKind.PublicKeyword)))
207+
.WithExpressionBody(
208+
ArrowExpressionClause(
209+
LiteralExpression(
210+
SyntaxKind.StringLiteralExpression,
211+
Literal(name))))
212+
.WithSemicolonToken(
213+
Token(SyntaxKind.SemicolonToken));
214+
}
215+
}
216+
}

0 commit comments

Comments
 (0)