Skip to content

Commit 8f0d16a

Browse files
committed
2 parents 9deeae1 + 1b3509f commit 8f0d16a

8 files changed

Lines changed: 253 additions & 28 deletions

File tree

Tocsoft.GraphQLCodeGen.Cli/CodeGenerator.cs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Text;
1111
using System.Text.RegularExpressions;
1212
using System.Threading.Tasks;
13+
using Tocsoft.GraphQLCodeGen.Cli;
1314
using Tocsoft.GraphQLCodeGen.SchemaIntrospection;
1415
using static Tocsoft.GraphQLCodeGen.IntrospectedSchemeParser;
1516

@@ -19,9 +20,10 @@ internal class CodeGenerator
1920
{
2021
private readonly CodeGeneratorSettings settings;
2122
private readonly IEnumerable<SchemaIntrospection.IIntrosepctionProvider> introspectionProviders;
23+
private readonly ILogger logger;
2224

23-
public CodeGenerator(CodeGeneratorSettings settings)
24-
: this(settings, new IIntrosepctionProvider[] {
25+
public CodeGenerator(ILogger logger, CodeGeneratorSettings settings)
26+
: this(logger, settings, new IIntrosepctionProvider[] {
2527
new SchemaIntrospection.JsonIntrospection(),
2628
new SchemaIntrospection.HttpIntrospection(),
2729
new SchemaIntrospection.DllIntrospection(),
@@ -30,13 +32,14 @@ public CodeGenerator(CodeGeneratorSettings settings)
3032
{
3133
}
3234

33-
public CodeGenerator(CodeGeneratorSettings settings, IEnumerable<SchemaIntrospection.IIntrosepctionProvider> introspectionProviders)
35+
public CodeGenerator(ILogger logger, CodeGeneratorSettings settings, IEnumerable<SchemaIntrospection.IIntrosepctionProvider> introspectionProviders)
3436
{
37+
this.logger = logger;
3538
this.settings = settings;
3639
this.introspectionProviders = introspectionProviders;
3740
}
3841

39-
public async Task GenerateAsync()
42+
public async Task<bool> GenerateAsync()
4043
{
4144
IIntrosepctionProvider provider = this.introspectionProviders.Single(x => x.SchemaType == this.settings.Schema.SchemaType());
4245

@@ -53,12 +56,22 @@ public async Task GenerateAsync()
5356
// lets make a locatino index look up table and provide it
5457
ObjectModel.GraphQLDocument doc = Parse(sources);
5558

59+
if (doc.Errors.Any())
60+
{
61+
foreach (var error in doc.Errors)
62+
{
63+
logger.Error(error.ToString());
64+
}
65+
return false;
66+
}
67+
5668
Models.ViewModel model = new Models.ViewModel(doc, this.settings);
5769

5870
string fileResult = new TemplateEngine(this.settings.Templates).Generate(model);
5971

6072
Directory.CreateDirectory(Path.GetDirectoryName(this.settings.OutputPath));
6173
File.WriteAllText(this.settings.OutputPath, fileResult);
74+
return true;
6275
}
6376

6477
}
@@ -76,9 +89,9 @@ public class CodeGeneratorSettings
7689

7790
public class CodeGeneratorSettingsLoader
7891
{
79-
public CodeGeneratorSettingsLoader()
92+
public CodeGeneratorSettingsLoader(ILogger logger)
8093
{
81-
94+
this.logger = logger;
8295
}
8396

8497
private List<string> DefaultTemplates(string outputPath)
@@ -186,6 +199,8 @@ private void LoadSettingsTree(SimpleSourceFile file)
186199
}
187200

188201
static readonly Regex regex = new Regex(@"^\s*#!\s*([a-zA-Z.]+)\s*:\s*(\"".*\""|[^ ]+?)(?:\s|$)", RegexOptions.Multiline | RegexOptions.Compiled);
202+
private readonly ILogger logger;
203+
189204
private SimpleSourceFile Load(string path)
190205
{
191206
// path must be a real full path by here

Tocsoft.GraphQLCodeGen.Cli/IntrospectedSchemeParser.cs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,69 @@
44
using System.Collections.Generic;
55
using System.Linq;
66
using System.Text;
7+
using GraphQLParser.Exceptions;
78

89
namespace Tocsoft.GraphQLCodeGen
910
{
1011
internal class IntrospectedSchemeParser
1112
{
1213
public static GraphQLDocument Parse(IEnumerable<NamedSource> parts)
1314
{
15+
List<LocatedNamedSource> sources = new List<LocatedNamedSource>();
1416
try
1517
{
1618
StringBuilder sb = new StringBuilder();
17-
List<LocatedNamedSource> sources = new List<LocatedNamedSource>();
19+
int linecount = 0;
1820
foreach (NamedSource part in parts)
1921
{
2022
int start = sb.Length;
2123
string body = part.Body.Replace("\r", "");
24+
2225
sb.Append(body);
2326
sb.Append("\n");
2427
sources.Add(new LocatedNamedSource()
2528
{
29+
LineStartAt = linecount,
2630
StartAt = start,
2731
EndAt = sb.Length,
2832
Path = part.Path,
2933
Body = body
3034
});
35+
36+
linecount += body.Count(x => x == '\n') + 1;
3137
}
3238

3339
string final = sb.ToString();
34-
return new GraphQLDocument(new Parser(new Lexer()).Parse(new Source(final)), sources);
40+
var parser = new Parser(new Lexer());
41+
var parsedDocument = parser.Parse(new Source(final));
42+
43+
return new GraphQLDocument(parsedDocument, sources);
44+
}
45+
catch (GraphQLSyntaxErrorException ex)
46+
{
47+
var msg = ex.ToString();
48+
var trimedStart = msg.Substring("GraphQLParser.Exceptions.GraphQLSyntaxErrorException: Syntax Error GraphQL (".Length);
49+
var lineColSpliiter = trimedStart.IndexOf(':');
50+
var lineColEnder = trimedStart.IndexOf(')');
51+
int line = int.Parse(trimedStart.Substring(0, lineColSpliiter));
52+
int col = int.Parse(trimedStart.Substring(lineColSpliiter+1, lineColEnder - lineColSpliiter -1));
53+
54+
var endOfLine = trimedStart.IndexOf('\n');
55+
var message = trimedStart.Substring(lineColEnder + 1, endOfLine - lineColEnder - 1).Trim();
56+
57+
var source = sources.Where(x => x.LineStartAt <= line).OrderByDescending(x => x.LineStartAt).FirstOrDefault();
58+
return GraphQLDocument.Error(new GraphQLError
59+
{
60+
Path = source?.Path,
61+
Line = source == null ? (int?)null : line - source.LineStartAt,
62+
Column = col,
63+
Code = ErrorCodes.SyntaxError,
64+
Message = message
65+
});
3566
}
3667
catch (Exception ex)
3768
{
38-
throw;
69+
return GraphQLDocument.Error(ErrorCodes.UnhandledException, ex.ToString());
3970
}
4071
}
4172
public class LocatedNamedSource
@@ -44,6 +75,7 @@ public class LocatedNamedSource
4475
public int EndAt { get; set; }
4576
public string Path { get; set; }
4677
public string Body { get; set; }
78+
public int LineStartAt { get; internal set; }
4779
}
4880

4981
}

Tocsoft.GraphQLCodeGen.Cli/ObjectModel/GraphQLDocument.cs

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,61 @@
55
using System.Linq;
66
using System.Text;
77
using static Tocsoft.GraphQLCodeGen.IntrospectedSchemeParser;
8+
using GraphQLParser;
89

910
namespace Tocsoft.GraphQLCodeGen.ObjectModel
1011
{
12+
internal enum ErrorCodes
13+
{
14+
Unknown = 0,
15+
UnhandledException = 1,
16+
UnknownField = 2,
17+
SyntaxError = 3,
18+
}
19+
20+
internal class GraphQLError
21+
{
22+
23+
public string Message { get; set; }
24+
25+
public ErrorCodes Code { get; set; }
26+
27+
public string Path { get; internal set; }
28+
public int? Line { get; internal set; }
29+
public int? Column { get; internal set; }
30+
31+
public override string ToString()
32+
{
33+
if (!Line.HasValue)
34+
{
35+
return $"ERROR GQL{ ((int)Code):0000}: { Message}";
36+
}
37+
38+
39+
return $"{Path}({Line},{Column}): ERROR GQL{((int)Code):0000}: {Message}";
40+
}
41+
}
42+
1143
internal class GraphQLDocument
1244
{
45+
public static GraphQLDocument Error(ErrorCodes code, string message)
46+
{
47+
var doc = new GraphQLDocument();
48+
49+
doc.AddError(code, message);
50+
return doc;
51+
}
52+
public static GraphQLDocument Error(GraphQLError error)
53+
{
54+
var doc = new GraphQLDocument();
55+
doc.errors.Add(error);
56+
return doc;
57+
}
58+
59+
private GraphQLDocument()
60+
{
61+
62+
}
1363
public GraphQLDocument(GraphQLParser.AST.GraphQLDocument ast, IEnumerable<LocatedNamedSource> queryParts)
1464
{
1565
this.ast = ast;
@@ -28,6 +78,38 @@ public GraphQLDocument(GraphQLParser.AST.GraphQLDocument ast, IEnumerable<Locate
2878
i.Resolve(this);
2979
}
3080
}
81+
private List<GraphQLError> errors = new List<GraphQLError>();
82+
public IEnumerable<GraphQLError> Errors => errors;
83+
84+
internal void AddError(ErrorCodes code, string message)
85+
{
86+
errors.Add(new GraphQLError()
87+
{
88+
Code = code,
89+
Message = message
90+
});
91+
}
92+
93+
94+
internal void AddError(ErrorCodes code, string message, ASTNode node)
95+
{
96+
var location = node.Location;
97+
(LocatedNamedSource part, int offsetStart, int length) = ResolveNode(location);
98+
99+
var allTextBeforeError = part.Body.Substring(0, offsetStart);
100+
var lines = allTextBeforeError.Split('\n');
101+
var line = lines.Count();
102+
var column = lines.Last().Length + 1;
103+
104+
errors.Add(new GraphQLError()
105+
{
106+
Path = part.Path,
107+
Code = code,
108+
Message = message,
109+
Line = line,
110+
Column = column
111+
});
112+
}
31113

32114
public IEnumerable<Operation> Operations { get; }
33115

@@ -45,16 +127,21 @@ internal ValueTypeReference ResolveValueType(GraphQLType type)
45127
return result;
46128
}
47129

48-
internal (string query, string filename) ResolveQuery(GraphQLLocation location)
130+
internal (LocatedNamedSource part, int offset, int length) ResolveNode(GraphQLLocation location)
49131
{
50132
LocatedNamedSource part = this.QueryParts.Where(x => x.StartAt <= location.Start).OrderByDescending(x => x.StartAt).First();
51-
52133
int offsetStart = location.Start - part.StartAt;
53134
int length = location.End - location.Start;
54135
if (length + offsetStart > part.Body.Length)
55136
{
56137
length = part.Body.Length - offsetStart;
57138
}
139+
return (part, offsetStart, length);
140+
}
141+
142+
internal (string query, string filename) ResolveQuery(GraphQLLocation location)
143+
{
144+
(LocatedNamedSource part, int offsetStart, int length) = ResolveNode(location);
58145

59146
string text = part.Body.Substring(offsetStart, length);
60147

Tocsoft.GraphQLCodeGen.Cli/ObjectModel/Selections/FieldSelection.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ internal class FieldSelection
1212

1313
public string UniqueIdentifier => $"{this.Name}_{this.ScalerType}{this.Selection?.UniqueIdentifier}";
1414
public string Name { get; set; }
15-
private WellknownScalarType? ScalerType => (this.Type.Type.Type as ScalarType)?.WellknownType;
15+
private WellknownScalarType? ScalerType => (this.Type?.Type.Type as ScalarType)?.WellknownType;
1616
public Field Type { get; set; }
1717
public SetSelection Selection { get; set; }
1818

@@ -39,7 +39,7 @@ public FieldSelection(GraphQLFieldSelection op)
3939
// to deduplocate part sof
4040
}
4141

42-
internal void Resolve(GraphQLDocument doc, IGraphQLFieldCollection rootType)
42+
internal bool Resolve(GraphQLDocument doc, IGraphQLFieldCollection rootType)
4343
{
4444
if (this.op.Name.Value == "__typename")
4545
{
@@ -49,7 +49,12 @@ internal void Resolve(GraphQLDocument doc, IGraphQLFieldCollection rootType)
4949
else
5050
{
5151
// this is special we need to treat it as such
52-
this.Type = rootType.Fields.Single(x => x.Name == this.op.Name.Value);
52+
this.Type = rootType.Fields.SingleOrDefault(x => x.Name == this.op.Name.Value);
53+
if(this.Type == null)
54+
{
55+
doc.AddError(ErrorCodes.UnknownField, $"The field '{this.op.Name.Value}' in not a valid member of '{rootType.Name}'", this.op);
56+
return false;
57+
}
5358
}
5459
if(this.Selection != null)
5560
{
@@ -62,6 +67,7 @@ internal void Resolve(GraphQLDocument doc, IGraphQLFieldCollection rootType)
6267
// // if we have subselction then it must be an object type really, unless an interface will work instead ???
6368
// selection.Resolve(FieldType.Type.Type as IGraphQLFieldCollection);
6469
//}
70+
return true;
6571
}
6672
}
6773
}

0 commit comments

Comments
 (0)