Skip to content

Commit b10640d

Browse files
Rewrite source generator to be incremental (Copilot)
1 parent 79ede78 commit b10640d

File tree

1 file changed

+55
-73
lines changed

1 file changed

+55
-73
lines changed

src/Certify.SourceGenerators/PublicAPISourceGenerator.cs

Lines changed: 55 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using Certify.SourceGenerators;
66
using Microsoft.CodeAnalysis;
77
using Microsoft.CodeAnalysis.Text;
8+
using Microsoft.CodeAnalysis.CSharp.Syntax;
9+
using Microsoft.CodeAnalysis.CSharp;
810

911
namespace SourceGenerator
1012
{
@@ -16,7 +18,7 @@ public class GeneratedAPI
1618
public string PublicAPIController { get; set; } = string.Empty;
1719

1820
public string PublicAPIRoute { get; set; } = string.Empty;
19-
public List<PermissionSpec> RequiredPermissions { get; set; } = [];
21+
public List<PermissionSpec> RequiredPermissions { get; set; } = new List<PermissionSpec>();
2022
public bool UseManagementAPI { get; set; } = false;
2123
public string ManagementHubCommandType { get; set; } = string.Empty;
2224
public string ServiceAPIRoute { get; set; } = string.Empty;
@@ -34,47 +36,53 @@ public PermissionSpec(string resourceType, string action)
3436
Action = action;
3537
}
3638
}
39+
3740
[Generator]
38-
[System.Diagnostics.CodeAnalysis.SuppressMessage("MicrosoftCodeAnalysisCompatibility", "RS1042:Implementations of this interface are not allowed", Justification = "Source generator needs ported to incremental generator")]
39-
public class PublicAPISourceGenerator : ISourceGenerator
41+
public class PublicAPISourceGenerator : IIncrementalGenerator
4042
{
41-
public void Execute(GeneratorExecutionContext context)
43+
public void Initialize(IncrementalGeneratorInitializationContext context)
4244
{
45+
var compilationProvider = context.CompilationProvider;
46+
var apiDefinitions = context.CompilationProvider
47+
.Select((compilation, _) => ApiMethods.GetApiDefinitions());
4348

44-
// get list of items we want to generate for our API glue
45-
var list = ApiMethods.GetApiDefinitions();
46-
47-
Debug.WriteLine(context.Compilation.AssemblyName);
49+
var combined = compilationProvider.Combine(apiDefinitions);
4850

49-
foreach (var config in list)
51+
context.RegisterSourceOutput(combined, (spc, source) =>
5052
{
51-
var paramSet = config.Params.ToList();
52-
paramSet.Add(new KeyValuePair<string, string>("authContext", "AuthContext"));
53-
var apiParamDecl = paramSet.Any() ? string.Join(", ", paramSet.Select(p => $"{p.Value} {p.Key}")) : "";
54-
var apiParamDeclWithoutAuthContext = config.Params.Any() ? string.Join(", ", config.Params.Select(p => $"{p.Value} {p.Key}")) : "";
53+
var compilation = source.Left;
54+
var list = source.Right;
55+
var assemblyName = compilation.AssemblyName;
5556

56-
var apiParamCall = paramSet.Any() ? string.Join(", ", paramSet.Select(p => $"{p.Key}")) : "";
57-
var apiParamCallWithoutAuthContext = config.Params.Any() ? string.Join(", ", config.Params.Select(p => $"{p.Key}")) : "";
58-
59-
if (context.Compilation.AssemblyName.EndsWith("Hub.Api") && config.PublicAPIController != null)
57+
foreach (var config in list)
6058
{
61-
ImplementPublicAPI(context, config, apiParamDeclWithoutAuthContext, apiParamDecl, apiParamCall);
59+
var paramSet = config.Params.ToList();
60+
paramSet.Add(new KeyValuePair<string, string>("authContext", "AuthContext"));
61+
var apiParamDecl = paramSet.Any() ? string.Join(", ", paramSet.Select(p => $"{p.Value} {p.Key}")) : "";
62+
var apiParamDeclWithoutAuthContext = config.Params.Any() ? string.Join(", ", config.Params.Select(p => $"{p.Value} {p.Key}")) : "";
63+
64+
var apiParamCall = paramSet.Any() ? string.Join(", ", paramSet.Select(p => $"{p.Key}")) : "";
65+
var apiParamCallWithoutAuthContext = config.Params.Any() ? string.Join(", ", config.Params.Select(p => $"{p.Key}")) : "";
66+
67+
if (assemblyName.EndsWith("Hub.Api") && config.PublicAPIController != null)
68+
{
69+
ImplementPublicAPI(spc, config, apiParamDeclWithoutAuthContext, apiParamDecl, apiParamCall);
70+
}
71+
72+
if (assemblyName.EndsWith("Certify.UI.Blazor"))
73+
{
74+
ImplementAppModel(spc, config, apiParamDeclWithoutAuthContext, apiParamCallWithoutAuthContext);
75+
}
76+
77+
if (assemblyName.EndsWith("Certify.Client") && !config.UseManagementAPI)
78+
{
79+
ImplementInternalAPIClient(spc, config, apiParamDecl, apiParamCall);
80+
}
6281
}
63-
64-
if (context.Compilation.AssemblyName.EndsWith("Certify.UI.Blazor"))
65-
{
66-
ImplementAppModel(context, config, apiParamDeclWithoutAuthContext, apiParamCallWithoutAuthContext);
67-
}
68-
69-
if (context.Compilation.AssemblyName.EndsWith("Certify.Client") && !config.UseManagementAPI)
70-
{
71-
// for methods which directly call the backend service (e.g. main server settings), implement the client API
72-
ImplementInternalAPIClient(context, config, apiParamDecl, apiParamCall);
73-
}
74-
}
82+
});
7583
}
7684

