Skip to content

Commit c7e670f

Browse files
committed
Final updates for blazor server
1 parent 9c63d2e commit c7e670f

17 files changed

+2480
-23
lines changed
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
using CodeFactory.Automation.NDF.Logic;
2+
using CodeFactory.Automation.NDF.Logic.AspNetCore.Blazor;
3+
using CodeFactory.Automation.Standard.Logic;
4+
using CodeFactory.WinVs;
5+
using CodeFactory.WinVs.Commands;
6+
using CodeFactory.WinVs.Commands.SolutionExplorer;
7+
using CodeFactory.WinVs.Logging;
8+
using CodeFactory.WinVs.Models.CSharp;
9+
using CodeFactory.WinVs.Models.CSharp.Builder;
10+
using CodeFactory.WinVs.Models.ProjectSystem;
11+
using System;
12+
using System.Collections.Generic;
13+
using System.Collections.Immutable;
14+
using System.Linq;
15+
using System.Text;
16+
using System.Threading.Tasks;
17+
using System.Windows;
18+
19+
namespace CodeFactory.Architecture.Blazor.Server
20+
{
21+
/// <summary>
22+
/// Code factory command for automation of a C# document when selected from a project in solution explorer.
23+
/// </summary>
24+
public class AddMissingControllerMembers : CSharpSourceCommandBase
25+
{
26+
private static readonly string commandTitle = "Add Missing Controller Members";
27+
private static readonly string commandDescription = "Adds missing contract interface members to the Controller implementation.";
28+
29+
#pragma warning disable CS1998
30+
31+
/// <inheritdoc />
32+
public AddMissingControllerMembers(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
33+
{
34+
//Intentionally blank
35+
}
36+
37+
#region External Configuration
38+
39+
/// <summary>
40+
/// The fully qualified name of the command to be used with configuration.
41+
/// </summary>
42+
public static string Type = typeof(AddMissingControllerMembers).FullName;
43+
44+
45+
/// <summary>
46+
/// Exection project for the command.
47+
/// </summary>
48+
public static string ExecutionProject = "ExecutionProject";
49+
50+
/// <summary>
51+
/// Execution folder for the command.
52+
/// </summary>
53+
public static string ExecutionFolder = "ExecutionFolder";
54+
55+
/// <summary>
56+
/// Repositories name prefix
57+
/// </summary>
58+
public static string ControllerPrefix = "ControllerPrefix";
59+
60+
61+
/// <summary>
62+
/// Repositories name suffix.
63+
/// </summary>
64+
public static string ControllerSuffix = "ControllerSuffix";
65+
66+
/// <summary>
67+
/// Loads the external configuration definition for this command.
68+
/// </summary>
69+
/// <returns>Will return the command configuration or null if this command does not support external configurations.</returns>
70+
public override ConfigCommand LoadExternalConfigDefinition()
71+
{
72+
var config = new ConfigCommand{ CommandType = Type, Name = commandTitle, Category = "Controller",Guidance = "Command is used when updating missing members from a controller implementation." }
73+
.UpdateExecutionProject
74+
(
75+
new ConfigProject
76+
{
77+
Name = ExecutionProject,
78+
Guidance = "The project where the controller class file resides in."
79+
}
80+
.AddFolder
81+
(
82+
new ConfigFolder
83+
{
84+
Name = ExecutionFolder,
85+
Required = false,
86+
Guidance = "The target folder the controller class will be found in."
87+
}
88+
)
89+
.AddParameter
90+
(
91+
new ConfigParameter
92+
{
93+
Name = ControllerPrefix,
94+
Guidance = "Optional, checks to makes sure the class starts with the provided prefix before considering it a controller."
95+
}
96+
)
97+
.AddParameter
98+
(
99+
new ConfigParameter
100+
{
101+
Name = ControllerSuffix,
102+
Guidance = "Optional, checks to makes sure the class starts with the provided suffix before considering it a controller."
103+
}
104+
)
105+
106+
);
107+
108+
return config;
109+
}
110+
#endregion
111+
112+
#region Overrides of VsCommandBase<IVsCSharpDocument>
113+
114+
/// <summary>
115+
/// Validation controller that will determine if this command should be enabled for execution.
116+
/// </summary>
117+
/// <param name="result">The target model data that will be used to determine if this command should be enabled.</param>
118+
/// <returns>Boolean flag that will tell code factory to enable this command or disable it.</returns>
119+
public override async Task<bool> EnableCommandAsync(VsCSharpSource result)
120+
{
121+
//Result that determines if the command is enabled and visible in the context menu for execution.
122+
bool isEnabled = false;
123+
124+
try
125+
{
126+
var controllerClass = result?.SourceCode?.Classes.FirstOrDefault();
127+
128+
isEnabled = controllerClass != null;
129+
130+
ConfigCommand command = null;
131+
132+
if( isEnabled )
133+
{
134+
command = await ConfigManager.LoadCommandByFolderAsync(Type, ExecutionFolder, result)
135+
?? await ConfigManager.LoadCommandByProjectAsync(Type, result);
136+
137+
isEnabled = command != null;
138+
}
139+
140+
if(isEnabled )
141+
{
142+
var controllerPrefix = command.ExecutionProject.ParameterValue(ControllerPrefix);
143+
var controllerSuffix = command.ExecutionProject.ParameterValue(ControllerSuffix);
144+
isEnabled = IsControllerClass(controllerClass,controllerPrefix,controllerSuffix);
145+
}
146+
147+
if(isEnabled ) isEnabled = GetMissingContainerInterfaceMembersFromController(controllerClass).Any();
148+
}
149+
catch (Exception unhandledError)
150+
{
151+
_logger.Error($"The following unhandled error occurred while checking if the solution explorer C# document command {commandTitle} is enabled. ",
152+
unhandledError);
153+
isEnabled = false;
154+
}
155+
156+
return isEnabled;
157+
}
158+
159+
/// <summary>
160+
/// Code factory framework calls this method when the command has been executed.
161+
/// </summary>
162+
/// <param name="result">The code factory model that has generated and provided to the command to process.</param>
163+
public override async Task ExecuteCommandAsync(VsCSharpSource result)
164+
{
165+
try
166+
{
167+
var controllerSource = result?.SourceCode;
168+
169+
if ( controllerSource == null ) return;
170+
171+
var controllerClass = controllerSource.Classes.FirstOrDefault();
172+
173+
if(controllerClass == null) return;
174+
175+
var missingMembers = GetMissingContainerInterfaceMembersFromController(controllerClass);
176+
177+
if( !missingMembers.Any() ) return;
178+
179+
180+
181+
controllerSource = await controllerSource.AddUsingStatementAsync("Microsoft.Extensions.Logging");
182+
controllerSource = await controllerSource.AddUsingStatementAsync("CodeFactory.NDF");
183+
184+
controllerClass = controllerSource.Classes.FirstOrDefault();
185+
186+
187+
var command = await ConfigManager.LoadCommandByFolderAsync(Type, ExecutionFolder, result)
188+
?? await ConfigManager.LoadCommandByProjectAsync(Type, result);
189+
190+
if(command == null)return;
191+
192+
193+
var loggerBlock = new LoggerBlockNDFLogger("_logger");
194+
195+
var catchBlocks = new List<ICatchBlock>
196+
{
197+
new CatchBlockManagedExceptionBlazorControllerMessage(loggerBlock),
198+
new CatchBlockExceptionBlazorControllerMessage(loggerBlock)
199+
};
200+
201+
var boundChecks = new List<IBoundsCheckBlock>
202+
{
203+
204+
new BoundsCheckBlockNullBlazorControllerMessage(true,loggerBlock),
205+
new BoundsCheckBlockStringBlazorControllerMessage(true,loggerBlock)
206+
};
207+
208+
var tryBlock = new TryBlockStandard(loggerBlock,catchBlocks);
209+
210+
var updatedControllerClass = await VisualStudioActions.AddClassMissingMembersAsync(result.SourceCode,controllerClass,false,loggerBlock,Microsoft.Extensions.Logging.LogLevel.Information,boundChecks,tryBlock,missingMembers);
211+
212+
}
213+
catch (CodeFactoryException cfException)
214+
{
215+
MessageBox.Show(cfException.Message,"CodeFactory Error",MessageBoxButton.OK,MessageBoxImage.Error);
216+
}
217+
catch (Exception unhandledError)
218+
{
219+
_logger.Error($"The following unhandled error occurred while executing the solution explorer C# document command {commandTitle}. ",
220+
unhandledError);
221+
222+
}
223+
224+
}
225+
226+
private bool IsControllerClass(CsClass controllerClass,string controllerPrefix, string controllerSuffix)
227+
{
228+
229+
bool iscontrollerClass = false;
230+
231+
if (controllerClass != null) iscontrollerClass = true;
232+
233+
if(iscontrollerClass & controllerPrefix != null) iscontrollerClass = controllerClass.Name.StartsWith(controllerPrefix);
234+
235+
if(iscontrollerClass & controllerSuffix != null) iscontrollerClass = controllerClass.Name.EndsWith(controllerSuffix);
236+
237+
return iscontrollerClass;
238+
}
239+
240+
#endregion
241+
242+
243+
private IReadOnlyList<CsMember> GetMissingContainerInterfaceMembersFromController(CsContainer source, List<MapNamespace> mappedNamespaces = null)
244+
{
245+
if (source == null)
246+
{
247+
throw new ArgumentNullException("source");
248+
}
249+
250+
if (source.ContainerType == CsContainerType.Interface)
251+
{
252+
return ImmutableList<CsMember>.Empty;
253+
}
254+
255+
if (source.InheritedInterfaces == null)
256+
{
257+
return ImmutableList<CsMember>.Empty;
258+
}
259+
260+
IReadOnlyList<KeyValuePair<int, CsMember>> sourceMembers = source.GetComparisonMembers(MemberComparisonType.Security);
261+
Dictionary<int, CsMember> dictionary = new Dictionary<int, CsMember>();
262+
foreach (CsInterface inheritedInterface in source.InheritedInterfaces)
263+
{
264+
switch (inheritedInterface.Name)
265+
{
266+
case "IHandleEvent":
267+
268+
continue;
269+
break;
270+
271+
case "IHandleAfterRender":
272+
continue;
273+
break;
274+
275+
case "IComponent":
276+
continue;
277+
break;
278+
279+
default:
280+
281+
break;
282+
}
283+
284+
IReadOnlyList<KeyValuePair<int, CsMember>> comparisonMembers = inheritedInterface.GetComparisonMembers(MemberComparisonType.Security);
285+
if (!comparisonMembers.Any())
286+
{
287+
continue;
288+
}
289+
290+
foreach (KeyValuePair<int, CsMember> item in comparisonMembers)
291+
{
292+
if (!dictionary.ContainsKey(item.Key))
293+
{
294+
dictionary.Add(item.Key, item.Value);
295+
}
296+
}
297+
}
298+
299+
if (!dictionary.Any())
300+
{
301+
return ImmutableList<CsMember>.Empty;
302+
}
303+
304+
return dictionary.Where((KeyValuePair<int, CsMember> interfaceMember) => !sourceMembers.Any(delegate (KeyValuePair<int, CsMember> m)
305+
{
306+
int key = m.Key;
307+
KeyValuePair<int, CsMember> keyValuePair2 = interfaceMember;
308+
return key == keyValuePair2.Key;
309+
})).Select(delegate (KeyValuePair<int, CsMember> interfaceMember)
310+
{
311+
KeyValuePair<int, CsMember> keyValuePair = interfaceMember;
312+
return keyValuePair.Value;
313+
}).ToImmutableList();
314+
}
315+
}
316+
}

0 commit comments

Comments
 (0)