From d3244bcf298f69dc4f742664934e60e5ed4f1dc0 Mon Sep 17 00:00:00 2001 From: Stephen Giffin Date: Mon, 6 Nov 2023 17:23:21 -0700 Subject: [PATCH 1/8] Updates to naming standards --- .../RefreshEFRepository.cs | 6 +- .../RefreshFluentValidation.cs | 5 +- .../RefreshRestService.cs | 94 +++++++++++++++- .../Json/RestJsonCSharpAbstractionBuilder.cs | 103 ++++++++++++++++-- .../Rest/Json/RestJsonServiceBuilder.cs | 55 ++++++++-- .../NameManagement.cs | 15 ++- 6 files changed, 250 insertions(+), 28 deletions(-) diff --git a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshEFRepository.cs b/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshEFRepository.cs index a774736..fe6f168 100644 --- a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshEFRepository.cs +++ b/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshEFRepository.cs @@ -382,7 +382,11 @@ await VisualStudioActions.GetProjectFolderFromConfigAsync(command.Project(Integr ?? throw new CodeFactoryException($"Could not load the entity that supports the ef model '{efModel.Name}', cannot refresh the EF repository."); NameManagement modelValidatorNameManagement = null; - if(!string.IsNullOrEmpty(modelValidatorSuffix) | !string.IsNullOrEmpty(modelValidatorPrefix)) modelValidatorNameManagement = NameManagement.Init(null,null,modelValidatorPrefix, modelValidatorSuffix); + + string removePrefixes = null; + + string removeSuffixes = null; + if(!string.IsNullOrEmpty(modelValidatorSuffix) | !string.IsNullOrEmpty(modelValidatorPrefix)) modelValidatorNameManagement = NameManagement.Init(removePrefixes,removeSuffixes,modelValidatorPrefix, modelValidatorSuffix); var validation = (await VisualStudioActions.RefreshValidationAsync(appModel,appModelProject,appModelFolder,modelValidatorNameManagement)) diff --git a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshFluentValidation.cs b/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshFluentValidation.cs index cc28a69..06ddbac 100644 --- a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshFluentValidation.cs +++ b/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshFluentValidation.cs @@ -168,9 +168,12 @@ await VisualStudioActions.GetProjectFromConfigAsync(command.ExecutionProject) var validatorSuffix = command.ExecutionProject.ParameterValue(ModelValidatorSuffix); + string removePrefixes = null; + string removeSuffixes = null; + NameManagement nameManagement = null; - if(!string.IsNullOrEmpty(validatorPrefix) | !string.IsNullOrEmpty(validatorSuffix)) nameManagement = NameManagement.Init(null,null, validatorPrefix, validatorSuffix); + if(!string.IsNullOrEmpty(validatorPrefix) | !string.IsNullOrEmpty(validatorSuffix)) nameManagement = NameManagement.Init(removePrefixes,removeSuffixes, validatorPrefix, validatorSuffix); var validationClass = VisualStudioActions.RefreshValidationAsync(sourceClass, sourceProject,sourceFolder,nameManagement); } diff --git a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshRestService.cs b/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshRestService.cs index 26a7a61..f2c5d6f 100644 --- a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshRestService.cs +++ b/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshRestService.cs @@ -1,4 +1,5 @@ using CodeFactory.Automation.NDF.Logic.AspNetCore.Service.Rest.Json; +using CodeFactory.Automation.Standard.Logic; using CodeFactory.WinVs; using CodeFactory.WinVs.Commands; using CodeFactory.WinVs.Commands.SolutionExplorer; @@ -88,6 +89,32 @@ public RefreshRestService(ILogger logger, IVsActions vsActions) : base(logger, v /// public static string ContractFolder = "ContractFolder"; + /// + /// Comma seperated list of prefixes to remove from the logc contract when creating the service name. + /// + public static string ServiceNameRemovePrefixes = "ServiceNameRemovePrefixes"; + + /// + /// Comma sperated list of suffixes to remove from the logic contract when creating the service name. + /// + public static string ServiceNameRemoveSuffixes = "ServiceNameRemoveSuffixes"; + + /// + /// Prefix to start the service name with. + /// + public static string ServiceNameAppendPrefix = "ServiceNameAppendPrefix"; + + /// + /// Prefix to start the service client name with. + /// + public static string ServiceClientNameAppendPrefix = "ServiceClientNameAppendPrefix"; + + /// + /// Suffix to append to the service client name. + /// + public static string ServiceClientNameAppendSuffix = "ServiceClientNameAppendPrefix"; + + /// /// Loads the external configuration definition for this command. /// @@ -104,7 +131,7 @@ public override ConfigCommand LoadExternalConfigDefinition() Guidance = "Enter the fully project name for the logic contracts project." } .AddFolder - ( + ( new ConfigFolder { Name = ExecutionFolder, @@ -113,6 +140,49 @@ public override ConfigCommand LoadExternalConfigDefinition() "Optional, set the relative path from the root of the project. If it is more then one directory deep then use forward slash instead of back slashes." } ) + .AddParameter + ( + new ConfigParameter + { + Name = ServiceNameRemovePrefixes, + Guidance = "Optional, provide a comma seperated value of each prefix to check for to be removed from the logic contract name when creating a service name." + } + ) + .AddParameter + ( + new ConfigParameter + { + Name = ServiceNameRemoveSuffixes, + Guidance = "Optional, provide a comma seperated value of each suffix to check for to be removed from the logic contract name when creating a service name.", + Value = "Logic" + } + ) + .AddParameter + ( + new ConfigParameter + { + Name = ServiceNameAppendPrefix, + Guidance = "Optional, provide the prefix to append to the service name." + } + ) + .AddParameter + ( + new ConfigParameter + { + Name = ServiceClientNameAppendPrefix, + Guidance = "Optional, provide the prefix to append to the service client name." + } + ) + .AddParameter + ( + new ConfigParameter + { + Name = ServiceClientNameAppendSuffix, + Guidance = "Optional, provide the suffix to append to the service client name.", + Value = "Client" + } + ) + ) .AddProject ( @@ -270,15 +340,31 @@ await VisualStudioActions.GetProjectFromConfigAsync(command.Project(ContractProj var contractFolder = await VisualStudioActions.GetProjectFolderFromConfigAsync(command.Project(ContractProject), ContractFolder); - var serviceClass = await VisualStudioActions.RefreshJsonRestService(logicContract, serviceProject, serviceFolder, + //Execution command parameters. + var serviceNameRemovePrefixes = command.ExecutionProject.ParameterValue(ServiceNameRemovePrefixes); + var serviceNameRemoveSuffixes = command.ExecutionProject.ParameterValue(ServiceNameRemoveSuffixes); + var serviceNameAppendPrefix = command.ExecutionProject.ParameterValue(ServiceNameAppendPrefix); + var serviceClientNameAppendPrefix = command.ExecutionProject.ParameterValue(ServiceClientNameAppendPrefix); + var serviceClientNameAppendSuffix = command.ExecutionProject.ParameterValue(ServiceClientNameAppendSuffix); + + + var serviceNameManagement = NameManagement.Init(serviceNameRemovePrefixes,serviceNameRemoveSuffixes,serviceNameAppendPrefix,null); + + var serviceName = serviceNameManagement.FormatName(logicContract.Name.GenerateCSharpFormattedClassName()); + + var serviceClass = await VisualStudioActions.RefreshJsonRestService(serviceName,logicContract, serviceProject, serviceFolder, modelProject,modelFolder) ?? throw new CodeFactoryException("Could not refresh the rest json service, cannot refresh the abstraction implementation."); - var abstractionContract = await VisualStudioActions.RefreshCSharpAbstractionContractAsync(logicContract, contractProject, contractFolder) + var serviceClientNameManagement = NameManagement.Init(serviceNameRemovePrefixes,serviceNameRemoveSuffixes,serviceClientNameAppendPrefix,serviceClientNameAppendSuffix); + + var serviceClientName = serviceClientNameManagement.FormatName(logicContract.Name.GenerateCSharpFormattedClassName()); + + var abstractionContract = await VisualStudioActions.RefreshCSharpAbstractionContractAsync($"I{serviceClientName}", logicContract, contractProject, contractFolder) ?? throw new CodeFactoryException("Could not refresh the abstraction contract. The abstraction cannot be updated."); - var abstractionClass = await VisualStudioActions.RefreshAbstractionClass(serviceClass,abstractionContract,serviceProject,abstractionProject,modelProject,abstractionFolder,modelFolder); + var abstractionClass = await VisualStudioActions.RefreshAbstractionClass(serviceClientName, serviceClass,abstractionContract,serviceProject,abstractionProject,modelProject,abstractionFolder,modelFolder); } diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/AspNetCore/Service/Rest/Json/RestJsonCSharpAbstractionBuilder.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/AspNetCore/Service/Rest/Json/RestJsonCSharpAbstractionBuilder.cs index 78cc588..bdcb73e 100644 --- a/src/Automation/CodeFactory.Automation.NDF.Logic/AspNetCore/Service/Rest/Json/RestJsonCSharpAbstractionBuilder.cs +++ b/src/Automation/CodeFactory.Automation.NDF.Logic/AspNetCore/Service/Rest/Json/RestJsonCSharpAbstractionBuilder.cs @@ -16,12 +16,25 @@ namespace CodeFactory.Automation.NDF.Logic.AspNetCore.Service.Rest.Json /// public static class RestJsonCSharpAbstractionBuilder { - public static async Task RefreshCSharpAbstractionContractAsync(this IVsActions source, + /// + /// Refreshes the interface definition of an service abstraction client. + /// + /// CodeFactory Automation. + /// The name of the abstraction contract to refresh. + /// The source contract used to refresh the abstraction contract. + /// The project the abstraction is created in. + /// Optional, the target project folder the abstraction contract should be located in. Default value is null. + /// The inteface model that represents the abstraction contract. + /// Raised if required information is missing or automation errors occurred. + public static async Task RefreshCSharpAbstractionContractAsync(this IVsActions source, string contractName, CsInterface sourceContract, VsProject contractProject, VsProjectFolder contractFolder = null) { if (source == null) throw new CodeFactoryException("CodeFactory automation was not provided cannot refresh the abstraction contract."); + if (string.IsNullOrEmpty(contractName)) + throw new CodeFactoryException("No contract name was provided, cannot refresh the abstraction contract."); + if (sourceContract == null) throw new CodeFactoryException("Cannot load the source contract, cannot refresh the abstraction contract."); @@ -31,18 +44,31 @@ public static async Task RefreshCSharpAbstractionContractAsync(this CsSource contractSource = (contractFolder != null ? (await contractFolder.FindCSharpSourceByInterfaceNameAsync(sourceContract.Name))?.SourceCode : (await contractProject.FindCSharpSourceByInterfaceNameAsync(sourceContract.Name))?.SourceCode) - ?? await source.CreateCSharpAbstractionContractAsync(sourceContract, contractProject, contractFolder); + ?? await source.CreateCSharpAbstractionContractAsync(contractName, sourceContract, contractProject, contractFolder); return await source.UpdateCSharpAbstractionContractAsync(sourceContract, contractSource); } - private static async Task CreateCSharpAbstractionContractAsync(this IVsActions source, + /// + /// Creates the interface definition of an service abstraction client. + /// + /// CodeFactory Automation. + /// The name of the abstraction contract to refresh. + /// The source contract used to refresh the abstraction contract. + /// The project the abstraction is created in. + /// Optional, the target project folder the abstraction contract should be located in. Default value is null. + /// The source code that hosts the inteface model that represents the abstraction contract. + /// Raised if required information is missing or automation errors occurred. + private static async Task CreateCSharpAbstractionContractAsync(this IVsActions source, string contractName, CsInterface sourceContract, VsProject contractProject, VsProjectFolder contractFolder = null) { if (source == null) throw new CodeFactoryException("CodeFactory automation was not provided cannot create the abstraction contract."); + if (string.IsNullOrEmpty(contractName)) + throw new CodeFactoryException("No contract name was provided, cannot refresh the abstraction contract."); + if (sourceContract == null) throw new CodeFactoryException("Cannot load the source contract, cannot create the abstraction contract."); @@ -61,24 +87,32 @@ private static async Task CreateCSharpAbstractionContractAsync(this IV contractFormatter.AppendCodeLine(0, $"namespace {defaultNamespace}"); contractFormatter.AppendCodeLine(0, "{"); contractFormatter.AppendCodeLine(1, "/// "); - contractFormatter.AppendCodeLine(1, $"/// Abstract implementation that supports '{sourceContract.Name.GenerateCSharpFormattedClassName()}'/>"); + contractFormatter.AppendCodeLine(1, $"/// Abstract implementation that supports '{contractName.GenerateCSharpFormattedClassName()}'/>"); contractFormatter.AppendCodeLine(1, "/// "); - contractFormatter.AppendCodeLine(1, $"public interface {sourceContract.Name}"); + contractFormatter.AppendCodeLine(1, $"public interface {contractName}"); contractFormatter.AppendCodeLine(1, "{"); contractFormatter.AppendCodeLine(1, "}"); contractFormatter.AppendCodeLine(0, "}"); - var doc = contractFolder != null ? await contractFolder.AddDocumentAsync($"{sourceContract.Name}.cs", contractFormatter.ReturnSource()) - : await contractProject.AddDocumentAsync($"{sourceContract.Name}.cs", contractFormatter.ReturnSource()); + var doc = contractFolder != null ? await contractFolder.AddDocumentAsync($"{contractName}.cs", contractFormatter.ReturnSource()) + : await contractProject.AddDocumentAsync($"{contractName}.cs", contractFormatter.ReturnSource()); return doc == null - ? throw new CodeFactoryException($"Failed to create the abstraction contract '{sourceContract.Name}'.") + ? throw new CodeFactoryException($"Failed to create the abstraction contract '{contractName}'.") : await doc.GetCSharpSourceModelAsync(); } + /// + /// Updates the interface definition of an service abstraction client. + /// + /// CodeFactory Automation. + /// The name of the abstraction contract to refresh. + /// The source contract used to refresh the abstraction contract. + /// The inteface model that represents the abstraction contract. + /// Raised if required information is missing or automation errors occurred. private static async Task UpdateCSharpAbstractionContractAsync(this IVsActions source, CsInterface sourceContract, CsSource contractSource) { @@ -123,12 +157,29 @@ private static async Task UpdateCSharpAbstractionContractAsync(this } - public static async Task RefreshAbstractionClass(this IVsActions source, CsClass serviceClass, + /// + /// Refreshes the instance of the abstraction client class. + /// + /// CodeFactory Automation. + /// The class name of the client to be refreshed. + /// The source service class the client is calling. + /// The abstraction interface the client consumes. + /// The project that hosts the service being consumed. + /// The abstraction project that hosts the client. + /// The service model project that contains the service models. + /// The project folder where the abstaction client is located. + /// The project folder where the model data can be found for the client. + /// The client class model that was refreshed. + /// Raised if configuration information is missing or automation errors occurred. + public static async Task RefreshAbstractionClass(this IVsActions source, string clientName, CsClass serviceClass, CsInterface abstractionContract, VsProject serviceProject, VsProject abstractionProject, VsProject modelProject, VsProjectFolder abstractionFolder = null, VsProjectFolder modelFolder = null) { if (source == null) throw new CodeFactoryException("CodeFactory automation was not provided cannot refresh the abstraction."); + if (string.IsNullOrEmpty(clientName)) + throw new CodeFactoryException("The client name was not provided, cannot refresh the abstraction."); + if (abstractionContract == null) throw new CodeFactoryException("Cannot load the abstraction contract, cannot refresh the abstraction."); @@ -155,17 +206,32 @@ public static async Task RefreshAbstractionClass(this IVsActions source var abstractionSource = (abstractionFolder != null ? (await abstractionFolder.FindCSharpSourceByClassNameAsync(abstractionClassName))?.SourceCode : (await abstractionProject.FindCSharpSourceByClassNameAsync(abstractionClassName))?.SourceCode) - ?? await source.CreateAbstractionClassAsync(abstractionContract, serviceProject, abstractionProject, abstractionFolder); + ?? await source.CreateAbstractionClassAsync(clientName, abstractionContract, serviceProject, abstractionProject, abstractionFolder); return await source.UpdateAbstractionClassAsync(abstractionSource, serviceClass, abstractionContract, serviceProject, abstractionProject, modelProject, abstractionFolder, modelFolder); } - private static async Task CreateAbstractionClassAsync(this IVsActions source, + /// + /// Creates the instance of the abstraction client class. + /// + /// CodeFactory Automation. + /// The class name of the client to be refreshed. + /// The source service class the client is calling. + /// The abstraction interface the client consumes. + /// The project that hosts the service being consumed. + /// The abstraction project that hosts the client. + /// The project folder where the abstaction client is located. + /// The source code that containes the client class model that was created. + /// Raised if configuration information is missing or automation errors occurred. + private static async Task CreateAbstractionClassAsync(this IVsActions source, string clientName, CsInterface abstractionContract, VsProject serviceProject, VsProject abstractionProject, VsProjectFolder abstractionFolder = null) { if (source == null) throw new CodeFactoryException("CodeFactory automation was not provided cannot create the abstraction."); + if (string.IsNullOrEmpty(clientName)) + throw new CodeFactoryException("The client name was not provided, cannot create the abstraction."); + if (abstractionContract == null) throw new CodeFactoryException("Cannot load the abstraction contract, cannot create the abstraction."); @@ -181,7 +247,7 @@ private static async Task CreateAbstractionClassAsync(this IVsActions if (string.IsNullOrEmpty(sourceNamespace)) throw new CodeFactoryException("Could not identify the target namespace for the abstraction, abstraction cannot be created."); - var abstractionName = abstractionContract.Name.GenerateCSharpFormattedClassName(); + var abstractionName = clientName; var sourceFormatter = new SourceFormatter(); @@ -234,6 +300,19 @@ private static async Task CreateAbstractionClassAsync(this IVsActions } + /// + /// Updates the instance of the abstraction client class. + /// + /// CodeFactory Automation. + /// The source service class the client is calling. + /// The abstraction interface the client consumes. + /// The project that hosts the service being consumed. + /// The abstraction project that hosts the client. + /// The service model project that contains the service models. + /// The project folder where the abstaction client is located. + /// The project folder where the model data can be found for the client. + /// The client class model that was refreshed. + /// Raised if configuration information is missing or automation errors occurred. private static async Task UpdateAbstractionClassAsync(this IVsActions source, CsSource abstractionSource, CsClass serviceClass, CsInterface abstractionContract, VsProject serviceProject, VsProject abstractionProject, VsProject modelProject, VsProjectFolder abstractionFolder = null, VsProjectFolder modelFolder = null) { diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/AspNetCore/Service/Rest/Json/RestJsonServiceBuilder.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/AspNetCore/Service/Rest/Json/RestJsonServiceBuilder.cs index 8d9aaa9..8ea85b2 100644 --- a/src/Automation/CodeFactory.Automation.NDF.Logic/AspNetCore/Service/Rest/Json/RestJsonServiceBuilder.cs +++ b/src/Automation/CodeFactory.Automation.NDF.Logic/AspNetCore/Service/Rest/Json/RestJsonServiceBuilder.cs @@ -11,19 +11,34 @@ namespace CodeFactory.Automation.NDF.Logic.AspNetCore.Service.Rest.Json { -/// + /// /// Automation logic for creating and updated web api rest based services. /// public static class RestJsonServiceBUilder { - - public static async Task RefreshJsonRestService(this IVsActions source, CsInterface logicContract, + /// + /// Refreshes a rest json service implementation. + /// + /// CodeFactory Automation SDK. + /// The name of the service without the controller suffix appended to it. + /// The interface contract for the logic implementation to be supported. + /// The project the service is hosted in. + /// The service folder the service controller will be created in. + /// The project that hosts the rest service models. + /// Optional parameter, provides the folder the model should be located in. Default value is null. + /// The implemented service class. + /// Raised if required information is missing for logic operations could not complete. + + public static async Task RefreshJsonRestService(this IVsActions source, string serviceName, CsInterface logicContract, VsProject serviceProject, VsProjectFolder serviceFolder, VsProject modelProject, VsProjectFolder modelFolder = null) { if (source == null) throw new CodeFactoryException("CodeFactory automation was not provided cannot refresh the service."); + if (string.IsNullOrEmpty(serviceName)) + throw new CodeFactoryException("The service name was not provided cannot refresh the service."); + if (logicContract == null) throw new CodeFactoryException("Cannot load the logic contract, cannot refresh the service."); @@ -47,10 +62,10 @@ public static async Task RefreshJsonRestService(this IVsActions source, await source.AddSupportRestClassesAsync(modelProject); await source.AddJsonServiceExtensionsAsync(serviceProject); - var serviceName = $"{logicContract.Name.GenerateCSharpFormattedClassName()}Controller"; + var serviceFormattedName = $"{logicContract.Name.GenerateCSharpFormattedClassName()}Controller"; - CsSource serviceSource = (await serviceFolder.FindCSharpSourceByClassNameAsync(serviceName))?.SourceCode - ?? await source.CreateJsonRestServiceAsync(logicContract, serviceProject, serviceFolder); + CsSource serviceSource = (await serviceFolder.FindCSharpSourceByClassNameAsync(serviceFormattedName))?.SourceCode + ?? await source.CreateJsonRestServiceAsync(serviceName,logicContract, serviceProject, serviceFolder); var serviceClass = await source.UpdateJsonRestServiceAsync(logicContract, serviceSource, serviceProject, serviceFolder, modelProject, modelFolder); @@ -61,12 +76,25 @@ public static async Task RefreshJsonRestService(this IVsActions source, return serviceClass; } - private static async Task CreateJsonRestServiceAsync(this IVsActions source, + /// + /// Creates a new instance of the rest json web api. + /// + /// CodeFactory Automation SDK. + /// The name of the service without the controller suffix appended to it. + /// The interface contract for the logic implementation to be supported. + /// The project the service is hosted in. + /// The service folder the service controller will be created in. + /// The implemented source code that holds the new definition of the service. + /// Raised if required information is missing for logic operations could not complete. + private static async Task CreateJsonRestServiceAsync(this IVsActions source, string serviceName, CsInterface logicContract, VsProject serviceProject, VsProjectFolder serviceFolder) { if (source == null) throw new CodeFactoryException("CodeFactory automation was not provided cannot create the service."); + if (string.IsNullOrEmpty(serviceName)) + throw new CodeFactoryException("The service name was not provided cannot refresh the service."); + if (logicContract == null) throw new CodeFactoryException("Cannot load the logic contract, cannot create the service."); @@ -79,8 +107,6 @@ private static async Task CreateJsonRestServiceAsync(this IVsActions s var sourceNamespace = await serviceFolder.GetCSharpNamespaceAsync(); if (string.IsNullOrEmpty(sourceNamespace)) throw new CodeFactoryException("Could not identify the target namespace for the service, service cannot be created."); - var serviceName = logicContract.Name.GenerateCSharpFormattedClassName(); - var sourceFormatter = new SourceFormatter(); sourceFormatter.AppendCodeLine(0, "using System;"); @@ -141,6 +167,17 @@ private static async Task CreateJsonRestServiceAsync(this IVsActions s return serviceSource; } + + /// + /// Updates a instance of the rest json web api with the missing members. + /// + /// CodeFactory Automation SDK. + /// The name of the service without the controller suffix appended to it. + /// The interface contract for the logic implementation to be supported. + /// The project the service is hosted in. + /// The service folder the service controller will be created in. + /// The implemented source code that holds the new definition of the service. + /// Raised if required information is missing for logic operations could not complete. private static async Task UpdateJsonRestServiceAsync(this IVsActions source, CsInterface logicContract, CsSource serviceSource, VsProject serviceProject, VsProjectFolder serviceFolder, VsProject modelProject, VsProjectFolder modelFolder = null) diff --git a/src/Automation/CodeFactory.Automation.Standard.Logic/NameManagement.cs b/src/Automation/CodeFactory.Automation.Standard.Logic/NameManagement.cs index 96a4961..c0d29a8 100644 --- a/src/Automation/CodeFactory.Automation.Standard.Logic/NameManagement.cs +++ b/src/Automation/CodeFactory.Automation.Standard.Logic/NameManagement.cs @@ -56,7 +56,7 @@ protected NameManagement(IEnumerable removePrefixes, IEnumerable /// /// List of prefixes to remove from the beginning of the objects name. /// List of suffixes to remove from the end of the objects name. - /// The prefix to append to the beginning of the objects name; + /// The prefix to append to the beginning of the objects name. /// The suffix to append to the end of the objects name. /// Initialized name manager. public static NameManagement Init(IEnumerable removePrefixes, IEnumerable removeSuffixes, string addPrefix, string addSuffix) @@ -64,6 +64,19 @@ public static NameManagement Init(IEnumerable removePrefixes, IEnumerabl return new NameManagement(removePrefixes,removeSuffixes, addPrefix, addSuffix); } + /// + /// Initializes a new instance of a data class. + /// + /// Comma seperated list of prefixes to remove from name. + /// Comma seperated list of suffixes to remove from name. + /// The prefix to append to the beginning of the objects name. + /// The suffix to append to the end of the objects name. + /// Initialized name manager. + public static NameManagement Init(string removePrefixes, string removeSuffixes, string addPrefix, string addSuffix) + { + return new NameManagement(removePrefixes != null? removePrefixes.Split(','):null ,removeSuffixes != null? removeSuffixes.Split(','):null , addPrefix, addSuffix); + } + /// /// List of prefixes to remove from the beginning of the objects name. From a8769e9b4cb95ab358324863dd5b1210b4b17c16 Mon Sep 17 00:00:00 2001 From: Stephen Giffin Date: Tue, 7 Nov 2023 14:39:27 -0700 Subject: [PATCH 2/8] Refactored all commands to support full configuration based naming and auto registration with library loaders. --- .../AddMissingControllerMembers.cs | 2 +- .../AddMissingLogicMembers.cs | 7 +- .../AddMissingRepositoryMembers.cs | 2 +- .../{ => CSharpFile}/RefreshEFRepository.cs | 209 +++++++---- .../RefreshFluentValidation.cs | 7 +- .../CSharpFile/RefreshLogic.cs | 305 +++++++++++++++ .../{ => CSharpFile}/RefreshRestService.cs | 2 +- .../{ => CSharpFile}/RefreshTest.cs | 57 ++- .../UpdateLogicImplementation.cs | 45 ++- ...eFactory.Architecture.Blazor.Server.csproj | 26 +- .../{ => IDE}/LoadExternalConfiguration.cs | 8 +- .../RegisterTransientServices.cs | 8 +- .../CreateAutomationConfiguration.cs | 7 +- .../ReloadAutomationConfiguration.cs | 7 +- .../Json/RestJsonCSharpAbstractionBuilder.cs | 17 +- .../CodeFactory.Automation.NDF.Logic.csproj | 3 + .../Data/Sql/EF/RepositoryBuilder.cs | 62 +-- .../DependencyInjectionBuilder.cs | 17 +- .../General/FluentValidationBuilder.cs | 170 +++++++++ .../General/LogicBuilder.cs | 253 +++++++++++++ .../General/ModelBuilder.cs | 352 ++++++++++++++++++ .../Testing/MSTest/IntegrationTestBuilder.cs | 21 +- .../CloneInterfaceBuilder.cs | 9 +- .../POCOBuilder.cs | 1 + 24 files changed, 1428 insertions(+), 169 deletions(-) rename src/Architecture/CodeFactory.Architecture.Blazor.Server/{ => CSharpFile}/AddMissingControllerMembers.cs (99%) rename src/Architecture/CodeFactory.Architecture.Blazor.Server/{ => CSharpFile}/AddMissingLogicMembers.cs (99%) rename src/Architecture/CodeFactory.Architecture.Blazor.Server/{ => CSharpFile}/AddMissingRepositoryMembers.cs (99%) rename src/Architecture/CodeFactory.Architecture.Blazor.Server/{ => CSharpFile}/RefreshEFRepository.cs (71%) rename src/Architecture/CodeFactory.Architecture.Blazor.Server/{ => CSharpFile}/RefreshFluentValidation.cs (96%) create mode 100644 src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshLogic.cs rename src/Architecture/CodeFactory.Architecture.Blazor.Server/{ => CSharpFile}/RefreshRestService.cs (99%) rename src/Architecture/CodeFactory.Architecture.Blazor.Server/{ => CSharpFile}/RefreshTest.cs (76%) rename src/Architecture/CodeFactory.Architecture.Blazor.Server/{ => CSharpFile}/UpdateLogicImplementation.cs (85%) rename src/Architecture/CodeFactory.Architecture.Blazor.Server/{ => IDE}/LoadExternalConfiguration.cs (91%) rename src/Architecture/CodeFactory.Architecture.Blazor.Server/{ => Project}/RegisterTransientServices.cs (92%) rename src/Architecture/CodeFactory.Architecture.Blazor.Server/{ => Solution}/CreateAutomationConfiguration.cs (92%) rename src/Architecture/CodeFactory.Architecture.Blazor.Server/{ => Solution}/ReloadAutomationConfiguration.cs (92%) create mode 100644 src/Automation/CodeFactory.Automation.NDF.Logic/General/FluentValidationBuilder.cs create mode 100644 src/Automation/CodeFactory.Automation.NDF.Logic/General/LogicBuilder.cs create mode 100644 src/Automation/CodeFactory.Automation.NDF.Logic/General/ModelBuilder.cs diff --git a/src/Architecture/CodeFactory.Architecture.Blazor.Server/AddMissingControllerMembers.cs b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/AddMissingControllerMembers.cs similarity index 99% rename from src/Architecture/CodeFactory.Architecture.Blazor.Server/AddMissingControllerMembers.cs rename to src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/AddMissingControllerMembers.cs index 39d9714..c369f0f 100644 --- a/src/Architecture/CodeFactory.Architecture.Blazor.Server/AddMissingControllerMembers.cs +++ b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/AddMissingControllerMembers.cs @@ -16,7 +16,7 @@ using System.Threading.Tasks; using System.Windows; -namespace CodeFactory.Architecture.Blazor.Server +namespace CodeFactory.Architecture.Blazor.Server.CSharpFile { /// /// Code factory command for automation of a C# document when selected from a project in solution explorer. diff --git a/src/Architecture/CodeFactory.Architecture.Blazor.Server/AddMissingLogicMembers.cs b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/AddMissingLogicMembers.cs similarity index 99% rename from src/Architecture/CodeFactory.Architecture.Blazor.Server/AddMissingLogicMembers.cs rename to src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/AddMissingLogicMembers.cs index 0061655..339002c 100644 --- a/src/Architecture/CodeFactory.Architecture.Blazor.Server/AddMissingLogicMembers.cs +++ b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/AddMissingLogicMembers.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; using System.Windows; -namespace CodeFactory.Architecture.Blazor.Server +namespace CodeFactory.Architecture.Blazor.Server.CSharpFile { /// /// Code factory command for automation of a C# document when selected from a project in solution explorer. @@ -210,9 +210,8 @@ public override async Task ExecuteCommandAsync(VsCSharpSource result) var boundChecks = new List { - - new BoundsCheckBlockNullNDFException(true,loggerBlock), - new BoundsCheckBlockStringNDFException(true,loggerBlock) + new BoundsCheckBlockStringNDFException(true,loggerBlock), + new BoundsCheckBlockNullNDFException(true,loggerBlock) }; var tryBlock = new TryBlockStandard(loggerBlock,catchBlocks); diff --git a/src/Architecture/CodeFactory.Architecture.Blazor.Server/AddMissingRepositoryMembers.cs b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/AddMissingRepositoryMembers.cs similarity index 99% rename from src/Architecture/CodeFactory.Architecture.Blazor.Server/AddMissingRepositoryMembers.cs rename to src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/AddMissingRepositoryMembers.cs index 183babc..90c59c1 100644 --- a/src/Architecture/CodeFactory.Architecture.Blazor.Server/AddMissingRepositoryMembers.cs +++ b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/AddMissingRepositoryMembers.cs @@ -16,7 +16,7 @@ using System.Threading.Tasks; using System.Windows; -namespace CodeFactory.Architecture.Blazor.Server +namespace CodeFactory.Architecture.Blazor.Server.CSharpFile { /// /// Code factory command for automation of a C# document when selected from a project in solution explorer. diff --git a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshEFRepository.cs b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshEFRepository.cs similarity index 71% rename from src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshEFRepository.cs rename to src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshEFRepository.cs index fe6f168..6d925ad 100644 --- a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshEFRepository.cs +++ b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshEFRepository.cs @@ -15,8 +15,10 @@ using System.Text; using System.Threading.Tasks; using System.Windows; +using CodeFactory.Automation.NDF.Logic.Testing.MSTest; +using CodeFactory.Automation.NDF.Logic.General; -namespace CodeFactory.Architecture.Blazor.Server +namespace CodeFactory.Architecture.Blazor.Server.CSharpFile { /// /// Code factory command for automation of a C# document when selected from a project in solution explorer. @@ -92,27 +94,60 @@ public RefreshEFRepository(ILogger logger, IVsActions vsActions) : base(logger, /// public static string IntegrationTestFolder = "IntegrationTestFolder"; + /// + /// The name of the entity framework context class. + /// public static string EFContextClassName = "EFContextClassName"; /// - /// Comma seperated list of the prefixes to remove from the EF class name when generating the target application entity model. + /// Optional, list of prefixes seperated by comma to remove from the name of the entity framework entity. + /// + public static string EFEntityRemovePrefixes = "EFEntityRemovePrefixes"; + + /// + /// Optional, list of prefixes seperated by comma to remove from the name of the entity framework entity. + /// + public static string EFEntityRemoveSuffixes = "EFEntityRemoveSuffixes"; + + /// + /// Optional, prefix to assign to a repository when creating. + /// + public static string RepositoryPrefix = "RepositoryPrefix"; + + /// + /// Optional, suffix to assign to a repository when creating. + /// + public static string RepositorySuffix = "RepositoryPrefix"; + + /// + /// Optional, prefix to assign to a application model when creating. /// - public static string RemoveNamePrefixesForEntityModel = "RemoveNamePrefixesForEntityModel"; + public static string AppModelPrefix ="AppModelPrefix"; /// - /// The repository and repository contract suffix to assign. + /// Optional, suffix to assign to a application model when creating. /// - public static string RepositoryAndContractSuffix = "RepositoryAndContractSuffix"; + public static string AppModelSuffix ="AppModelSuffix"; /// - /// The prefix to assign to the name of a models validation class. + /// Optional, prefix to assign to a integration test when creating. /// - public static string ModelValidatorPrefix = "ModelValidatorPrefix"; + public static string TestPrefix ="TestPrefix"; /// - /// The suffix to assign to the name of a models validation class. + /// Optional, suffix to assign to a integration test when creating. /// - public static string ModelValidatorSuffix = "ModelValidatorSuffix"; + public static string TestSuffix ="TestSuffix"; + + /// + /// The prefix to assign to the name of a application models validation class. + /// + public static string AppModelValidatorPrefix = "AppModelValidatorPrefix"; + + /// + /// The suffix to assign to the name of a application models validation class. + /// + public static string AppModelValidatorSuffix = "AppModelValidatorSuffix"; /// /// Loads the external configuration definition for this command. @@ -150,35 +185,18 @@ public override ConfigCommand LoadExternalConfigDefinition() ( new ConfigParameter { - Name = RemoveNamePrefixesForEntityModel, - Guidance = "Comma seperated value list of the prefixes in case sensititve format to be removed from the application entity name." - } - ) - .AddParameter - ( - new ConfigParameter - { - Name= RepositoryAndContractSuffix, - Guidance = "Suffix to be assigned to the name of the repository and the contract for the repository." + Name = EFEntityRemovePrefixes, + Guidance = "Comma seperated value list of the prefixes in case sensitive format to be removed from the entity framework entity name when creating new objects." } ) .AddParameter ( new ConfigParameter - { - Name= ModelValidatorPrefix, - Guidance = "The prefix to assign to the name of a models validation class." - } - ) - .AddParameter - ( - new ConfigParameter - { - Name= ModelValidatorSuffix, - Guidance = "The suffix to assign to the name of a models validation class." + { + Name = EFEntityRemoveSuffixes, + Guidance = "Comma seperated value list of the suffixes in case sensitive format to be removed from the entity framework entity name when creating new objects." } ) - ) .AddProject ( @@ -198,6 +216,23 @@ public override ConfigCommand LoadExternalConfigDefinition() "Optional, set the relative path from the root of the project. If it is more then one directory deep then use '/' instead of back slashes." } ) + .AddParameter + ( + new ConfigParameter + { + Name = AppModelPrefix, + Guidance = "Optional, prefix to assign to the application model entity when it is created." + } + ) + .AddParameter + ( + new ConfigParameter + { + Name = AppModelSuffix, + Guidance = "Optional, suffix to assign to the application model entity when it is created.", + Value = "AppModel" + } + ) ) .AddProject ( @@ -217,6 +252,23 @@ public override ConfigCommand LoadExternalConfigDefinition() "Optional, set the relative path from the root of the project. If it is more then one directory deep then use '/' instead of back slashes." } ) + .AddParameter + ( + new ConfigParameter + { + Name = RepositoryPrefix, + Guidance = "Optional, prefix to assign to the repository when it is created." + } + ) + .AddParameter + ( + new ConfigParameter + { + Name = RepositorySuffix, + Guidance = "Optional, suffix to assign to the repository when it is created.", + Value = "Repository" + } + ) ) .AddProject ( @@ -255,6 +307,23 @@ public override ConfigCommand LoadExternalConfigDefinition() "Optional, set the relative path from the root of the project. If it is more then one directory deep then use '/' instead of back slashes." } ) + .AddParameter + ( + new ConfigParameter + { + Name = TestPrefix, + Guidance = "Optional, prefix to assign to the integration test when it is created." + } + ) + .AddParameter + ( + new ConfigParameter + { + Name = TestSuffix, + Guidance = "Optional, suffix to assign to the integration test when it is created.", + Value = "Test" + } + ) ); return command; @@ -307,18 +376,31 @@ public override async Task ExecuteCommandAsync(VsCSharpSource result) VsProjectFolder efModelFolder = await VisualStudioActions.GetProjectFolderFromConfigAsync(command.ExecutionProject, ExecutionModelFolder); + var contextClassName = command.ExecutionProject.ParameterValue(EFContextClassName); + var efEntityRemovePrefixs = command.ExecutionProject.ParameterValue(EFEntityRemovePrefixes); + var efEntityRemoveSuffixes = command.ExecutionProject.ParameterValue(EFEntityRemoveSuffixes); + VsProject appModelProject = await VisualStudioActions.GetProjectFromConfigAsync(command.Project(EntityProject)) ?? throw new CodeFactoryException("Could not load the entity model project, cannot refresh the EF repository."); VsProjectFolder appModelFolder = await VisualStudioActions.GetProjectFolderFromConfigAsync(command.Project(EntityProject), EntityFolder); + var appModelPrefix = command.Project(EntityProject).ParameterValue(AppModelPrefix); + var appModelSuffix = command.Project(EntityProject).ParameterValue(AppModelSuffix); + var appModelValidatorPrefix = command.Project(EntityProject).ParameterValue(AppModelValidatorPrefix); + var appModelValidatorSuffix = command.Project(EntityProject).ParameterValue(AppModelValidatorSuffix); + VsProject repoProject = await VisualStudioActions.GetProjectFromConfigAsync(command.Project(RepoProject)) ?? throw new CodeFactoryException("Could not load the repository project, cannot refresh the EF repository."); VsProjectFolder repoFolder = await VisualStudioActions.GetProjectFolderFromConfigAsync(command.Project(RepoProject), RepoFolder); + var repoPrefix = command.Project(RepoProject).ParameterValue(RepositoryPrefix); + + var repoSuffix = command.Project(RepoProject).ParameterValue(RepositorySuffix); + VsProject contractProject = await VisualStudioActions.GetProjectFromConfigAsync(command.Project(RepoContractProject)) ?? throw new CodeFactoryException( @@ -334,31 +416,9 @@ await VisualStudioActions.GetProjectFromConfigAsync(command.Project(RepoContract await VisualStudioActions.GetProjectFolderFromConfigAsync(command.Project(IntegrationTestProject), IntegrationTestFolder); - var contextClassName = command.ExecutionProject.ParameterValue(EFContextClassName); - - var appModelPrefixReplacements = command.ExecutionProject.ParameterValue(RemoveNamePrefixesForEntityModel); - - var modelValidatorPrefix = command.ExecutionProject.ParameterValue(ModelValidatorPrefix); - var modelValidatorSuffix = command.ExecutionProject.ParameterValue(ModelValidatorSuffix); - - NameManagement nameManagement = null; + var testPrefix = testProject != null ? command.Project(IntegrationTestProject).ParameterValue(TestPrefix) : null; - if(!string.IsNullOrEmpty(appModelPrefixReplacements)) - { - var prefixes = appModelPrefixReplacements.Split(','); - - var renamePrefixes = new List(); - foreach (var prefix in prefixes) - { - var formattedPrefix = prefix.Trim(); - - if(!string.IsNullOrEmpty(formattedPrefix)) renamePrefixes.Add(formattedPrefix); - } - - if(renamePrefixes.Any()) nameManagement = NameManagement.Init(renamePrefixes,null,null,null); - } - - string repositorySuffix = command.ExecutionProject.ParameterValue(RepositoryAndContractSuffix); + var testSuffix = testProject != null ? command.Project(IntegrationTestProject).ParameterValue(TestSuffix) : null; if (string.IsNullOrWhiteSpace(contextClassName)) throw new CodeFactoryException( @@ -377,29 +437,44 @@ await VisualStudioActions.GetProjectFolderFromConfigAsync(command.Project(Integr var efModel = result.SourceCode?.Classes?.FirstOrDefault() ?? throw new CodeFactoryException("The EF entity class could not be loaded, cannot refresh the EF repository."); - var appModel = (await VisualStudioActions.RefreshPOCOAsync(efModel, appModelProject, + var nameManagement = NameManagement.Init(efEntityRemovePrefixs,efEntityRemoveSuffixes,appModelPrefix,appModelSuffix); + + var appModel = (await VisualStudioActions.RefreshModelAsync(efModel, appModelProject, EntityModelNamespaces(),nameManagement, appModelFolder, $"Application data model that supports '{efModel.Name}'", true,useSourceProperty: RepositoryBuilder.UseSourceProperty)) ?? throw new CodeFactoryException($"Could not load the entity that supports the ef model '{efModel.Name}', cannot refresh the EF repository."); - NameManagement modelValidatorNameManagement = null; + string noReplacePrefix = null; + string noReplaceSuffix = null; - string removePrefixes = null; - - string removeSuffixes = null; - if(!string.IsNullOrEmpty(modelValidatorSuffix) | !string.IsNullOrEmpty(modelValidatorPrefix)) modelValidatorNameManagement = NameManagement.Init(removePrefixes,removeSuffixes,modelValidatorPrefix, modelValidatorSuffix); - - - var validation = (await VisualStudioActions.RefreshValidationAsync(appModel,appModelProject,appModelFolder,modelValidatorNameManagement)) + var modelValidatorNameManagement = NameManagement.Init(noReplacePrefix,noReplaceSuffix,appModelValidatorPrefix,appModelValidatorSuffix); + + var validation = (await VisualStudioActions.RefreshValidationClassAsync(appModel,appModelProject,appModelFolder,modelValidatorNameManagement)) ?? throw new CodeFactoryException($"Could not refresh the validation for the app model '{appModel.Name}', cannot refresh the EF repository."); await VisualStudioActions.RefreshFluentValidationAsync(efModel,appModel,validation); - await VisualStudioActions.RefreshEntityFrameworkEntityTransform(appModel, efModel, efModelProject, efModelFolder); - var repoClass = await VisualStudioActions.RefreshEFRepositoryAsync(efModel, repoProject, contractProject, appModel, - contextClass, supportsNDF, supportsLogging, repoFolder, contractFolder,nameSuffix:repositorySuffix); + var repositoryName = NameManagement.Init(efEntityRemovePrefixs,efEntityRemoveSuffixes,repoPrefix,repoSuffix).FormatName(efModel.Name); + + var repoClass = await VisualStudioActions.RefreshEFRepositoryAsync(repositoryName,efModel, repoProject, contractProject, appModel, + contextClass, supportsNDF, supportsLogging, repoFolder, contractFolder); + + + if(repoClass != null & testProject != null) + { + var contractName = $"I{repositoryName}"; + + CsInterface contractInterface = contractFolder != null ? (await contractFolder.FindCSharpSourceByInterfaceNameAsync(contractName))?.SourceCode?.Interfaces?.FirstOrDefault() + : (await contractProject.FindCSharpSourceByInterfaceNameAsync(contractName))?.SourceCode?.Interfaces?.FirstOrDefault(); + + if(contractInterface != null) + { + var testName = NameManagement.Init(noReplacePrefix,noReplaceSuffix,testPrefix,testSuffix).FormatName(repositoryName); + await VisualStudioActions.RefreshMSTestIntegrationTestAsync(testName, contractInterface,testProject); + } + } } catch (CodeFactoryException codeFactoryError) diff --git a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshFluentValidation.cs b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshFluentValidation.cs similarity index 96% rename from src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshFluentValidation.cs rename to src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshFluentValidation.cs index 06ddbac..d6efd46 100644 --- a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshFluentValidation.cs +++ b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshFluentValidation.cs @@ -12,8 +12,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Windows; -namespace CodeFactory.Architecture.Blazor.Server +namespace CodeFactory.Architecture.Blazor.Server.CSharpFile { /// /// Code factory command for automation of a C# document when selected from a project in solution explorer. @@ -177,6 +178,10 @@ await VisualStudioActions.GetProjectFromConfigAsync(command.ExecutionProject) var validationClass = VisualStudioActions.RefreshValidationAsync(sourceClass, sourceProject,sourceFolder,nameManagement); } + catch (CodeFactoryException codeFactoryError) + { + MessageBox.Show(codeFactoryError.Message, "Automation Error", MessageBoxButton.OK, MessageBoxImage.Error); + } catch (Exception unhandledError) { _logger.Error($"The following unhandled error occurred while executing the solution explorer C# document command {commandTitle}. ", diff --git a/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshLogic.cs b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshLogic.cs new file mode 100644 index 0000000..38820d1 --- /dev/null +++ b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshLogic.cs @@ -0,0 +1,305 @@ +using CodeFactory.Automation.NDF.Logic.General; +using CodeFactory.Automation.Standard.Logic; +using CodeFactory.WinVs; +using CodeFactory.WinVs.Commands; +using CodeFactory.WinVs.Commands.SolutionExplorer; +using CodeFactory.WinVs.Logging; +using CodeFactory.WinVs.Models.CSharp; +using CodeFactory.WinVs.Models.CSharp.Builder; +using CodeFactory.WinVs.Models.ProjectSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; + +namespace CodeFactory.Architecture.Blazor.Server.CSharpFile +{ + /// + /// Code factory command for automation of a C# document when selected from a project in solution explorer. + /// + public class RefreshLogic : CSharpSourceCommandBase + { + private static readonly string commandTitle = "Refresh Logic"; + private static readonly string commandDescription = "Refreshes the logic implementation for the target logic interface."; + +#pragma warning disable CS1998 + + /// + public RefreshLogic(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription) + { + //Intentionally blank + } + + #region External Configuration + + /// + /// The fully qualified name of the command to be used with configuration. + /// + public static string Type = typeof(RefreshLogic).FullName; + + /// + /// Exection project for the command. + /// + public static string ExecutionProject = "ExecutionProject"; + + /// + /// Execution folder for the command. + /// + public static string ExecutionFolder = "ExecutionFolder"; + + /// + /// The logic project that holds the logic class to be refreshed. + /// + public static string LogicProject = "LogicProject"; + + /// + /// Optional, folder where the logic class it be refreshed is located. + /// + public static string LogicFolder = "LogicFolder"; + + /// + /// Optional, comma seperated value list of prefixes to be removed from the logic contracts name. + /// + public static string RemovePrefixes = "RemovePrefixes"; + + /// + /// Optional, comma seperated value list of the suffixes to be removed from the logic contract name. + /// + public static string RemoveSuffixes = "RemoveSuffixes"; + + /// + /// Optional, the prefix to append to the logic class name. + /// + public static string LogicPrefix = "LogicPrefix"; + + /// + /// Optional, the suffix to append to the logic class name. + /// + public static string LogicSuffix = "LogicSuffix"; + + /// + /// Loads the external configuration definition for this command. + /// + /// Will return the command configuration or null if this command does not support external configurations. + public override ConfigCommand LoadExternalConfigDefinition() + { + return new ConfigCommand { Name = commandTitle, Category = "Logic", CommandType = Type, } + .UpdateExecutionProject + ( + new ConfigProject + { + Name = ExecutionProject, + Guidance = "Project that contains the interface that triggers the automation." + } + .AddFolder + ( + new ConfigFolder + { + Name = ExecutionFolder, + Required = false, + Guidance = "Optional, project folder that contains the interface that triggers the automation." + } + ) + .AddParameter + ( + new ConfigParameter + { + Name = RemovePrefixes, + Guidance = "Optional, comma seperated value list of prefix values to remove from the interface name." + } + ) + .AddParameter + ( + new ConfigParameter + { + Name = RemoveSuffixes, + Guidance = "Optional, comma seperated value list of suffix values to remove from the interface name." + } + ) + + ) + .AddProject + ( + new ConfigProject + { + Name = LogicProject, + Guidance = "Name of the project that hosts the logic class to be refreshed." + } + .AddFolder + ( + new ConfigFolder + { + Name = LogicFolder, + Required = false, + Guidance = "Optional, The project folder the logic class is hosted in." + } + ) + .AddParameter + ( + new ConfigParameter + { + Name = LogicPrefix, + Guidance = "Optional, the prefix to append to the logic class name." + + } + ) + .AddParameter + ( + new ConfigParameter + { + Name = LogicSuffix, + Guidance = "Optional, the suffix to append to the logic class name." + + } + ) + ); + + + } + #endregion + + #region Overrides of VsCommandBase + + /// + /// Validation logic that will determine if this command should be enabled for execution. + /// + /// The target model data that will be used to determine if this command should be enabled. + /// Boolean flag that will tell code factory to enable this command or disable it. + public override async Task EnableCommandAsync(VsCSharpSource result) + { + //Result that determines if the command is enabled and visible in the context menu for execution. + bool isEnabled = false; + + try + { + + var logicContract = result.SourceCode?.Interfaces.FirstOrDefault(); + + isEnabled = logicContract != null; + + ConfigCommand command = null; + + if( isEnabled ) + { + command = await ConfigManager.LoadCommandByFolderAsync(Type, ExecutionFolder, result) + ?? await ConfigManager.LoadCommandByProjectAsync(Type, result); + + isEnabled = command != null; + } + + if(isEnabled) isEnabled = await LogicNeedsUpdatesAsync(command,logicContract); + + } + catch (Exception unhandledError) + { + _logger.Error($"The following unhandled error occurred while checking if the solution explorer C# document command {commandTitle} is enabled. ", + unhandledError); + isEnabled = false; + } + + return isEnabled; + } + + /// + /// Code factory framework calls this method when the command has been executed. + /// + /// The code factory model that has generated and provided to the command to process. + public override async Task ExecuteCommandAsync(VsCSharpSource result) + { + try + { + var logicContract = result.SourceCode?.Interfaces.FirstOrDefault() + ?? throw new CodeFactoryException("Could not load the source interface cannot refresh the logic."); + + var commandConfig = await ConfigManager.LoadCommandByFolderAsync(Type, ExecutionFolder, result) + ?? await ConfigManager.LoadCommandByProjectAsync(Type, result) + ?? throw new CodeFactoryException("Could not load the configuratin, cannot refresh the logic"); + + var logicName = GenerateLogicClassName(commandConfig,logicContract) + ?? throw new CodeFactoryException("Could not determine the logic class name, cannot refresh the logic"); + + var contractProject = await VisualStudioActions.GetProjectFromConfigAsync(commandConfig.ExecutionProject) + ?? throw new CodeFactoryException("Cannot load the contract interface project, cannot refresh the logic"); + + var contractProjectFolder = await VisualStudioActions.GetProjectFolderFromConfigAsync(commandConfig.ExecutionProject,ExecutionFolder); + + var logicProject = await VisualStudioActions.GetProjectFromConfigAsync(commandConfig.Project(LogicProject)) + ?? throw new CodeFactoryException("Could not load the logic project, cannot refresh the logic."); + + var logicProjectFolder = await VisualStudioActions.GetProjectFolderFromConfigAsync(commandConfig.Project(LogicProject),LogicFolder); + + await VisualStudioActions.RefreshLogicAsync(logicName,logicContract.Name,logicProject,contractProject,logicFolder:logicProjectFolder,contractFolder: contractProjectFolder); + + } + catch (CodeFactoryException codeFactoryError) + { + MessageBox.Show(codeFactoryError.Message, "Automation Error", MessageBoxButton.OK, MessageBoxImage.Error); + } + catch (Exception unhandledError) + { + _logger.Error($"The following unhandled error occurred while executing the solution explorer C# document command {commandTitle}. ", + unhandledError); + + } + + } + + #endregion + + /// + /// Helper method that determines if the command should be activated. Checks to make sure all the conditions are met to refresh the logic. + /// + /// The commands configuration. + /// The source interface that is being used to refresh the logic. + /// True if the command should be enabled, false if not. + private async Task LogicNeedsUpdatesAsync(ConfigCommand command,CsInterface interfaceContract) + { + + var logicProject = await VisualStudioActions.GetProjectFromConfigAsync(command.Project(LogicProject)); + + if(logicProject == null) return false; + + var logicFolder = await VisualStudioActions.GetProjectFolderFromConfigAsync(command.Project(LogicProject),LogicFolder); + + var logicClassName = GenerateLogicClassName(command,interfaceContract); + + if(logicClassName == null) return false; + + var logicSource = logicFolder != null + ? await logicFolder.FindCSharpSourceByClassNameAsync(logicClassName,false) + : await logicProject.FindCSharpSourceByClassNameAsync(logicClassName,false); + + if(logicSource == null) return true; + + var logicClass = logicSource?.SourceCode?.Classes?.FirstOrDefault(); + + if(logicClass == null) return false; + + return logicClass.GetMissingInterfaceMembers().Any(); + + } + + /// + /// Generates the name of the logic class to be refreshed. + /// + /// The command configuration to use to generate the class name. + /// The interface that implements the logic class to refresh. + /// Formatted name or null if the name cannot be determined. + private string GenerateLogicClassName(ConfigCommand command,CsInterface interfaceContract) + { + if(command == null) return null; + + if(interfaceContract == null) return null; + + var removePrefixes = command.ExecutionProject.ParameterValue(RemovePrefixes); + var removeSuffixes = command.ExecutionProject.ParameterValue(RemoveSuffixes); + var logicPrefix = command.Project(LogicProject).ParameterValue(LogicPrefix); + var logicSuffix = command.Project(LogicProject).ParameterValue(LogicSuffix); + + return NameManagement.Init(removePrefixes,removeSuffixes, logicPrefix, logicSuffix).FormatName(interfaceContract.Name.GenerateCSharpFormattedClassName()); + + } + } +} diff --git a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshRestService.cs b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshRestService.cs similarity index 99% rename from src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshRestService.cs rename to src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshRestService.cs index f2c5d6f..7bd33ef 100644 --- a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshRestService.cs +++ b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshRestService.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; using System.Windows; -namespace CodeFactory.Architecture.Blazor.Server +namespace CodeFactory.Architecture.Blazor.Server.CSharpFile { /// /// Code factory command for automation of a C# document when selected from a project in solution explorer. diff --git a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshTest.cs b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshTest.cs similarity index 76% rename from src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshTest.cs rename to src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshTest.cs index 723b29b..93e9500 100644 --- a/src/Architecture/CodeFactory.Architecture.Blazor.Server/RefreshTest.cs +++ b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/RefreshTest.cs @@ -1,4 +1,5 @@ using CodeFactory.Automation.NDF.Logic.Testing.MSTest; +using CodeFactory.Automation.Standard.Logic; using CodeFactory.WinVs; using CodeFactory.WinVs.Commands; using CodeFactory.WinVs.Commands.SolutionExplorer; @@ -11,8 +12,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Windows; -namespace CodeFactory.Architecture.Blazor.Server +namespace CodeFactory.Architecture.Blazor.Server.CSharpFile { /// /// Code factory command for automation of a C# document when selected from a project in solution explorer. @@ -47,6 +49,17 @@ public RefreshTest(ILogger logger, IVsActions vsActions) : base(logger, vsAction /// public static string TestProject = "TestProject"; + /// + /// Prefix to append to the name of the integration test being created. + /// + public static string TestPrefix = "TestPrefix"; + + /// + /// Suffix to append to the name of the integration test being created. + /// + public static string TestSuffix = "TestSuffix"; + + /// /// Loads the external configuration definition for this command. /// @@ -60,14 +73,32 @@ public override ConfigCommand LoadExternalConfigDefinition() Guidance="Automation command that generates integration tests from a provided interface." } .UpdateExecutionProject - ( + ( new ConfigProject { Name = ExecutionProject, Guidance = "Enter the name of the project the command will trigger from." } - ) + ) .AddProject - ( + ( new ConfigProject { Name = TestProject, Guidance = "Enter the name of the project that hosts the MSTest integration testings." } - ); + ) + .AddParameter + ( + new ConfigParameter + { + Name = TestPrefix, + Guidance = "Optional, prefix to append to the name of the integration test when being created." + } + ) + .AddParameter + ( + new ConfigParameter + { + Name = TestSuffix, + Guidance = "Optional, Suffix to append to the name of the integration test when being created.", + Value = "Test" + + } + ); return config; } @@ -129,12 +160,26 @@ public override async Task ExecuteCommandAsync(VsCSharpSource result) var testProject = (await VisualStudioActions.GetProjectFromConfigAsync(config.Project(TestProject))) ?? throw new CodeFactoryException("Could not locate the test project cannot refresh the test."); + + var targetInterface = result.SourceCode?.Interfaces?.FirstOrDefault() ?? throw new CodeFactoryException("Could not locate the interface to have tests created from."); - var test = VisualStudioActions.RefreshMSTestIntegrationTestAsync(targetInterface, testProject); + var testPrefix = config.Project(TestProject).ParameterValue(TestPrefix); + var testSuffix = config.Project(TestProject).ParameterValue(TestSuffix); + + string noRemove = null; + + var testName = NameManagement.Init(noRemove,noRemove,testPrefix,testSuffix).FormatName(targetInterface.Name.GenerateCSharpFormattedClassName()); + var test = VisualStudioActions.RefreshMSTestIntegrationTestAsync(testName,targetInterface, testProject); + + + } + catch (CodeFactoryException codeFactoryError) + { + MessageBox.Show(codeFactoryError.Message, "Automation Error", MessageBoxButton.OK, MessageBoxImage.Error); } catch (Exception unhandledError) { diff --git a/src/Architecture/CodeFactory.Architecture.Blazor.Server/UpdateLogicImplementation.cs b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/UpdateLogicImplementation.cs similarity index 85% rename from src/Architecture/CodeFactory.Architecture.Blazor.Server/UpdateLogicImplementation.cs rename to src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/UpdateLogicImplementation.cs index 8e0663f..60e57c4 100644 --- a/src/Architecture/CodeFactory.Architecture.Blazor.Server/UpdateLogicImplementation.cs +++ b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CSharpFile/UpdateLogicImplementation.cs @@ -1,4 +1,5 @@ -using CodeFactory.Automation.Standard.Logic; +using CodeFactory.Automation.NDF.Logic.General; +using CodeFactory.Automation.Standard.Logic; using CodeFactory.WinVs; using CodeFactory.WinVs.Commands; using CodeFactory.WinVs.Commands.SolutionExplorer; @@ -11,8 +12,9 @@ using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Windows; -namespace CodeFactory.Architecture.Blazor.Server +namespace CodeFactory.Architecture.Blazor.Server.CSharpFile { /// /// Code factory command for automation of a C# document when selected from a project in solution explorer. @@ -50,12 +52,12 @@ public UpdateLogicImplementation(ILogger logger, IVsActions vsActions) : base(lo /// /// List of prefixes to be removed from the interface definition. /// - public static string RepoPrefix = "RepoPrefix"; + public static string RemoveRepoPrefix = "RemoveRepoPrefix"; /// /// List of suffixes to be removed from the interface definition. /// - public static string RepoSuffix = "RepoSuffix"; + public static string RemoveRepoSuffix = "RepoSuffix"; /// /// The project the logic contract can be found in. @@ -115,7 +117,7 @@ public override ConfigCommand LoadExternalConfigDefinition() ( new ConfigParameter { - Name = RepoPrefix, + Name = RemoveRepoPrefix, Guidance = "List of prefixes to be removed from the beginning of the contract name." } @@ -124,7 +126,7 @@ public override ConfigCommand LoadExternalConfigDefinition() ( new ConfigParameter { - Name = RepoSuffix, + Name = RemoveRepoSuffix, Guidance = "List of suffixes to be removed from the end of the contract name." } @@ -218,8 +220,8 @@ public override async Task EnableCommandAsync(VsCSharpSource result) if(isEnabled ) { - var repoPrefix = command.ExecutionProject.ParameterValue(RepoPrefix); - var repoSuffix = command.ExecutionProject.ParameterValue(RepoSuffix); + var repoPrefix = command.ExecutionProject.ParameterValue(RemoveRepoPrefix); + var repoSuffix = command.ExecutionProject.ParameterValue(RemoveRepoSuffix); isEnabled = IsRepositoryContract(repoInterface,repoPrefix,repoSuffix); } } @@ -248,24 +250,35 @@ public override async Task ExecuteCommandAsync(VsCSharpSource result) var repoContract = result.SourceCode?.Interfaces.FirstOrDefault() ?? throw new CodeFactoryException("Could not load the repository contract cannot update the logic implementation."); - var logicContractProject = await VisualStudioActions.GetProjectFromConfigAsync(command.Project(LogicContractProject)); + var logicContractProject = await VisualStudioActions.GetProjectFromConfigAsync(command.Project(LogicContractProject)) + ?? throw new CodeFactoryException("The logic contract project could not be loaded, cannot refresh the logic class."); var logicContractProjectFolder = await VisualStudioActions.GetProjectFolderFromConfigAsync(command.Project(LogicContractProject),LogicContractProjectFolder); - var repoPrefix = command.ExecutionProject.ParameterValue(RepoPrefix); - var repoSuffix = command.ExecutionProject.ParameterValue(RepoSuffix); + var logicProject = await VisualStudioActions.GetProjectFromConfigAsync(command.Project(LogicProject)) + ?? throw new CodeFactoryException("The logic project could not be loaded, cannot refresh the logic class."); + + var logicProjectFolder = await VisualStudioActions.GetProjectFolderFromConfigAsync(command.Project(LogicProject),LogicProjectFolder); + + var repoRemovePrefix = command.ExecutionProject.ParameterValue(RemoveRepoPrefix); + var repoRemoveSuffix = command.ExecutionProject.ParameterValue(RemoveRepoSuffix); var logicPrefix = command.Project(LogicContractProject)?.ParameterValue(LogicPrefix); var logicSuffix = command.Project(LogicContractProject)?.ParameterValue(LogicSuffix); - var removePrefixes = repoPrefix == null ? null: new List(repoPrefix.Split(',')); - var removeSuffixes = repoSuffix == null ? null: new List(repoSuffix.Split(',')); - var nameManagement = NameManagement.Init(removePrefixes,removeSuffixes,logicPrefix,logicSuffix); + + var logicName = NameManagement.Init(repoRemovePrefix,repoRemoveSuffix,logicPrefix,logicSuffix).FormatName(repoContract.Name.GenerateCSharpFormattedClassName()); - var logicContract = await VisualStudioActions.CloneInterfaceAsync(repoContract,false,logicContractProject, - logicContractProjectFolder,nameManagement,"Logic contract implementation."); + var logicContract = await VisualStudioActions.CloneInterfaceAsync($"I{logicName}", repoContract,false,logicContractProject, + logicContractProjectFolder,"Logic contract implementation."); + + var logicClass = await VisualStudioActions.RefreshLogicAsync(logicName,$"I{logicName}",logicProject,logicContractProject,logicFolder:logicProjectFolder,contractFolder:logicContractProjectFolder); } + catch (CodeFactoryException codeFactoryError) + { + MessageBox.Show(codeFactoryError.Message, "Automation Error", MessageBoxButton.OK, MessageBoxImage.Error); + } catch (Exception unhandledError) { _logger.Error($"The following unhandled error occurred while executing the solution explorer C# document command {commandTitle}. ", diff --git a/src/Architecture/CodeFactory.Architecture.Blazor.Server/CodeFactory.Architecture.Blazor.Server.csproj b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CodeFactory.Architecture.Blazor.Server.csproj index 1b674cd..4d9f31f 100644 --- a/src/Architecture/CodeFactory.Architecture.Blazor.Server/CodeFactory.Architecture.Blazor.Server.csproj +++ b/src/Architecture/CodeFactory.Architecture.Blazor.Server/CodeFactory.Architecture.Blazor.Server.csproj @@ -48,19 +48,20 @@ - - - - - + + + + + + - - - - - - - + + + + + + + @@ -72,6 +73,7 @@ CodeFactory.Automation.Standard.Logic +