77-
private static void ImplementAppModel(GeneratorExecutionContext context, GeneratedAPI config, string apiParamDeclWithoutAuthContext, string apiParamCallWithoutAuthContext)
85+
private static void ImplementAppModel(SourceProductionContext context, GeneratedAPI config, string apiParamDeclWithoutAuthContext, string apiParamCallWithoutAuthContext)
7886
{
7987
context.AddSource($"AppModel.{config.OperationName}.g.cs", SourceText.From($@"
8088
using System.Collections.Generic;
@@ -83,20 +91,20 @@ private static void ImplementAppModel(GeneratorExecutionContext context, Generat
8391
using Certify.Models.Providers;
8492
using Certify.Models.Hub;
8593
86-
namespace Certify.UI.Client.Core
94+
namespace Certify.UI.Client.Core
95+
{{
96+
public partial class AppModel
8797
{{
88-
public partial class AppModel
98+
public async Task<{config.ReturnType}> {config.OperationName}({apiParamDeclWithoutAuthContext})
8999
{{
90-
public async Task<{config.ReturnType}> {config.OperationName}({apiParamDeclWithoutAuthContext})
91-
{{
92-
return await _api.{config.OperationName}Async({apiParamCallWithoutAuthContext});
93-
}}
100+
return await _api.{config.OperationName}Async({apiParamCallWithoutAuthContext});
94101
}}
95102
}}
103+
}}
96104
", Encoding.UTF8));
97105
}
98106

99-
private static void ImplementPublicAPI(GeneratorExecutionContext context, GeneratedAPI config, string apiParamDeclWithoutAuthContext, string apiParamDecl, string apiParamCall)
107+
private static void ImplementPublicAPI(SourceProductionContext context, GeneratedAPI config, string apiParamDeclWithoutAuthContext, string apiParamDecl, string apiParamCall)
100108
{
101109
var publicApiSrc = $@"
102110
@@ -112,7 +120,6 @@ private static void ImplementPublicAPI(GeneratorExecutionContext context, Genera
112120
using Certify.Models;
113121
using Certify.Models.Hub;
114122
115-
116123
namespace Certify.Server.Hub.Api.Controllers
117124
{{
118125
public partial class {config.PublicAPIController}Controller
@@ -134,8 +141,7 @@ public partial class {config.PublicAPIController}Controller
134141
return new OkObjectResult(result);
135142
}}
136143
}}
137-
}};
138-
";
144+
}}";
139145

140146
if (config.RequiredPermissions.Any())
141147
{
@@ -145,9 +151,7 @@ public partial class {config.PublicAPIController}Controller
145151
fragment += $@"
146152
if (!await IsAuthorized(_client, ""{perm.ResourceType}"" , ""{perm.Action}""))
147153
{{
148-
{{
149-
return Unauthorized();
150-
}}
154+
return Unauthorized();
151155
}}
152156
";
153157
}
@@ -161,8 +165,6 @@ public partial class {config.PublicAPIController}Controller
161165

162166
context.AddSource($"{config.PublicAPIController}Controller.{config.OperationName}.g.cs", SourceText.From(publicApiSrc, Encoding.UTF8));
163167

164-
// Management API service
165-
166168
if (!string.IsNullOrEmpty(config.ManagementHubCommandType))
167169
{
168170
var src = $@"
@@ -192,13 +194,12 @@ public partial class ManagementAPI
192194
return await PerformInstanceCommandTaskWithResult<{config.ReturnType}>(instanceId, args, ""{config.ManagementHubCommandType}"") ?? [];
193195
}}
194196
}}
195-
}}
196-
";
197+
}}";
197198
context.AddSource($"ManagementAPI.{config.OperationName}.g.cs", SourceText.From(src, Encoding.UTF8));
198199
}
199200
}
200201

201-
private static void ImplementInternalAPIClient(GeneratorExecutionContext context, GeneratedAPI config, string apiParamDecl, string apiParamCall)
202+
private static void ImplementInternalAPIClient(SourceProductionContext context, GeneratedAPI config, string apiParamDecl, string apiParamCall)
202203
{
203204
var template = @"
204205
using Certify.Models;
@@ -211,8 +212,7 @@ private static void ImplementInternalAPIClient(GeneratorExecutionContext context
211212
namespace Certify.Client
212213
{
213214
MethodTemplate
214-
}
215-
";
215+
}";
216216

217217
if (config.OperationMethod == "HttpGet")
218218
{
@@ -225,7 +225,6 @@ public partial interface ICertifyInternalApiClient
225225
/// </summary>
226226
/// <returns></returns>
227227
Task<{config.ReturnType}> {config.OperationName}({apiParamDecl});
228-
229228
}}
230229
231230
public partial class CertifyApiClient
@@ -239,9 +238,7 @@ public partial class CertifyApiClient
239238
var result = await FetchAsync($""{config.ServiceAPIRoute}"", authContext);
240239
return JsonToObject<{config.ReturnType}>(result);
241240
}}
242-
243-
}}
244-
");
241+
}}");
245242
var source = SourceText.From(code, Encoding.UTF8);
246243
context.AddSource($"{config.PublicAPIController}.{config.OperationName}.ICertifyInternalApiClient.g.cs", source);
247244
}
@@ -267,7 +264,6 @@ public partial interface ICertifyInternalApiClient
267264
/// </summary>
268265
/// <returns></returns>
269266
Task<{config.ReturnType}> {config.OperationName}({postApiParamDecl});
270-
271267
}}
272268
273269
public partial class CertifyApiClient
@@ -281,8 +277,7 @@ public partial class CertifyApiClient
281277
var result = await PostAsync($""{postAPIRoute}"", {postApiCall});
282278
return JsonToObject<{config.ReturnType}>(await result.Content.ReadAsStringAsync());
283279
}}
284-
}}
285-
"), Encoding.UTF8));
280+
}}"), Encoding.UTF8));
286281
}
287282

288283
if (config.OperationMethod == "HttpDelete")
@@ -306,25 +301,12 @@ public partial class CertifyApiClient
306301
/// <returns></returns>
307302
public async Task<{config.ReturnType}> {config.OperationName}({apiParamDecl})
308303
{{
309-
var route = $""{config.ServiceAPIRoute}"";
304+
var route = $""{config.ServiceAPIRoute}"";
310305
var result = await DeleteAsync(route, authContext);
311306
return JsonToObject<{config.ReturnType}>(await result.Content.ReadAsStringAsync());
312307
}}
313-
}}
314-
"), Encoding.UTF8));
315-
}
316-
}
317-
318-
public void Initialize(GeneratorInitializationContext context)
319-
{
320-
#if DEBUG
321-
// uncomment this to launch a debug session which code generation runs
322-
// then add a watch on
323-
if (!Debugger.IsAttached)
324-
{
325-
// Debugger.Launch();
308+
}}"), Encoding.UTF8));
326309
}
327-
#endif
328310
}
329311
}
330312
}

0 commit comments

Comments
 (0)