diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/AddMissingLogicMembers.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/AddMissingLogicMembersCommand.cs
similarity index 97%
rename from src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/AddMissingLogicMembers.cs
rename to src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/AddMissingLogicMembersCommand.cs
index b9c6ce3..ca3c407 100644
--- a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/AddMissingLogicMembers.cs
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/AddMissingLogicMembersCommand.cs
@@ -19,7 +19,7 @@ namespace CodeFactory.Architecture.AspNetCore.Service.Rest.CSharpFile
///
/// Code factory command for automation of a C# document when selected from a project in solution explorer.
///
- public class AddMissingLogicMembers : CSharpSourceCommandBase
+ public class AddMissingLogicMembersCommand : CSharpSourceCommandBase
{
private static readonly string commandTitle = "Add Missing Logic Members";
private static readonly string commandDescription = "Adds missing contract interface members to the Logic implementation.";
@@ -27,7 +27,7 @@ public class AddMissingLogicMembers : CSharpSourceCommandBase
#pragma warning disable CS1998
///
- public AddMissingLogicMembers(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
+ public AddMissingLogicMembersCommand(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
{
//Intentionally blank
}
@@ -37,7 +37,7 @@ public AddMissingLogicMembers(ILogger logger, IVsActions vsActions) : base(logge
///
/// The fully qualified name of the command to be used with configuration.
///
- public static string Type = typeof(AddMissingLogicMembers).FullName;
+ public static string Type = typeof(AddMissingLogicMembersCommand).FullName;
///
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/AddMissingRepositoryMembers.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/AddMissingRepositoryMembersCommand.cs
similarity index 96%
rename from src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/AddMissingRepositoryMembers.cs
rename to src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/AddMissingRepositoryMembersCommand.cs
index 9db81f5..3f4df55 100644
--- a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/AddMissingRepositoryMembers.cs
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/AddMissingRepositoryMembersCommand.cs
@@ -12,16 +12,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace CodeFactory.Architecture.AspNetCore.Service.Rest.CSharpFile
{
- ///
- /// Code factory command for automation of a C# document when selected from a project in solution explorer.
- ///
- public class AddMissingRepositoryMembers : CSharpSourceCommandBase
+ ///
+ /// Code factory command for automation of a C# document when selected from a project in solution explorer.
+ ///
+ public class AddMissingRepositoryMembersCommand : CSharpSourceCommandBase
{
private static readonly string commandTitle = "Add Missing Repository Members";
private static readonly string commandDescription = "Adds missing contract interface members to the repository implementation.";
@@ -29,7 +28,7 @@ public class AddMissingRepositoryMembers : CSharpSourceCommandBase
#pragma warning disable CS1998
///
- public AddMissingRepositoryMembers(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
+ public AddMissingRepositoryMembersCommand(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
{
//Intentionally blank
}
@@ -39,7 +38,7 @@ public AddMissingRepositoryMembers(ILogger logger, IVsActions vsActions) : base(
///
/// The fully qualified name of the command to be used with configuration.
///
- public static string Type = typeof(AddMissingRepositoryMembers).FullName;
+ public static string Type = typeof(AddMissingRepositoryMembersCommand).FullName;
///
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshEFRepository.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshEFRepository.cs
deleted file mode 100644
index 96b5d8e..0000000
--- a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshEFRepository.cs
+++ /dev/null
@@ -1,529 +0,0 @@
-using CodeFactory.Automation.NDF.Logic.Data.Sql.EF;
-using CodeFactory.Automation.NDF.Logic;
-using CodeFactory.Automation.Standard.Logic.FluentValidation;
-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;
-using CodeFactory.Automation.NDF.Logic.Testing.MSTest;
-using CodeFactory.Automation.NDF.Logic.General;
-
-namespace CodeFactory.Architecture.AspNetCore.Service.Rest.CSharpFile
-{
- ///
- /// Code factory command for automation of a C# document when selected from a project in solution explorer.
- ///
- public class RefreshEFRepository : CSharpSourceCommandBase
- {
- private static readonly string commandTitle = "Refresh EF Repository";
- private static readonly string commandDescription = "Refreshes the EF repository and models implementation.";
-
-
-#pragma warning disable CS1998
-
- ///
- public RefreshEFRepository(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(RefreshEFRepository).FullName;
-
- ///
- /// The execution project that contains the definition of the entity framework entity models.
- ///
- public static string ExecutionProject = "ExecutionProject";
-
- ///
- /// The execution project folder the entity framework models are stored in. This is optional and only used when entities are stored in a target folder.
- ///
- public static string ExecutionModelFolder = "ExecutionModelFolder";
-
- ///
- /// The project where application level entities will be created and stored.
- ///
- public static string EntityProject = "EntityProject";
-
- ///
- /// The target folder where application entities will be created and stored. This is optional and only used when entities are stored in a target folder.
- ///
- public static string EntityFolder = "EntityFolder";
-
- ///
- /// The repository project where repositories will be created or updated in.
- ///
- public static string RepoProject = "RepoProject";
-
- ///
- /// The target folder where repositories will be created and stored. This is optional and only used when repositories are stored in a target folder.
- ///
- public static string RepoFolder = "RepoFolder";
-
- ///
- /// The target project that will store the interface contract definition for the created and updated repositories.
- ///
- public static string RepoContractProject = "RepoContractProject";
-
- ///
- /// The target folder where interface contract definitions for repositories is stored. This is optional and only used with interface contracts for repositories stored in the target folder.
- ///
- public static string RepoContractFolder = "RepoContractFolder";
-
- ///
- /// The target project to generate and update integration tests that support the repository.
- ///
- public static string IntegrationTestProject = "IntegrationTestProject";
-
- ///
- /// The target folder where integration tests will be stored. This is optional and only used with integration tests that are to be stored in the target folder.
- ///
- public static string IntegrationTestFolder = "IntegrationTestFolder";
-
- ///
- /// The name of the entity framework context class.
- ///
- public static string EFContextClassName = "EFContextClassName";
-
- ///
- /// 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 = "RepositorySuffix";
-
- ///
- /// Optional, prefix to assign to a application model when creating.
- ///
- public static string AppModelPrefix ="AppModelPrefix";
-
- ///
- /// Optional, suffix to assign to a application model when creating.
- ///
- public static string AppModelSuffix ="AppModelSuffix";
-
- ///
- /// Optional, prefix to assign to a integration test when creating.
- ///
- public static string TestPrefix ="TestPrefix";
-
- ///
- /// Optional, suffix to assign to a integration test when creating.
- ///
- 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.
- ///
- /// Will return the command configuration or null if this command does not support external configurations.
- public override ConfigCommand LoadExternalConfigDefinition()
- {
- var command = new ConfigCommand { Category = "RefreshEFRepository", Name = "EFRepositoryRefresh", CommandType = Type }
- .UpdateExecutionProject
- (
- new ConfigProject
- {
- Name = ExecutionProject,
- Guidance = "Enter the fully project name for the project that hosts the EF models."
- }
- .AddFolder
- (
- new ConfigFolder
- {
- Name = ExecutionModelFolder,
- Required = false,
- Guidance =
- "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 = EFContextClassName,
- Guidance = "Enter the class name of the database context used by entity framework."
- }
- )
- .AddParameter
- (
- new ConfigParameter
- {
- 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 = 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
- (
- new ConfigProject
- {
- Name = EntityProject,
- Guidance =
- "Enter the full project name for the project that hosts the generated application POCO models that represent the EF Models."
- }
- .AddFolder
- (
- new ConfigFolder
- {
- Name = EntityFolder,
- Required = false,
- Guidance =
- "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"
- }
- )
- .AddParameter
- (
- new ConfigParameter
- {
- Name = AppModelValidatorPrefix,
- Guidance = "Optional, prefix to assign to the application model entity validator when it is created."
-
- }
- )
- .AddParameter
- (
- new ConfigParameter
- {
- Name = AppModelValidatorSuffix,
- Guidance = "Optional, suffix to assign to the application model entity validator when it is created.",
- Value = "Validator"
- }
- )
- )
- .AddProject
- (
- new ConfigProject
- {
- Name = RepoProject,
- Guidance =
- "Enter the full project name for the project that hosts the generated repositories that represent the EF Models."
- }
- .AddFolder
- (
- new ConfigFolder()
- {
- Name = RepoFolder,
- Required = false,
- Guidance =
- "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
- (
- new ConfigProject
- {
- Name = RepoContractProject,
- Guidance =
- "Enter the full project name for the project that hosts the interface definition for the repository contract."
- }
- .AddFolder
- (
- new ConfigFolder
- {
- Name = RepoContractFolder,
- Required = false,
- Guidance =
- "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."
- }
- )
- )
- .AddProject
- (
- new ConfigProject
- {
- Name = IntegrationTestProject,
- Guidance =
- "Enter the full project name for the project that hosts the generated integration test for the repository."
- }
- .AddFolder
- (
- new ConfigFolder()
- {
- Name = IntegrationTestFolder,
- Required = false,
- Guidance =
- "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;
- }
- #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
- {
- isEnabled = (await ConfigManager.LoadCommandByFolderAsync(Type, ExecutionModelFolder, result, FolderLoadType.TargetFolderOnly)) != null;
-
- if (!isEnabled) isEnabled = (await ConfigManager.LoadCommandByProjectAsync(Type, result)) != null;
- }
- 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 command = (await ConfigManager.LoadCommandByFolderAsync(Type, ExecutionModelFolder, result)
- ?? await ConfigManager.LoadCommandByProjectAsync(Type, result))
- ?? throw new CodeFactoryException("Could not load the automation configuration cannot refresh the EF repository.");
-
- VsProject efModelProject = await VisualStudioActions.GetProjectFromConfigAsync(command.ExecutionProject)
- ?? throw new CodeFactoryException("Could load the EF hosting project, cannot refresh the EF repository.");
-
- 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(
- "Could not load the repository contract project, cannot refresh the EF repository.");
-
-
- VsProjectFolder contractFolder = await VisualStudioActions.GetProjectFolderFromConfigAsync(command.Project(RepoContractProject), RepoFolder);
-
- VsProject testProject =
- await VisualStudioActions.GetProjectFromConfigAsync(command.Project(IntegrationTestProject));
-
- VsProjectFolder testFolder =
- await VisualStudioActions.GetProjectFolderFromConfigAsync(command.Project(IntegrationTestProject),
- IntegrationTestFolder);
-
- var testPrefix = testProject != null ? command.Project(IntegrationTestProject).ParameterValue(TestPrefix) : null;
-
- var testSuffix = testProject != null ? command.Project(IntegrationTestProject).ParameterValue(TestSuffix) : null;
-
- if (string.IsNullOrWhiteSpace(contextClassName))
- throw new CodeFactoryException(
- "The entity framework context class name was not provided, cannot refresh the EF repository.");
-
- var contextClass = (await efModelProject.FindCSharpSourceByClassNameAsync(contextClassName))
- ?.SourceCode?.Classes?.FirstOrDefault()
- ?? throw new CodeFactoryException($"The entity framework context class '{contextClassName}' could not be loaded, cannot refresh the EF repository,");
-
- await VisualStudioActions.RefreshDbContextAsync(contextClassName, efModelProject, efModelFolder);
-
- bool supportsLogging = await repoProject.SupportsLogging();
-
- bool supportsNDF = await repoProject.SupportsNDF();
-
- var efModel = result.SourceCode?.Classes?.FirstOrDefault()
- ?? throw new CodeFactoryException("The EF entity class could not be loaded, cannot refresh the EF repository.");
-
- 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.");
-
- string noReplacePrefix = null;
- string noReplaceSuffix = null;
-
- 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 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)
- {
- 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 creates the default namespaces to add to a new entity class that is crearted.
- ///
- /// List of using statements to use.
- private List EntityModelNamespaces()
- {
- return new List
- {
- new ManualUsingStatementNamespace("System"),
- new ManualUsingStatementNamespace("System.Collections.Generic"),
- new ManualUsingStatementNamespace("System.Linq"),
- new ManualUsingStatementNamespace("System.Text")
- };
- }
- }
-
-}
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshEFRepositoryCommand.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshEFRepositoryCommand.cs
new file mode 100644
index 0000000..85311b8
--- /dev/null
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshEFRepositoryCommand.cs
@@ -0,0 +1,558 @@
+using CodeFactory.Automation.NDF.Logic;
+using CodeFactory.Automation.NDF.Logic.Data.Sql.EF;
+using CodeFactory.Automation.NDF.Logic.General;
+using CodeFactory.Automation.NDF.Logic.Testing.MSTest;
+using CodeFactory.Automation.NDF.Logic.Testing.XUnit;
+using CodeFactory.Automation.Standard.Logic;
+using CodeFactory.Automation.Standard.Logic.Extensions;
+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.ProjectSystem;
+using Drives.Automation.NDF.Logic.Data.Sql.EF;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace CodeFactory.Architecture.AspNetCore.Service.Rest.CSharpFile
+{
+ ///
+ /// Code factory command for automation of a C# document when selected from a project in solution explorer.
+ ///
+ public class RefreshEFRepositoryCommand : CSharpSourceCommandBase
+ {
+ private static readonly string commandTitle = "Refresh EF Repository";
+ private static readonly string commandDescription = "Refreshes the EF repository and models implementation.";
+
+
+#pragma warning disable CS1998
+
+ ///
+ public RefreshEFRepositoryCommand(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(RefreshEFRepositoryCommand).FullName;
+
+ ///
+ /// The execution project that contains the definition of the entity framework entity models.
+ ///
+ public static string ExecutionProject = "ExecutionProject";
+
+ ///
+ /// The execution project folder the entity framework models are stored in. This is optional and only used when entities are stored in a target folder.
+ ///
+ public static string ExecutionModelFolder = "ExecutionModelFolder";
+
+ ///
+ /// The project where application level entities will be created and stored.
+ ///
+ public static string EntityProject = "EntityProject";
+
+ ///
+ /// The target folder where application entities will be created and stored. This is optional and only used when entities are stored in a target folder.
+ ///
+ public static string EntityFolder = "EntityFolder";
+
+ ///
+ /// The repository project where repositories will be created or updated in.
+ ///
+ public static string RepoProject = "RepoProject";
+
+ ///
+ /// The target folder where repositories will be created and stored. This is optional and only used when repositories are stored in a target folder.
+ ///
+ public static string RepoFolder = "RepoFolder";
+
+ ///
+ /// The target project that will store the interface contract definition for the created and updated repositories.
+ ///
+ public static string RepoContractProject = "RepoContractProject";
+
+ ///
+ /// The target folder where interface contract definitions for repositories is stored. This is optional and only used with interface contracts for repositories stored in the target folder.
+ ///
+ public static string RepoContractFolder = "RepoContractFolder";
+
+ ///
+ /// The target project to generate and update integration tests that support the repository.
+ ///
+ public static string IntegrationTestProject = "IntegrationTestProject";
+
+ ///
+ /// The target folder where integration tests will be stored. This is optional and only used with integration tests that are to be stored in the target folder.
+ ///
+ public static string IntegrationTestFolder = "IntegrationTestFolder";
+
+ ///
+ /// The name of the entity framework context class.
+ ///
+ public static string EFContextClassName = "EFContextClassName";
+
+ ///
+ /// 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 = "RepositorySuffix";
+
+ ///
+ /// Optional, prefix to assign to a application model when creating.
+ ///
+ public static string AppModelPrefix = "AppModelPrefix";
+
+ ///
+ /// Optional, suffix to assign to a application model when creating.
+ ///
+ public static string AppModelSuffix = "AppModelSuffix";
+
+ ///
+ /// Optional, prefix to assign to a integration test when creating.
+ ///
+ public static string TestPrefix = "TestPrefix";
+
+ ///
+ /// Optional, suffix to assign to a integration test when creating.
+ ///
+ 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";
+
+ ///
+ /// Optional, flag to generate default Add, Update, and Delete operations.
+ ///
+ public static string GenerateCrudOperations = "GenerateCrudOperations";
+
+ ///
+ /// 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()
+ {
+ var command = new ConfigCommand { Category = "RefreshEFRepository", Name = "EFRepositoryRefresh", CommandType = Type }
+ .UpdateExecutionProject
+ (
+ new ConfigProject
+ {
+ Name = ExecutionProject,
+ Guidance = "Enter the fully project name for the project that hosts the EF models."
+ }
+ .AddFolder
+ (
+ new ConfigFolder
+ {
+ Name = ExecutionModelFolder,
+ Required = false,
+ Guidance =
+ "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 = EFContextClassName,
+ Guidance = "Enter the class name of the database context used by entity framework."
+ }
+ )
+ .AddParameter
+ (
+ new ConfigParameter
+ {
+ 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 = 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
+ (
+ new ConfigProject
+ {
+ Name = EntityProject,
+ Guidance =
+ "Enter the full project name for the project that hosts the generated application POCO models that represent the EF Models."
+ }
+ .AddFolder
+ (
+ new ConfigFolder
+ {
+ Name = EntityFolder,
+ Required = false,
+ Guidance =
+ "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"
+ }
+ )
+ .AddParameter
+ (
+ new ConfigParameter
+ {
+ Name = AppModelValidatorPrefix,
+ Guidance = "Optional, prefix to assign to the application model entity validator when it is created."
+
+ }
+ )
+ .AddParameter
+ (
+ new ConfigParameter
+ {
+ Name = AppModelValidatorSuffix,
+ Guidance = "Optional, suffix to assign to the application model entity validator when it is created.",
+ Value = "Validator"
+ }
+ )
+ )
+ .AddProject
+ (
+ new ConfigProject
+ {
+ Name = RepoProject,
+ Guidance =
+ "Enter the full project name for the project that hosts the generated repositories that represent the EF Models."
+ }
+ .AddFolder
+ (
+ new ConfigFolder()
+ {
+ Name = RepoFolder,
+ Required = false,
+ Guidance =
+ "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
+ (
+ new ConfigProject
+ {
+ Name = RepoContractProject,
+ Guidance =
+ "Enter the full project name for the project that hosts the interface definition for the repository contract."
+ }
+ .AddFolder
+ (
+ new ConfigFolder
+ {
+ Name = RepoContractFolder,
+ Required = false,
+ Guidance =
+ "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 = GenerateCrudOperations,
+ Guidance = "True by default. Defines whether the initial contracts include methods for Create, Update, and Delete.",
+ Value = "true"
+ }
+ )
+ )
+ .AddProject
+ (
+ new ConfigProject
+ {
+ Name = IntegrationTestProject,
+ Guidance =
+ "Enter the full project name for the project that hosts the generated integration test for the repository."
+ }
+ .AddFolder
+ (
+ new ConfigFolder()
+ {
+ Name = IntegrationTestFolder,
+ Required = false,
+ Guidance =
+ "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;
+ }
+ #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
+ {
+ isEnabled = (await ConfigManager.LoadCommandByFolderAsync(Type, ExecutionModelFolder, result, FolderLoadType.TargetFolderOnly)) != null;
+
+ if (!isEnabled) isEnabled = (await ConfigManager.LoadCommandByProjectAsync(Type, result)) != null;
+ }
+ 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 command = (await ConfigManager.LoadCommandByFolderAsync(Type, ExecutionModelFolder, result)
+ ?? await ConfigManager.LoadCommandByProjectAsync(Type, result))
+ ?? throw new CodeFactoryException("Could not load the automation configuration cannot refresh the EF repository.");
+
+ VsProject efModelProject = await VisualStudioActions.GetProjectFromConfigAsync(command.ExecutionProject)
+ ?? throw new CodeFactoryException("Could load the EF hosting project, cannot refresh the EF repository.");
+
+ 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(
+ "Could not load the repository contract project, cannot refresh the EF repository.");
+
+
+ VsProjectFolder contractFolder = await VisualStudioActions.GetProjectFolderFromConfigAsync(command.Project(RepoContractProject), RepoFolder);
+
+ VsProject testProject =
+ await VisualStudioActions.GetProjectFromConfigAsync(command.Project(IntegrationTestProject));
+
+ VsProjectFolder testFolder =
+ await VisualStudioActions.GetProjectFolderFromConfigAsync(command.Project(IntegrationTestProject),
+ IntegrationTestFolder);
+
+ var testPrefix = testProject != null ? command.Project(IntegrationTestProject).ParameterValue(TestPrefix) : null;
+
+ var testSuffix = testProject != null ? command.Project(IntegrationTestProject).ParameterValue(TestSuffix) : null;
+
+ if (string.IsNullOrWhiteSpace(contextClassName))
+ throw new CodeFactoryException(
+ "The entity framework context class name was not provided, cannot refresh the EF repository.");
+
+ var contextClass = (await efModelProject.FindCSharpSourceByClassNameAsync(contextClassName))
+ ?.SourceCode?.Classes?.FirstOrDefault()
+ ?? throw new CodeFactoryException($"The entity framework context class '{contextClassName}' could not be loaded, cannot refresh the EF repository,");
+
+ await VisualStudioActions.RefreshDbContextAsync(contextClassName, efModelProject, efModelFolder);
+
+ bool supportsLogging = await repoProject.SupportsLogging();
+
+ bool supportsNDF = await repoProject.SupportsNDF();
+
+ var efModel = result.SourceCode?.Classes?.FirstOrDefault()
+ ?? throw new CodeFactoryException("The EF entity class could not be loaded, cannot refresh the EF repository.");
+
+ var nameManagement = NameManagement.Init(efEntityRemovePrefixs, efEntityRemoveSuffixes, appModelPrefix, appModelSuffix);
+
+ var appModel = (await VisualStudioActions.RefreshModelWithoutDependenciesAsync(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.");
+
+ string noReplacePrefix = null;
+ string noReplaceSuffix = null;
+
+ 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 repositoryName = NameManagement.Init(efEntityRemovePrefixs, efEntityRemoveSuffixes, repoPrefix, repoSuffix).FormatName(efModel.Name);
+
+ // Get boolean parameters to know what methods we are going to generate by default.
+ bool generateCrudOperations = bool.Parse(command.Project(RepoContractProject).ParameterValue(GenerateCrudOperations));
+
+ var repoClass = await VisualStudioActions.RefreshEFRepositoryWithCRUDAsync(repositoryName, efModel, repoProject, contractProject, appModel,
+ contextClass, supportsNDF, supportsLogging, repoFolder, contractFolder, generateCrudOperations);
+
+ 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);
+
+
+ if (await testProject.TestProjectIsConfiguredMSTestAsync())
+ {
+ var RefreshMSTestCommand = new RefreshMSTestCommand(null, null);
+ await VisualStudioActions.RefreshMSTestIntegrationTestAsync(testName, contractInterface, testProject);
+ }
+
+ if (await testProject.TestProjectIsConfiguredXUnitAsync())
+ {
+ var RefreshXUnitTestCommand = new RefreshXUnitTestCommand(null, null);
+ await VisualStudioActions.RefreshIntegrationTestAsync(testName, contractInterface, testProject);
+
+ }
+ }
+ }
+
+ }
+ 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 creates the default namespaces to add to a new entity class that is crearted.
+ ///
+ /// List of using statements to use.
+ private List EntityModelNamespaces()
+ {
+ return new List
+ {
+ new ManualUsingStatementNamespace("System"),
+ new ManualUsingStatementNamespace("System.Collections.Generic"),
+ new ManualUsingStatementNamespace("System.Linq"),
+ new ManualUsingStatementNamespace("System.Text")
+ };
+ }
+ }
+
+}
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshFluentValidation.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshFluentValidationCommand.cs
similarity index 92%
rename from src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshFluentValidation.cs
rename to src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshFluentValidationCommand.cs
index a84831c..2215d9e 100644
--- a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshFluentValidation.cs
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshFluentValidationCommand.cs
@@ -1,25 +1,21 @@
-using CodeFactory.Automation.Standard.Logic.FluentValidation;
-using CodeFactory.Automation.Standard.Logic;
+using CodeFactory.Automation.Standard.Logic;
+using CodeFactory.Automation.Standard.Logic.FluentValidation;
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.AspNetCore.Service.Rest.CSharpFile
{
- ///
- /// Code factory command for automation of a C# document when selected from a project in solution explorer.
- ///
- public class RefreshFluentValidation : CSharpSourceCommandBase
+ ///
+ /// Code factory command for automation of a C# document when selected from a project in solution explorer.
+ ///
+ public class RefreshFluentValidationCommand : CSharpSourceCommandBase
{
private static readonly string commandTitle = "Refresh Validation";
private static readonly string commandDescription = "Refreshes the fluent validation class that supports the class implemented in this source file.";
@@ -27,7 +23,7 @@ public class RefreshFluentValidation : CSharpSourceCommandBase
#pragma warning disable CS1998
///
- public RefreshFluentValidation(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
+ public RefreshFluentValidationCommand(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
{
//Intentionally blank
}
@@ -37,7 +33,7 @@ public RefreshFluentValidation(ILogger logger, IVsActions vsActions) : base(logg
///
/// The fully qualified name of the command to be used with configuration.
///
- public static string Type = typeof(RefreshFluentValidation).FullName;
+ public static string Type = typeof(RefreshFluentValidationCommand).FullName;
///
/// The execution project that contains the definition of the model to refresh validation in.
@@ -67,7 +63,7 @@ public RefreshFluentValidation(ILogger logger, IVsActions vsActions) : base(logg
public override ConfigCommand LoadExternalConfigDefinition()
{
return new ConfigCommand
- { Category = "ModelValidation", Name = nameof(RefreshFluentValidation), CommandType = Type }
+ { Category = "ModelValidation", Name = nameof(RefreshFluentValidationCommand), CommandType = Type }
.UpdateExecutionProject
(
new ConfigProject
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshLogic.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshLogicCommand.cs
similarity index 96%
rename from src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshLogic.cs
rename to src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshLogicCommand.cs
index 347ac4f..6439ed8 100644
--- a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshLogic.cs
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshLogicCommand.cs
@@ -1,25 +1,23 @@
using CodeFactory.Automation.NDF.Logic.General;
using CodeFactory.Automation.Standard.Logic;
+using CodeFactory.Automation.Standard.Logic.Extensions;
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.AspNetCore.Service.Rest.CSharpFile
{
- ///
- /// Code factory command for automation of a C# document when selected from a project in solution explorer.
- ///
- public class RefreshLogic : CSharpSourceCommandBase
+ ///
+ /// Code factory command for automation of a C# document when selected from a project in solution explorer.
+ ///
+ public class RefreshLogicCommand : CSharpSourceCommandBase
{
private static readonly string commandTitle = "Refresh Logic";
private static readonly string commandDescription = "Refreshes the logic implementation for the target logic interface.";
@@ -27,7 +25,7 @@ public class RefreshLogic : CSharpSourceCommandBase
#pragma warning disable CS1998
///
- public RefreshLogic(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
+ public RefreshLogicCommand(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
{
//Intentionally blank
}
@@ -37,7 +35,7 @@ public RefreshLogic(ILogger logger, IVsActions vsActions) : base(logger, vsActio
///
/// The fully qualified name of the command to be used with configuration.
///
- public static string Type = typeof(RefreshLogic).FullName;
+ public static string Type = typeof(RefreshLogicCommand).FullName;
///
/// Exection project for the command.
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshTest.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshMSTestCommand.cs
similarity index 89%
rename from src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshTest.cs
rename to src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshMSTestCommand.cs
index 81513b7..edb7b14 100644
--- a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshTest.cs
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshMSTestCommand.cs
@@ -1,33 +1,31 @@
using CodeFactory.Automation.NDF.Logic.Testing.MSTest;
using CodeFactory.Automation.Standard.Logic;
+using CodeFactory.Automation.Standard.Logic.Extensions;
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.AspNetCore.Service.Rest.CSharpFile
{
- ///
- /// Code factory command for automation of a C# document when selected from a project in solution explorer.
- ///
- public class RefreshTest : CSharpSourceCommandBase
+ ///
+ /// Code factory command for automation of a C# document when selected from a project in solution explorer.
+ ///
+ public class RefreshMSTestCommand : CSharpSourceCommandBase
{
- private static readonly string commandTitle = "Refresh Test";
- private static readonly string commandDescription = "Refreshes an integration test from the target interface.";
+ private static readonly string commandTitle = "Refresh MSTest Test";
+ private static readonly string commandDescription = "Refreshes an MSTest integration test from the target interface.";
#pragma warning disable CS1998
///
- public RefreshTest(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
+ public RefreshMSTestCommand(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
{
//Intentionally blank
}
@@ -37,7 +35,7 @@ public RefreshTest(ILogger logger, IVsActions vsActions) : base(logger, vsAction
///
/// The fully qualified name of the command to be used with configuration.
///
- public static string Type = typeof(RefreshTest).FullName;
+ public static string Type = typeof(RefreshMSTestCommand).FullName;
///
/// Project executing the command
@@ -59,7 +57,6 @@ public RefreshTest(ILogger logger, IVsActions vsActions) : base(logger, vsAction
///
public static string TestSuffix = "TestSuffix";
-
///
/// Loads the external configuration definition for this command.
///
@@ -69,7 +66,7 @@ public override ConfigCommand LoadExternalConfigDefinition()
var config = new ConfigCommand
{
CommandType = Type, Category="Testing",
- Name=nameof(RefreshTest),
+ Name=nameof(RefreshMSTestCommand),
Guidance="Automation command that generates integration tests from a provided interface."
}
.UpdateExecutionProject
@@ -96,7 +93,6 @@ public override ConfigCommand LoadExternalConfigDefinition()
Name = TestSuffix,
Guidance = "Optional, Suffix to append to the name of the integration test when being created.",
Value = "Test"
-
}
);
@@ -132,7 +128,7 @@ public override async Task EnableCommandAsync(VsCSharpSource result)
isEnabled = testProject != null;
- if (isEnabled) isEnabled = await testProject.TestProjectIsConfiguredAsync();
+ if (isEnabled) isEnabled = await testProject.TestProjectIsConfiguredMSTestAsync();
}
}
}
@@ -158,9 +154,7 @@ public override async Task ExecuteCommandAsync(VsCSharpSource result)
?? throw new CodeFactoryException("Could not load the commands configuration.");
var testProject = (await VisualStudioActions.GetProjectFromConfigAsync(config.Project(TestProject)))
- ?? throw new CodeFactoryException("Could not locate the test project cannot refresh the test.");
-
-
+ ?? 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.");
@@ -172,10 +166,7 @@ public override async Task ExecuteCommandAsync(VsCSharpSource result)
var testName = NameManagement.Init(noRemove,noRemove,testPrefix,testSuffix).FormatName(targetInterface.Name.GenerateCSharpFormattedClassName());
-
var test = VisualStudioActions.RefreshMSTestIntegrationTestAsync(testName,targetInterface, testProject);
-
-
}
catch (CodeFactoryException codeFactoryError)
{
@@ -185,12 +176,8 @@ public override async Task ExecuteCommandAsync(VsCSharpSource result)
{
_logger.Error($"The following unhandled error occurred while executing the solution explorer C# document command {commandTitle}. ",
unhandledError);
-
}
-
}
-
#endregion
}
-
}
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshRestService.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshRestServiceCommand.cs
similarity index 95%
rename from src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshRestService.cs
rename to src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshRestServiceCommand.cs
index 4bd6c02..d3dd285 100644
--- a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshRestService.cs
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshRestServiceCommand.cs
@@ -1,25 +1,23 @@
using CodeFactory.Automation.NDF.Logic.AspNetCore.Service.Rest.Json;
using CodeFactory.Automation.Standard.Logic;
+using CodeFactory.Automation.Standard.Logic.Extensions;
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.AspNetCore.Service.Rest.CSharpFile
{
- ///
- /// Code factory command for automation of a C# document when selected from a project in solution explorer.
- ///
- public class RefreshRestService : CSharpSourceCommandBase
+ ///
+ /// Code factory command for automation of a C# document when selected from a project in solution explorer.
+ ///
+ public class RefreshRestServiceCommand : CSharpSourceCommandBase
{
private static readonly string commandTitle = "Refresh Rest Service";
private static readonly string commandDescription = "Refreshes the implementation of a rest service from a contract definition.";
@@ -27,7 +25,7 @@ public class RefreshRestService : CSharpSourceCommandBase
#pragma warning disable CS1998
///
- public RefreshRestService(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
+ public RefreshRestServiceCommand(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
{
//Intentionally blank
}
@@ -37,7 +35,7 @@ public RefreshRestService(ILogger logger, IVsActions vsActions) : base(logger, v
///
/// The name of the command the configuration is tied to.
///
- public static string Type = typeof(RefreshRestService).FullName;
+ public static string Type = typeof(RefreshRestServiceCommand).FullName;
///
/// The execution project that contains the definition of the logic contract to implement as a service.
@@ -101,7 +99,7 @@ public RefreshRestService(ILogger logger, IVsActions vsActions) : base(logger, v
public override ConfigCommand LoadExternalConfigDefinition()
{
var command = new ConfigCommand
- { Category = "JsonRestService", Name = nameof(RefreshRestService), CommandType = Type }
+ { Category = "JsonRestService", Name = nameof(RefreshRestServiceCommand), CommandType = Type }
.UpdateExecutionProject
(
new ConfigProject
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshXUnitTestCommand .cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshXUnitTestCommand .cs
new file mode 100644
index 0000000..97cd38a
--- /dev/null
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/RefreshXUnitTestCommand .cs
@@ -0,0 +1,179 @@
+using CodeFactory.Automation.NDF.Logic.Testing.XUnit;
+using CodeFactory.Automation.Standard.Logic;
+using CodeFactory.Automation.Standard.Logic.Extensions;
+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.ProjectSystem;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace CodeFactory.Architecture.AspNetCore.Service.Rest.CSharpFile
+{
+ ///
+ /// Code factory command for automation of a C# document when selected from a project in solution explorer.
+ ///
+ public class RefreshXUnitTestCommand : CSharpSourceCommandBase
+ {
+ private static readonly string commandTitle = "Refresh xUnit Test";
+ private static readonly string commandDescription = "Refreshes an integration test from the target interface.";
+#pragma warning disable CS1998
+
+ ///
+ public RefreshXUnitTestCommand(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(RefreshXUnitTestCommand).FullName;
+
+ ///
+ /// Project executing the command
+ ///
+ public static string ExecutionProject = "ExecutionProject";
+
+ ///
+ /// Project that hosts the intergration tests
+ ///
+ 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.
+ ///
+ /// Will return the command configuration or null if this command does not support external configurations.
+ public override ConfigCommand LoadExternalConfigDefinition()
+ {
+ var config = new ConfigCommand
+ {
+ CommandType = Type,
+ Category = "Testing",
+ Name = nameof(RefreshXUnitTestCommand),
+ 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 xUnit 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;
+ }
+ #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
+ {
+ isEnabled = result.SourceCode?.Interfaces?.FirstOrDefault() != null;
+
+ if (isEnabled)
+ {
+ var config = await ConfigManager.LoadCommandByProjectAsync(Type, result);
+
+ isEnabled = config != null;
+
+ if (isEnabled)
+ {
+ var testProject = await VisualStudioActions.GetProjectFromConfigAsync(config.Project(TestProject));
+
+ isEnabled = testProject != null;
+
+ if (isEnabled) isEnabled = await testProject.TestProjectIsConfiguredXUnitAsync();
+ }
+ }
+ }
+ 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 config = (await ConfigManager.LoadCommandByProjectAsync(Type, result))
+ ?? throw new CodeFactoryException("Could not load the commands configuration.");
+
+ 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 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.RefreshIntegrationTestAsync(testName, targetInterface, testProject);
+ }
+ catch (Exception unhandledError)
+ {
+ _logger.Error($"The following unhandled error occurred while executing the solution explorer C# document command {commandTitle}. ",
+ unhandledError);
+ }
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/UpdateLogicImplementation.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/UpdateLogicImplementationCommand.cs
similarity index 96%
rename from src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/UpdateLogicImplementation.cs
rename to src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/UpdateLogicImplementationCommand.cs
index e52b270..d7744e8 100644
--- a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/UpdateLogicImplementation.cs
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CSharpFile/UpdateLogicImplementationCommand.cs
@@ -1,25 +1,23 @@
using CodeFactory.Automation.NDF.Logic.General;
using CodeFactory.Automation.Standard.Logic;
+using CodeFactory.Automation.Standard.Logic.Extensions;
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.AspNetCore.Service.Rest.CSharpFile
{
- ///
- /// Code factory command for automation of a C# document when selected from a project in solution explorer.
- ///
- public class UpdateLogicImplementation : CSharpSourceCommandBase
+ ///
+ /// Code factory command for automation of a C# document when selected from a project in solution explorer.
+ ///
+ public class UpdateLogicImplementationCommand : CSharpSourceCommandBase
{
private static readonly string commandTitle = "Update Logic Implementation";
private static readonly string commandDescription = "Clones changes from the respository contract to the logic contract and refreshes the logic implementation.";
@@ -27,7 +25,7 @@ public class UpdateLogicImplementation : CSharpSourceCommandBase
#pragma warning disable CS1998
///
- public UpdateLogicImplementation(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
+ public UpdateLogicImplementationCommand(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
{
//Intentionally blank
}
@@ -37,7 +35,7 @@ public UpdateLogicImplementation(ILogger logger, IVsActions vsActions) : base(lo
///
/// The fully qualified name of the command to be used with configuration.
///
- public static string Type = typeof(UpdateLogicImplementation).FullName;
+ public static string Type = typeof(UpdateLogicImplementationCommand).FullName;
///
/// Exection project for the command.
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CodeFactory.Architecture.AspNetCore.Service.Rest.csproj b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CodeFactory.Architecture.AspNetCore.Service.Rest.csproj
index 1e4830a..01df8ab 100644
--- a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CodeFactory.Architecture.AspNetCore.Service.Rest.csproj
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/CodeFactory.Architecture.AspNetCore.Service.Rest.csproj
@@ -48,19 +48,21 @@
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/IDE/LoadExternalConfiguration.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/IDE/LoadExternalConfigurationCommand.cs
similarity index 61%
rename from src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/IDE/LoadExternalConfiguration.cs
rename to src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/IDE/LoadExternalConfigurationCommand.cs
index dc58566..6e34816 100644
--- a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/IDE/LoadExternalConfiguration.cs
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/IDE/LoadExternalConfigurationCommand.cs
@@ -1,30 +1,27 @@
using CodeFactory.Architecture.AspNetCore.Service.Rest.CSharpFile;
+using CodeFactory.Automation.NDF.Logic.Testing.MSTest;
+using CodeFactory.Automation.NDF.Logic.Testing.XUnit;
using CodeFactory.WinVs;
using CodeFactory.WinVs.Commands;
using CodeFactory.WinVs.Commands.IDE;
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;
namespace CodeFactory.Architecture.AspNetCore.Service.Rest.IDE
{
- ///
- /// Code factory command that is executed when the solution is loaded. This command only gets called one time on load of the solution.
- ///
- public class LoadExternalConfiguration : SolutionLoadCommandBase
+ ///
+ /// Code factory command that is executed when the solution is loaded. This command only gets called one time on load of the solution.
+ ///
+ public class LoadExternalConfigurationCommand : SolutionLoadCommandBase
{
private static readonly string commandTitle = "Load External Configuration";
private static readonly string commandDescription = "Loads the external configuration for automation.";
#pragma warning disable CS1998
///
- public LoadExternalConfiguration(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
+ public LoadExternalConfigurationCommand(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
{
//Intentionally blank
}
@@ -38,28 +35,42 @@ public override async Task ExecuteCommandAsync(VsSolution result)
try
{
- var refreshEFRepository = new RefreshEFRepository(null, null);
+ var refreshEFRepository = new RefreshEFRepositoryCommand(null, null);
refreshEFRepository.LoadExternalConfigDefinition().RegisterCommandWithDefaultConfiguration();
- var refreshRestService = new RefreshRestService(null, null);
+ var refreshRestService = new RefreshRestServiceCommand(null, null);
refreshRestService.LoadExternalConfigDefinition().RegisterCommandWithDefaultConfiguration();
- var refreshTest = new RefreshTest(null, null);
- refreshTest.LoadExternalConfigDefinition().RegisterCommandWithDefaultConfiguration();
+ // Load configuration based on what type of test project you have loaded
+ var projects = await result.GetProjectsAsync(false);
+ foreach (var project in projects)
+ {
+ if (await project.TestProjectIsConfiguredMSTestAsync())
+ {
+ var RefreshMSTestCommand = new RefreshMSTestCommand(null, null);
+ RefreshMSTestCommand.LoadExternalConfigDefinition().RegisterCommandWithDefaultConfiguration();
+ }
- var refreshFluentValidation = new RefreshFluentValidation(null, null);
+ if (await project.TestProjectIsConfiguredXUnitAsync())
+ {
+ var RefreshXUnitTestCommand = new RefreshXUnitTestCommand(null, null);
+ RefreshXUnitTestCommand.LoadExternalConfigDefinition().RegisterCommandWithDefaultConfiguration();
+ }
+ }
+
+ var refreshFluentValidation = new RefreshFluentValidationCommand(null, null);
refreshFluentValidation.LoadExternalConfigDefinition().RegisterCommandWithDefaultConfiguration();
- var addMissingRepositoryMembers = new AddMissingRepositoryMembers(null, null);
+ var addMissingRepositoryMembers = new AddMissingRepositoryMembersCommand(null, null);
addMissingRepositoryMembers.LoadExternalConfigDefinition().RegisterCommandWithDefaultConfiguration();
- var updateLogicImplementation = new UpdateLogicImplementation(null, null);
+ var updateLogicImplementation = new UpdateLogicImplementationCommand(null, null);
updateLogicImplementation.LoadExternalConfigDefinition().RegisterCommandWithDefaultConfiguration();
- var addMissingLogicMembers = new AddMissingLogicMembers(null, null);
+ var addMissingLogicMembers = new AddMissingLogicMembersCommand(null, null);
addMissingLogicMembers.LoadExternalConfigDefinition().RegisterCommandWithDefaultConfiguration();
- var refreshLogic = new RefreshLogic(null, null);
+ var refreshLogic = new RefreshLogicCommand(null, null);
refreshLogic.LoadExternalConfigDefinition().RegisterCommandWithDefaultConfiguration();
ConfigManager.LoadConfiguration(result, "Automation", VisualStudioActions);
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Project/RegisterTransientServices.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Project/RegisterTransientServicesCommand.cs
similarity index 87%
rename from src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Project/RegisterTransientServices.cs
rename to src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Project/RegisterTransientServicesCommand.cs
index e1d8dbf..6650ece 100644
--- a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Project/RegisterTransientServices.cs
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Project/RegisterTransientServicesCommand.cs
@@ -3,22 +3,17 @@
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.AspNetCore.Service.Rest.Project
{
-///
- /// Code factory command for automation of a project when selected from solution explorer.
- ///
- public class RegisterTransientServices : ProjectCommandBase
+ ///
+ /// Code factory command for automation of a project when selected from solution explorer.
+ ///
+ public class RegisterTransientServicesCommand : ProjectCommandBase
{
private static readonly string commandTitle = "Register Transient Services";
private static readonly string commandDescription = "Registers Transient classes with a NDF dependency injection loader for a target project.";
@@ -26,7 +21,7 @@ public class RegisterTransientServices : ProjectCommandBase
#pragma warning disable CS1998
///
- public RegisterTransientServices(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
+ public RegisterTransientServicesCommand(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
{
//Intentionally blank
}
@@ -37,7 +32,7 @@ public RegisterTransientServices(ILogger logger, IVsActions vsActions) : base(lo
///
/// The fully qualified name of the command to be used with configuration.
///
- public static string Type = typeof(RegisterTransientServices).FullName;
+ public static string Type = typeof(RegisterTransientServicesCommand).FullName;
///
/// Loads the external configuration definition for this command.
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/CountLinesOfCodeCommand.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/CountLinesOfCodeCommand.cs
new file mode 100644
index 0000000..d3f82e8
--- /dev/null
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/CountLinesOfCodeCommand.cs
@@ -0,0 +1,111 @@
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs;
+using CodeFactory.WinVs.Commands;
+using CodeFactory.WinVs.Commands.SolutionExplorer;
+using CodeFactory.WinVs.Logging;
+using CodeFactory.WinVs.Models.ProjectSystem;
+using Drives.Automation.Standards.Logic.Extensions;
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace Drives.Architecture.Commands.Solution
+{
+ ///
+ /// Code factory command for automation of a C# document when selected from a project in solution explorer.
+ ///
+ public class CountLinesOfCodeCommand : SolutionCommandBase
+ {
+ private static readonly string commandTitle = "Count Lines of Code";
+ private static readonly string commandDescription = "Counds the lines of code within in this Project.";
+ public IVsActions _vsActions { get; set; }
+#pragma warning disable CS1998
+
+ ///
+ public CountLinesOfCodeCommand(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
+ {
+ //Intentionally blank
+ _vsActions = vsActions;
+ }
+
+ #region Overrides of VsCommandBase
+
+ #region External Configuration
+
+ ///
+ /// The fully qualified name of the command to be used with configuration.
+ ///
+ public static string Type = typeof(CountLinesOfCodeCommand).FullName;
+
+ ///
+ /// 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()
+ {
+ var config = new ConfigCommand
+ {
+ CommandType = Type,
+ Category = "Code Analysis",
+ Name = nameof(CountLinesOfCodeCommand),
+ Guidance = "Automation command that calculates a total number of lines included in this Sollution."
+ };
+
+ return config;
+ }
+ #endregion
+
+ ///
+ /// 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(VsSolution result)
+ {
+ //Result that determines if the command is enabled and visible in the context menu for execution.
+ bool isEnabled = false;
+
+ try
+ {
+ var projects = await result.GetProjectsAsync(true);
+ foreach (var project in projects)
+ {
+ var children = await project.GetChildrenAsync(true);
+ if (children.Any(p => p.ModelType.Equals(VisualStudioModelType.Document) && (p.IsMarkupCode() || p.IsSourceCode())))
+ {
+ isEnabled = true;
+ break;
+ }
+ }
+ }
+ 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(VsSolution result)
+ {
+ try
+ {
+ // Prompt Dialog for user input.
+ MessageBox.Show($"There are {await result.CountLinesOfCodeAsync()} lines of code in the {result.Name} solution.");
+ }
+ catch (Exception unhandledError)
+ {
+ _logger.Error($"The following unhandled error occurred while executing the solution explorer C# document command {commandTitle}. ",
+ unhandledError);
+ }
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/CreateAutomationConfiguration.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/CreateAutomationConfigurationCommand.cs
similarity index 86%
rename from src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/CreateAutomationConfiguration.cs
rename to src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/CreateAutomationConfigurationCommand.cs
index bcb078d..608fdbf 100644
--- a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/CreateAutomationConfiguration.cs
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/CreateAutomationConfigurationCommand.cs
@@ -2,29 +2,24 @@
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.AspNetCore.Service.Rest.Solution
{
- ///
- /// Code factory command for automation of the solution when selected from solution explorer.
- ///
- public class CreateAutomationConfiguration : SolutionCommandBase
+ ///
+ /// Code factory command for automation of the solution when selected from solution explorer.
+ ///
+ public class CreateAutomationConfigurationCommand : SolutionCommandBase
{
private static readonly string commandTitle = "Create Automation Configuration";
private static readonly string commandDescription = "Creates an empty automation configuration.";
#pragma warning disable CS1998
///
- public CreateAutomationConfiguration(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
+ public CreateAutomationConfigurationCommand(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
{
//Intentionally blank
}
@@ -34,7 +29,7 @@ public CreateAutomationConfiguration(ILogger logger, IVsActions vsActions) : bas
///
/// The fully qualified name of the command to be used with configuration.
///
- public static string Type = typeof(CreateAutomationConfiguration).FullName;
+ public static string Type = typeof(CreateAutomationConfigurationCommand).FullName;
///
/// Loads the external configuration definition for this command.
diff --git a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/ReloadAutomationConfiguration.cs b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/ReloadAutomationConfigurationCommand.cs
similarity index 86%
rename from src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/ReloadAutomationConfiguration.cs
rename to src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/ReloadAutomationConfigurationCommand.cs
index c2d9d6d..0db356f 100644
--- a/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/ReloadAutomationConfiguration.cs
+++ b/src/Architecture/CodeFactory.Architecture.AspNetCore.Service.Rest/Solution/ReloadAutomationConfigurationCommand.cs
@@ -2,29 +2,24 @@
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.AspNetCore.Service.Rest.Solution
{
-///
- /// Code factory command for automation of the solution when selected from solution explorer.
- ///
- public class ReloadAutomationConfiguration : SolutionCommandBase
+ ///
+ /// Code factory command for automation of the solution when selected from solution explorer.
+ ///
+ public class ReloadAutomationConfigurationCommand : SolutionCommandBase
{
private static readonly string commandTitle = "Reload Automation Configuration";
private static readonly string commandDescription = "Reloads the automation configuration.";
#pragma warning disable CS1998
///
- public ReloadAutomationConfiguration(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
+ public ReloadAutomationConfigurationCommand(ILogger logger, IVsActions vsActions) : base(logger, vsActions, commandTitle, commandDescription)
{
//Intentionally blank
}
@@ -34,7 +29,7 @@ public ReloadAutomationConfiguration(ILogger logger, IVsActions vsActions) : bas
///
/// The fully qualified name of the command to be used with configuration.
///
- public static string Type = typeof(ReloadAutomationConfiguration).FullName;
+ public static string Type = typeof(ReloadAutomationConfigurationCommand).FullName;
///
/// Loads the external configuration definition for this command.
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/AspNetCore/Service/Rest/Json/MethodExtensions.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/AspNetCore/Service/Rest/Json/MethodExtensions.cs
index cca3a0d..9076ba8 100644
--- a/src/Automation/CodeFactory.Automation.NDF.Logic/AspNetCore/Service/Rest/Json/MethodExtensions.cs
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/AspNetCore/Service/Rest/Json/MethodExtensions.cs
@@ -1,4 +1,6 @@
-using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.WinVs.Models.CSharp.Builder;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -74,5 +76,324 @@ public static bool IsPostCall(this CsMethod source)
return source.HasParameters;
}
- }
+
+
+ //
+ // Summary:
+ // Generates a string representation of the await statement.
+ //
+ // Parameters:
+ // source:
+ // CsMethod to generate parameters for.
+ //
+ // Returns:
+ // True if has a return type.
+ //
+ // Exceptions:
+ // T:System.ArgumentNullException:
+ // Instance of the method was not provided.
+ public static SourceFormatter GenerateNewTestObjectsFromParameters(this CsMethod source, SourceFormatter sourceFormatter)
+ {
+ StringBuilder parameterBuilder = new StringBuilder();
+
+ if (source == null)
+ {
+ throw new ArgumentNullException("source");
+ }
+
+ // Initialize parameter variables
+ if (source.HasParameters)
+ {
+ bool firstParameter = true;
+
+ foreach (var testParameter in source.Parameters)
+ {
+ if (firstParameter)
+ {
+ parameterBuilder.Append($"{testParameter.Name}");
+ firstParameter = false;
+ }
+ else parameterBuilder.Append($", {testParameter.Name}");
+
+ string defaultValue = testParameter.ParameterType.GenerateCSharpDefaultTestValue(testParameter.Name);
+ sourceFormatter.AppendCodeLine(3,
+ defaultValue != null
+ ? $"{testParameter.ParameterType.GenerateCSharpTypeName()} {testParameter.Name} = {defaultValue};"
+ : $"{testParameter.ParameterType.GenerateCSharpTypeName()} {testParameter.Name};");
+
+ // Create default properties.
+ if (!testParameter.ParameterType.IsWellKnownType)
+ {
+ foreach (var property in testParameter.ParameterType.GetClassModel().Properties)
+ {
+ // Ignore the first property, as this is the primary key.
+ if (property.Name != testParameter.ParameterType.GetClassModel().Properties[0].Name)
+ sourceFormatter.AppendCodeLine(3, $"{testParameter.Name}.{property.Name} = {property.PropertyType.GenerateCSharpDefaultTestValue(property.Name)};");
+ }
+ }
+
+ sourceFormatter.AppendCodeLine(0);
+ }
+ }
+
+ return sourceFormatter;
+ }
+
+ ///
+ /// Generates the full method signature for a REST Controller.
+ ///
+ /// Method to generate type name from.
+ /// Fully formatted list of attribute parameters.
+ public static string GenerateRESTControllerMethodSignature(this CsMethod source, SourceClassManager serviceManager, bool isOverload)
+ {
+ var returnSyntax = (string)null;
+ if (source == null || !source.IsLoaded) return returnSyntax;
+
+ string serviceCallName = source.GetRestName(isOverload);
+ string serviceMethodName = $"{serviceCallName}Async";
+ CsType returnType = source.ReturnType.TaskReturnType();
+
+ bool returnsData = returnType != null;
+
+ var returnTypeSyntax = returnType == null ? "NoDataResult" : $"ServiceResult<{returnType.GenerateCSharpTypeName(serviceManager.NamespaceManager, serviceManager.MappedNamespaces)}>";
+
+ returnSyntax = $"public async Task> {serviceMethodName}({source.BuildServiceMethodParameters(serviceManager)})";
+
+ return returnSyntax;
+ }
+
+ ///
+ /// Generates
+ ///
+ /// Method to generate type name from.
+ /// Fully formatted list of attribute parameters.
+ public static SourceFormatter GenerateHttpClientCaller(this CsMethod source, SourceFormatter contentFormatter, SourceClassManager classManager, string serviceName, string returnTypeSyntax)
+ {
+ var returnSyntax = (string)null;
+ if (source == null || !source.IsLoaded) return contentFormatter;
+
+ CsType returnType = source.ReturnType.TaskReturnType();
+ var serviceUrlParameter = $"_serviceUrl";
+ var collectionPath = serviceName;
+ if (!string.IsNullOrEmpty(collectionPath))
+ collectionPath = $"{collectionPath.ToLower()}/{collectionPath.ToLower().Pluralize()}/";
+
+ var url = $"$\"{{{serviceUrlParameter}.Url}}/api/v1/{collectionPath}\"";
+
+ if (source.Name != "GetAsync")
+ {
+ // build api caller with query parameters.
+ if (source.HasParameters && source.Parameters.All(p => p.ParameterType.IsWellKnownType))
+ {
+ var queryParameters = new StringBuilder();
+ bool isFirst = true;
+
+ foreach (var parameter in source.Parameters)
+ {
+ if (isFirst)
+ {
+ queryParameters.Append($"?{parameter.Name.ToSnakeCase()}={{{parameter.Name}}}");
+ isFirst = false;
+ }
+ else
+ {
+ queryParameters.Append($"&{parameter.Name.ToSnakeCase()}={{{parameter.Name}}}");
+ }
+ }
+
+ url = $"$\"{{{serviceUrlParameter}.Url}}/api/v1/{collectionPath}{queryParameters}\"";
+ contentFormatter.AppendCodeLine(5, $"var request = new HttpRequestMessage({source.GenerateHttpRequestMethodType()}, {url});");
+ contentFormatter.AppendCodeLine(5, $"request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(\"application/json\"));");
+ }
+ // Build api caller with serialized body content.
+ else
+ {
+ var requestName = source.GetRestServiceRequestModelName(serviceName);
+
+ contentFormatter.AppendCodeLine(5, $"var request = new HttpRequestMessage({source.GenerateHttpRequestMethodType()}, {url});");
+ contentFormatter.AppendCodeLine(5, $"request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(\"application/json\"));");
+
+ if (source.HasParameters)
+ {
+ contentFormatter.AppendCodeLine(5, $"request.Content = new StringContent(JsonSerializer.Serialize({source.Parameters[0].Name}), Encoding.UTF8);");
+ contentFormatter.AppendCodeLine(5, $"request.Content.Headers.ContentType = new MediaTypeHeaderValue(\"application/json\");");
+ }
+ }
+ }
+ else
+ {
+ url = $"$\"{{{serviceUrlParameter}.Url}}/api/v1/{collectionPath}{{{source.Parameters[0].Name}}}\"";
+ contentFormatter.AppendCodeLine(5, $"var request = new HttpRequestMessage({source.GenerateHttpRequestMethodType()}, {url});");
+ contentFormatter.AppendCodeLine(5, $"request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(\"application/json\"));");
+ }
+
+ contentFormatter.AppendCodeLine(5, $"var serviceData = await httpClient.SendAsync(request);");
+ contentFormatter.AppendCodeLine(5);
+
+ contentFormatter.AppendCodeLine(5, "await RaiseUnhandledExceptionsAsync(serviceData);");
+ contentFormatter.AppendCodeLine(5);
+ contentFormatter.AppendCodeLine(5, $"var serviceResult = await serviceData.Content.ReadFromJsonAsync<{returnTypeSyntax}>();");
+ contentFormatter.AppendCodeLine(5);
+ return contentFormatter;
+ }
+
+ ///
+ /// Generates the full method signature for a BFF Controller.
+ ///
+ /// Method to generate type name from.
+ /// Fully formatted list of attribute parameters.
+ public static string GenerateBFFControllerMethodSignature(this CsMethod source, SourceClassManager serviceManager, bool isOverload)
+ {
+ var returnSyntax = (string)null;
+ if (source == null || !source.IsLoaded) return returnSyntax;
+
+ string serviceCallName = source.GetRestName(isOverload);
+ string serviceMethodName = $"{serviceCallName}Async";
+ CsType returnType = source.ReturnType.TaskReturnType();
+
+ if (returnType != null)
+ returnSyntax = $"public async Task<{returnType.GenerateCSharpTypeName(serviceManager.NamespaceManager, serviceManager.MappedNamespaces)}> {serviceMethodName}({source.BuildServiceMethodParameters(serviceManager)})";
+ else
+ returnSyntax = $"public async Task {serviceMethodName}({source.BuildServiceMethodParameters(serviceManager)})";
+ return returnSyntax;
+ }
+
+ ///
+ /// Builds a service attribute based on method properties.
+ ///
+ /// Method to generate type name from.
+ /// Fully formatted list of attributes.
+ public static string GenerateHttpAttribute(this CsMethod source, SourceClassManager serviceManager)
+ {
+ var returnSyntax = (string)null;
+ if (source == null || !source.IsLoaded) return returnSyntax;
+
+ if (source.HasParameters && source.Parameters.Count == 1 && source.Parameters[0].ParameterType.IsWellKnownType)
+ {
+ if (source.Parameters[0].ParameterType.WellKnownType == CsKnownLanguageType.String)
+ returnSyntax = "[" + source.GenerateHttpAttributeType() + "(\"{" + source.Parameters[0].Name.ToSnakeCase() + "}\")]";
+ else
+ returnSyntax = "[" + source.GenerateHttpAttributeType() + "(\"{" + source.Parameters[0].Name.ToSnakeCase() + ":" + source.Parameters[0].ParameterType.GenerateCSharpTypeName(serviceManager.NamespaceManager, serviceManager.MappedNamespaces) + "}\")]";
+ }
+ else
+ {
+ // If there are more than 1 parameter, exclude them from here since they will need to be added as method parameters.
+ returnSyntax = $"[{source.GenerateHttpAttributeType()}]";
+ }
+
+ return returnSyntax;
+ }
+
+ ///
+ /// Builds a string list of attribute parameters.
+ ///
+ /// Method to generate type name from.
+ /// Fully formatted list of attribute parameters.
+ private static string BuildServiceMethodParameters(this CsMethod source, SourceClassManager serviceManager)
+ {
+ var returnSyntax = (string)null;
+ if (source == null || !source.IsLoaded) return returnSyntax;
+
+ var parameterAttribute = "";
+ if (source.Name.ToLower() == "getasync")
+ parameterAttribute = "";
+ else if (source.Parameters.All(p => p.ParameterType.IsWellKnownType))
+ parameterAttribute = "[FromQuery] ";
+ else
+ parameterAttribute = "[FromBody] ";
+
+ if (source.HasParameters)
+ {
+ int count = 0;
+ foreach (var parameter in source.Parameters)
+ {
+ if (count > 0)
+ returnSyntax += ", ";
+
+ returnSyntax += ($"{parameterAttribute}{parameter.ParameterType.GenerateCSharpTypeName(serviceManager.NamespaceManager, serviceManager.MappedNamespaces)} {parameter.Name.ToSnakeCase()}");
+ count++;
+ }
+ }
+
+ return returnSyntax;
+ }
+
+ ///
+ /// Returns an Http Attribute Type.
+ ///
+ /// Method to generate type name from.
+ /// An Http Attribute Type.
+ private static string GenerateHttpAttributeType(this CsMethod source)
+ {
+ var returnSyntax = (string)null;
+ if (source == null || !source.IsLoaded) return returnSyntax;
+
+ if (source.Name == "GetAsync" || source.Name == "QueryAsync")
+ returnSyntax = "HttpGet";
+ else if (source.Name == "AddAsync")
+ returnSyntax = "HttpPost";
+ else if (source.Name == "UpdateAsync")
+ returnSyntax = "HttpPut";
+ else if (source.Name == "DeleteAsync")
+ returnSyntax = "HttpDelete";
+ else if (source.IsPostCall())
+ returnSyntax = "HttpPost";
+ else
+ returnSyntax = "HttpGet";
+
+ return returnSyntax;
+ }
+
+ ///
+ /// Returns an Http Attribute Type.
+ ///
+ /// Method to generate type name from.
+ /// An Http Attribute Type.
+ private static string GenerateHttpMethodType(this CsMethod source)
+ {
+ var returnSyntax = (string)null;
+ if (source == null || !source.IsLoaded) return returnSyntax;
+
+ if (source.Name == "GetAsync" || source.Name == "QueryAsync")
+ returnSyntax = "GetFromJsonAsync";
+ else if (source.Name == "AddAsync")
+ returnSyntax = "PostAsJsonAsync";
+ else if (source.Name == "UpdateAsync")
+ returnSyntax = "PutAsJsonAsync";
+ else if (source.Name == "DeleteAsync")
+ returnSyntax = "DeleteAsync";
+ else if (source.IsPostCall())
+ returnSyntax = "PostAsJsonAsync";
+ else
+ returnSyntax = "GetFromJsonAsync";
+
+ return returnSyntax;
+ }
+
+ ///
+ /// Returns an Http Attribute Type.
+ ///
+ /// Method to generate type name from.
+ /// An Http Attribute Type.
+ private static string GenerateHttpRequestMethodType(this CsMethod source)
+ {
+ var returnSyntax = (string)null;
+ if (source == null || !source.IsLoaded) return returnSyntax;
+
+ if (source.Name == "GetAsync" || source.Name == "QueryAsync")
+ returnSyntax = "HttpMethod.Get";
+ else if (source.Name == "AddAsync")
+ returnSyntax = "HttpMethod.Post";
+ else if (source.Name == "UpdateAsync")
+ returnSyntax = "HttpMethod.Put";
+ else if (source.Name == "DeleteAsync")
+ returnSyntax = "HttpMethod.Delete";
+ else if (source.IsPostCall())
+ returnSyntax = "HttpMethod.Post";
+ else
+ returnSyntax = "HttpMethod.Get";
+
+ return returnSyntax;
+ }
+ }
}
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 33d967e..c0edc9a 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
@@ -1,20 +1,19 @@
using CodeFactory.Automation.Standard.Logic;
-using CodeFactory.WinVs.Models.CSharp.Builder;
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs;
using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.WinVs.Models.CSharp.Builder;
using CodeFactory.WinVs.Models.ProjectSystem;
-using CodeFactory.WinVs;
-using System;
-using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CodeFactory.Automation.NDF.Logic.AspNetCore.Service.Rest.Json
{
-///
- /// Automation class that generates C# abstraction contracts.
- ///
- public static class RestJsonCSharpAbstractionBuilder
+ ///
+ /// Automation class that generates C# abstraction contracts.
+ ///
+ public static class RestJsonCSharpAbstractionBuilder
{
///
/// Refreshes the interface definition of an service abstraction client.
@@ -96,8 +95,8 @@ private static async Task CreateCSharpAbstractionContractAsync(this IV
contractFormatter.AppendCodeLine(1, "}");
contractFormatter.AppendCodeLine(0, "}");
- var doc = contractFolder != null ? await contractFolder.AddDocumentAsync($"{contractName}.cs", contractFormatter.ReturnSource())
- : await contractProject.AddDocumentAsync($"{contractName}.cs", contractFormatter.ReturnSource());
+ var doc = contractFolder != null ? await contractFolder.AddDocumentAsync($"{contractName}.cs", contractFormatter.ReturnSource().TrimStartEndLines())
+ : await contractProject.AddDocumentAsync($"{contractName}.cs", contractFormatter.ReturnSource().TrimStartEndLines());
return doc == null
? throw new CodeFactoryException($"Failed to create the abstraction contract '{contractName}'.")
@@ -481,7 +480,7 @@ private static async Task UpdateAbstractionClassAsync(this IVsActions s
{
contentFormatter.AppendCodeLine(3,
logicReturnType.IsClass
- ? $"{logicReturnType.GenerateCSharpTypeName(abstractManager.NamespaceManager,abstractManager.MappedNamespaces)} result = null; "
+ ? $"{logicReturnType.GenerateCSharpTypeName(abstractManager.NamespaceManager,abstractManager.MappedNamespaces)}? result = null; "
: $"{logicReturnType.GenerateCSharpTypeName(abstractManager.NamespaceManager, abstractManager.MappedNamespaces)} result;");
contentFormatter.AppendCodeLine(3);
}
@@ -575,12 +574,12 @@ private static async Task UpdateAbstractionClassAsync(this IVsActions s
contentFormatter.AppendCodeLine(3, "_logger.InformationExitLog();");
contentFormatter.AppendCodeLine(3);
- if (returnsData) contentFormatter.AppendCodeLine(3, "return result;");
+ if (returnsData) contentFormatter.AppendCodeLine(3, "return result!;");
contentFormatter.AppendCodeLine(2, "}");
contentFormatter.AppendCodeLine(2);
- await abstractManager.ConstructorsAddAfterAsync(contentFormatter.ReturnSource());
+ await abstractManager.ConstructorsAddAfterAsync(contentFormatter.ReturnSource().TrimEndLines());
contentFormatter.ResetFormatter();
}
@@ -711,7 +710,7 @@ private static async Task AddRestAbstractionBaseClassAsync(this IVsActions sourc
sourceFormatter.AppendCodeLine(1, "}");
sourceFormatter.AppendCodeLine(0, "}");
- await abstractionProject.AddDocumentAsync("RestAbstraction.cs", sourceFormatter.ReturnSource());
+ await abstractionProject.AddDocumentAsync("RestAbstraction.cs", sourceFormatter.ReturnSource().TrimStartEndLines());
}
private static async Task AddServiceUrlBaseClassAsync(this IVsActions source, VsProject abstractionProject)
@@ -810,8 +809,8 @@ private static async Task AddServiceUrlClassAsync(this IVsActions source, VsProj
sourceFormatter.AppendCodeLine(1, "}");
sourceFormatter.AppendCodeLine(0, "}");
- if (abstractionFolder != null) await abstractionFolder.AddDocumentAsync($"{className}.cs", sourceFormatter.ReturnSource());
- else await abstractionProject.AddDocumentAsync($"{className}.cs", sourceFormatter.ReturnSource());
+ if (abstractionFolder != null) await abstractionFolder.AddDocumentAsync($"{className}.cs", sourceFormatter.ReturnSource().TrimEndLines());
+ else await abstractionProject.AddDocumentAsync($"{className}.cs", sourceFormatter.ReturnSource().TrimEndLines());
}
}
}
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 fae61a8..f803d20 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
@@ -1,20 +1,18 @@
-using CodeFactory.WinVs.Models.CSharp.Builder;
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs;
using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.WinVs.Models.CSharp.Builder;
using CodeFactory.WinVs.Models.ProjectSystem;
-using CodeFactory.WinVs;
-using System;
-using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using CodeFactory.Automation.Standard.Logic;
namespace CodeFactory.Automation.NDF.Logic.AspNetCore.Service.Rest.Json
{
- ///
- /// Automation logic for creating and updated web api rest based services.
- ///
- public static class RestJsonServiceBUilder
+ ///
+ /// Automation logic for creating and updated web api rest based services.
+ ///
+ public static class RestJsonServiceBUilder
{
///
/// Refreshes a rest json service implementation.
@@ -316,7 +314,7 @@ private static async Task UpdateJsonRestServiceAsync(this IVsActions so
{
logicFormatter.AppendCodeLine(3, logicReturnType.IsValueType
? $"{logicReturnType.GenerateCSharpTypeName(serviceManager.NamespaceManager, serviceManager.MappedNamespaces)} result;"
- : $"{logicReturnType.GenerateCSharpTypeName(serviceManager.NamespaceManager, serviceManager.MappedNamespaces)} result = null;");
+ : $"{logicReturnType.GenerateCSharpTypeName(serviceManager.NamespaceManager, serviceManager.MappedNamespaces)}? result = null;");
}
logicFormatter.AppendCodeLine(3, "try");
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/CodeFactory.Automation.NDF.Logic.csproj b/src/Automation/CodeFactory.Automation.NDF.Logic/CodeFactory.Automation.NDF.Logic.csproj
index baf0570..2f669ac 100644
--- a/src/Automation/CodeFactory.Automation.NDF.Logic/CodeFactory.Automation.NDF.Logic.csproj
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/CodeFactory.Automation.NDF.Logic.csproj
@@ -73,9 +73,15 @@
+
+
+
+
+
+
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/Data/Sql/EF/RepositoryBuilder.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/Data/Sql/EF/RepositoryBuilder.cs
index dad5432..7f899cb 100644
--- a/src/Automation/CodeFactory.Automation.NDF.Logic/Data/Sql/EF/RepositoryBuilder.cs
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/Data/Sql/EF/RepositoryBuilder.cs
@@ -1,464 +1,468 @@
-using CodeFactory.WinVs.Models.CSharp.Builder;
+using CodeFactory;
+using CodeFactory.Automation.NDF.Logic;
+using CodeFactory.Automation.NDF.Logic.Data.Sql;
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs;
using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.WinVs.Models.CSharp.Builder;
using CodeFactory.WinVs.Models.ProjectSystem;
-using CodeFactory.WinVs;
using Microsoft.Extensions.Logging;
-using System;
using System.Collections.Generic;
using System.Linq;
-using System.Text;
using System.Threading.Tasks;
-using CodeFactory.Automation.Standard.Logic;
-namespace CodeFactory.Automation.NDF.Logic.Data.Sql.EF
+namespace Drives.Automation.NDF.Logic.Data.Sql.EF
{
-///
- /// Automation logic to create or update a data repository that supports a target entity hosted in Entity Framework.
- ///
- public static class RepositoryBuilder
- {
- ///
- /// Creates or updates a repository that uses entity framework for management of data.
- ///
- /// CodeFactory automation for Visual Studio for Windows.
- /// The name of the repository to refresh.
- /// Entity framework entity.
- /// POCO model that supports the repository.
- /// Project the contract will be added to.
- /// Optional, project folder contracts will be stored in, default is null.
- /// Optional, additional namespaces to add to the contract definition, default is null.
- /// Optional, the name of the logger field if logging is supported, default value is '_logger'
- /// Optional, the target logging level for logging messages, default value is Information.
- /// Repository project.
- /// EF context class for accessing entity framework.
- /// Optional, flag that determines if the NDF libraries are used, default true.
- /// Optional, flag that determines if logging is supported, default true.
- /// Optional, project folder the repositories are stored in, default is null.
- /// Optional, list of additional namespaces to update the repository with.
- /// Created or updated repository.
- /// Raised if required data to create or update the repository is missing.
- public static async Task RefreshEFRepositoryAsync(this IVsActions source, string repositoryName, CsClass efEntity, VsProject repoProject,
- VsProject contractProject, CsClass poco, CsClass contextClass, bool useNDF = true,bool supportLogging = true, VsProjectFolder repoFolder = null, VsProjectFolder contractFolder = null,
- List additionRepositoryNamespaces = null,
- List additionalContractNamespaces = null,string loggerFieldName = "_logger", LogLevel logLevel = LogLevel.Information)
- {
-
- if (source == null)
- throw new CodeFactoryException("CodeFactory automation was not provided, cannot refresh the EF repository.");
-
- if(string.IsNullOrEmpty(repositoryName))
- throw new CodeFactoryException("The repository name was not provided, cannot create the repository.");
-
- if (efEntity == null)
- throw new CodeFactoryException("The entity framework model was not provided, cannot refresh the EF repository.");
-
- if (repoProject == null) throw new CodeFactoryException("The repository project was not provided, cannot refresh the EF repository.");
-
- if (contractProject == null)
- throw new CodeFactoryException("The contract project for the repository was not provided, cannot refresh the EF repository.");
-
- if (poco == null)
- throw new CodeFactoryException("The POCO model was not provided, cannot refresh the EF repository.");
-
- if (contextClass == null)
- throw new CodeFactoryException("The entity framework data context class was not provided, cannot refresh the EF repository.");
-
- 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)
- contractInterface = (await source.CreateRepositoryContractAsync(contractName, efEntity, contractProject, poco,
- contractFolder, additionalContractNamespaces)) ?? throw new CodeFactoryException("Could not create a repos");
-
- var repoName = repositoryName;
-
- CsSource repoSource = repoFolder != null
- ? (await repoFolder.FindCSharpSourceByClassNameAsync(repoName))?.SourceCode
- : (await repoProject.FindCSharpSourceByClassNameAsync(repoName))?.SourceCode;
-
- bool newRepo = false;
-
- if(repoSource == null)
- {
- newRepo = true;
- repoSource = await source.CreateEFRepositoryAsync(repositoryName, efEntity, repoProject, contractInterface, poco,
- contextClass, useNDF, supportLogging, repoFolder,additionRepositoryNamespaces)
- ?? throw new CodeFactoryException($"Could not create the repository '{repoName}'.");
- }
-
-
- var repoClass = await source.UpdateEFRepositoryAsync(efEntity, repoProject, repoSource, contractInterface, poco,
- contextClass, useNDF, supportLogging, repoFolder, additionRepositoryNamespaces);
-
- if(newRepo) await source.RegisterTransientClassesAsync(repoProject,false);
-
- return repoClass;
- }
-
- ///
- /// Create the repositories interface contract.
- ///
- /// CodeFactory automation for Visual Studio for Windows.
- /// The name of the contract to be created.
- /// Entity framework entity.
- /// POCO model that supports the repository.
- /// Project the contract will be added to.
- /// Optional, project folder contracts will be stored in, default is null.
- /// Optional, additional namespaces to add to the contract definition, default is null.
- /// Contract interface definition
- /// Raised if required data is missing to create the interface.
- private static async Task CreateRepositoryContractAsync(this IVsActions source, string contractName, CsClass efEntity, VsProject contractProject,
- CsClass poco, VsProjectFolder contractFolder = null,List additionalContractNamespaces = null)
- {
- if (source == null)
- throw new CodeFactoryException("CodeFactory automation was not provided, cannot create the repository contract.");
-
- if (efEntity == null)
- throw new CodeFactoryException("The entity framework model was not provided, cannot create the repository contract.");
-
- if (contractProject == null)
- throw new CodeFactoryException("The contract project for the repository was not provided, cannot create the repository contract.");
-
- if (poco == null)
- throw new CodeFactoryException("The POCO model was not provided, cannot create the repository contract.");
-
-
- string defaultNamespace = contractFolder != null
- ? await contractFolder.GetCSharpNamespaceAsync()
- : contractProject.DefaultNamespace;
-
- SourceFormatter contractFormatter = new SourceFormatter();
-
- contractFormatter.AppendCodeLine(0, "using System;");
- contractFormatter.AppendCodeLine(0, "using System.Collections.Generic;");
- contractFormatter.AppendCodeLine(0, "using System.Text;");
- contractFormatter.AppendCodeLine(0, "using System.Threading.Tasks;");
-
- if (additionalContractNamespaces != null)
- {
- foreach (var additionalContractNamespace in additionalContractNamespaces)
- {
- contractFormatter.AppendCodeLine(0, additionalContractNamespace.HasAlias
- ? $"using {additionalContractNamespace.Alias} = {additionalContractNamespace.ReferenceNamespace};"
- : $"using {additionalContractNamespace.ReferenceNamespace};");
- }
- }
-
- contractFormatter.AppendCodeLine(0, $"using {poco?.Namespace};");
- contractFormatter.AppendCodeLine(0, $"namespace {defaultNamespace}");
- contractFormatter.AppendCodeLine(0, "{");
- contractFormatter.AppendCodeLine(1, "/// ");
- contractFormatter.AppendCodeLine(1, $"/// Contract for implementing a repository that supports the model ");
- contractFormatter.AppendCodeLine(1, "/// ");
- contractFormatter.AppendCodeLine(1, $"public interface {contractName}");
- contractFormatter.AppendCodeLine(1, "{");
- contractFormatter.AppendCodeLine(1);
- contractFormatter.AppendCodeLine(2, "/// ");
- contractFormatter.AppendCodeLine(2, $"/// Adds a new instance of the model.");
- contractFormatter.AppendCodeLine(2, "/// ");
- contractFormatter.AppendCodeLine(2, $"Task<{poco?.Name}> AddAsync({poco?.Name} {poco?.Name.GenerateCSharpCamelCase()});");
- contractFormatter.AppendCodeLine(2);
- contractFormatter.AppendCodeLine(2, "/// ");
- contractFormatter.AppendCodeLine(2, $"/// Updates a instance of the model.");
- contractFormatter.AppendCodeLine(2, "/// ");
- contractFormatter.AppendCodeLine(2, $"Task<{poco?.Name}> UpdateAsync({poco?.Name} {poco?.Name.GenerateCSharpCamelCase()});");
- contractFormatter.AppendCodeLine(2);
- contractFormatter.AppendCodeLine(2, "/// ");
- contractFormatter.AppendCodeLine(2, $"/// Deletes the instance of the model.");
- contractFormatter.AppendCodeLine(2, "/// ");
- contractFormatter.AppendCodeLine(2, $"Task DeleteAsync({poco?.Name} {poco?.Name.GenerateCSharpCamelCase()});");
- contractFormatter.AppendCodeLine(2);
-
- contractFormatter.AppendCodeLine(1, "}");
- contractFormatter.AppendCodeLine(0, "}");
-
- 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 repository contract '{contractName}' cannot upgrade the repository.")
- : ((await doc.GetCSharpSourceModelAsync())?.Interfaces.FirstOrDefault());
- }
-
- ///
- /// Creates a new repository that support entity framework.
- ///
- /// CodeFactory automation for Visual Studio for Windows.
- /// Entity framework entity.
- /// Repository project.
- /// Repository contract.
- /// POCO model that supports the repository.
- /// EF context class for accessing entity framework.
- /// Optional, flag that determines if the NDF libraries are used, default true.
- /// Optional, flag that determines if logging is supported, default true.
- /// Optional, project folder the repositories are stored in, default is null.
- /// Optional, list of additional namespaces to update the repository with.
- /// Optional, name of the logger field if logging is supported, default is '_logger'
- /// Optional, prefix to assign to the name of the repository, default is null.
- /// Optional, suffix to assign to the name of the repository, default is null.
- /// Source for the created repository.
- /// Raised if required data is missing to create the repository.
- private static async Task CreateEFRepositoryAsync(this IVsActions source, string repositoryName, CsClass efEntity, VsProject repoProject,
- CsInterface repoContract, CsClass poco, CsClass contextClass, bool useNDF = true, bool supportLogging = true, VsProjectFolder repoFolder = null,
- List additionRepositoryNamespaces = null, string loggerFieldName = "_logger")
- {
- if (source == null)
- throw new CodeFactoryException("CodeFactory automation was not provided, cannot create the repository.");
-
- if(string.IsNullOrEmpty(repositoryName))
- throw new CodeFactoryException("The repository name was not provided, cannot create the repository.");
-
- if (efEntity == null)
- throw new CodeFactoryException("The entity framework model was not provided, cannot create the repository.");
-
- if (repoContract == null)
- throw new CodeFactoryException("The repository interface was not provided, cannot create the repository.");
-
- if (repoProject == null)
- throw new CodeFactoryException("The repository project for the repository was not provided, cannot create the repository.");
-
- if (poco == null)
- throw new CodeFactoryException("The POCO model was not provided, cannot create the repository.");
-
- if (contextClass == null)
- throw new CodeFactoryException("No db contact class was provided, cannot create the repository.");
-
- string defaultNamespace = repoFolder != null
- ? await repoFolder.GetCSharpNamespaceAsync()
- : repoProject.DefaultNamespace;
-
- string repoName = repositoryName;
-
- SourceFormatter repoFormatter = new SourceFormatter();
-
- repoFormatter.AppendCodeLine(0, "using System;");
- repoFormatter.AppendCodeLine(0, "using System.Collections.Generic;");
- repoFormatter.AppendCodeLine(0, "using System.Text;");
- repoFormatter.AppendCodeLine(0, "using System.Threading.Tasks;");
- repoFormatter.AppendCodeLine(0, "using Microsoft.EntityFrameworkCore;");
- repoFormatter.AppendCodeLine(0, "using Microsoft.Data.SqlClient;");
-
- if (supportLogging)
- repoFormatter.AppendCodeLine(0, "using Microsoft.Extensions.Logging;");
- if (useNDF)
- {
- repoFormatter.AppendCodeLine(0, "using CodeFactory.NDF;");
- repoFormatter.AppendCodeLine(0, "using CodeFactory.NDF.SQL;");
- }
-
- if (additionRepositoryNamespaces != null)
- {
- foreach (var repoNamespace in additionRepositoryNamespaces)
- {
- repoFormatter.AppendCodeLine(0, repoNamespace.HasAlias
- ? $"using {repoNamespace.Alias} = {repoNamespace.ReferenceNamespace};"
- : $"using {repoNamespace.ReferenceNamespace};");
- }
- }
-
- repoFormatter.AppendCodeLine(0, $"using {efEntity.Namespace};");
- repoFormatter.AppendCodeLine(0, $"using {poco.Namespace};");
- repoFormatter.AppendCodeLine(0, $"using {repoContract.Namespace};");
- repoFormatter.AppendCodeLine(0);
- repoFormatter.AppendCodeLine(0, $"namespace {defaultNamespace}");
- repoFormatter.AppendCodeLine(0, "{");
- repoFormatter.AppendCodeLine(1, "/// ");
- repoFormatter.AppendCodeLine(1, $"/// Repository implementation that supports the model ");
- repoFormatter.AppendCodeLine(1, "/// ");
- repoFormatter.AppendCodeLine(1, $"public class {repoName}:{repoContract.Name}");
- repoFormatter.AppendCodeLine(1, "{");
- repoFormatter.AppendCodeLine(2, "/// ");
- repoFormatter.AppendCodeLine(2, "/// Connection string of the repository");
- repoFormatter.AppendCodeLine(2, "/// ");
- repoFormatter.AppendCodeLine(2, "private readonly string _connectionString;");
- repoFormatter.AppendCodeLine(2);
- repoFormatter.AppendCodeLine(2, "/// ");
- repoFormatter.AppendCodeLine(2, "/// Logger used by the repository.");
- repoFormatter.AppendCodeLine(2, "/// ");
- repoFormatter.AppendCodeLine(2, $"private readonly ILogger {loggerFieldName};");
- repoFormatter.AppendCodeLine(2);
- repoFormatter.AppendCodeLine(2, "/// ");
- repoFormatter.AppendCodeLine(2, "/// Creates a new instance of the repository.");
- repoFormatter.AppendCodeLine(2, "/// ");
- repoFormatter.AppendCodeLine(2, "/// Logger used with the repository.");
- repoFormatter.AppendCodeLine(2, "/// The connection information for the repository.");
- repoFormatter.AppendCodeLine(2, $"public {repoName}(ILogger<{repoName}> logger, IDBContextConnection<{contextClass.Name}> connection)");
- repoFormatter.AppendCodeLine(2, "{");
- repoFormatter.AppendCodeLine(3, $"{loggerFieldName} = logger;");
- repoFormatter.AppendCodeLine(3, "_connectionString = connection.ConnectionString;");
- repoFormatter.AppendCodeLine(2, "}");
- repoFormatter.AppendCodeLine(2);
- repoFormatter.AppendCodeLine(1, "}");
- repoFormatter.AppendCodeLine(0, "}");
-
- var doc = repoFolder != null ? await repoFolder.AddDocumentAsync($"{repoName}.cs", repoFormatter.ReturnSource())
- : await repoProject.AddDocumentAsync($"{repoName}.cs", repoFormatter.ReturnSource());
-
- return doc == null
- ? throw new CodeFactoryException($"Failed to create the repository '{repoName}' cannot upgrade the repository.")
- : await doc.GetCSharpSourceModelAsync();
- }
-
- ///
- /// Updates a repository with contract methods that are missing.
- ///
- /// CodeFactory automation for Visual Studio for Windows.
- /// Entity framework entity.
- /// Repository project.
- /// Repository source
- /// Repository contract.
- /// POCO model that supports the repository.
- /// EF context class for accessing entity framework.
- /// Optional, flag that determines if the NDF libraries are used, default true.
- /// Optional, flag that determines if logging is supported, default true.
- /// Optional, project folder the repositories are stored in, default is null.
- /// Optional, list of additional namespaces to update the repository with.
- /// Optional, the name of the logger field if logging is supported, default value is '_logger'
- /// Optional, the target logging level for logging messages, default value is Information.
- ///
- ///
- private static async Task UpdateEFRepositoryAsync(this IVsActions source, CsClass efEntity, VsProject repoProject,
- CsSource repoSource,CsInterface repoContract, CsClass poco, CsClass contextClass, bool useNDF = true, bool supportLogging = true, VsProjectFolder repoFolder = null,
- List additionRepositoryNamespaces = null,string loggerFieldName = "_logger", LogLevel logLevel = LogLevel.Information)
- {
- if (source == null)
- throw new CodeFactoryException("CodeFactory automation was not provided, cannot update the repository.");
-
- if (efEntity == null)
- throw new CodeFactoryException("The entity framework model was not provided, cannot update the repository.");
-
- if (repoContract == null)
- throw new CodeFactoryException("The repository interface was not provided, cannot update the repository.");
-
- if (repoProject == null)
- throw new CodeFactoryException("The repository project for the repository was not provided, cannot update the repository.");
-
- if (poco == null)
- throw new CodeFactoryException("The POCO model was not provided, cannot update the repository.");
-
- if (contextClass == null)
- throw new CodeFactoryException("No db contact class was provided, cannot update the repository.");
-
- var repoClass = repoSource?.Classes?.FirstOrDefault();
-
- if (repoClass == null)
- throw new CodeFactoryException("Could not load the class model for the existing repository, cannot update the repository.");
-
- var currentSource = repoSource;
-
- var repoMethods = repoClass.Methods;
-
- var contractMethods = repoContract.GetAllInterfaceMethods();
-
- var missingMethods = contractMethods.Where(m =>
- {
- var contractHash = m.GetComparisonHashCode();
- return repoMethods.All(c => c.GetComparisonHashCode() != contractHash);
- }).ToList();
-
- if (!missingMethods.Any()) return repoClass;
-
- var repoManager = new SourceClassManager(currentSource, repoClass, source);
-
- repoManager.LoadNamespaceManager();
-
- ILoggerBlock logFormatter = null;
-
- if (supportLogging)
- {
- if (useNDF) logFormatter = new LoggerBlockNDFLogger(loggerFieldName);
- else logFormatter = new LoggerBlockMicrosoft(loggerFieldName);
- }
-
- var boundCheckBlocks = new List();
- var catchBlocks = new List();
-
- if (useNDF)
- {
- boundCheckBlocks.Add(new BoundsCheckBlockStringNDFException(true,logFormatter));
- boundCheckBlocks.Add(new BoundsCheckBlockNullNDFException(true,logFormatter));
-
- catchBlocks.Add(new CatchBlockManagedExceptionNDFException(logFormatter));
- catchBlocks.Add(new CatchBlockDBUpdateExceptionNDFException(logFormatter));
- catchBlocks.Add(new CatchBlockSqlExceptionNDFException(logFormatter));
- catchBlocks.Add(new CatchBlockExceptionNDFException(logFormatter));
- }
- else
- {
- boundCheckBlocks.Add(new BoundsCheckBlockString(true,logFormatter));
- boundCheckBlocks.Add(new BoundsCheckBlockNull(true,logFormatter));
-
- catchBlocks.Add(new CatchBlockStandard(logFormatter));
- }
-
- var methodBuilder = new MethodBuilderStandard(logFormatter, boundCheckBlocks,new TryBlockStandard(logFormatter,catchBlocks));
-
- SourceFormatter injectFormatter = new SourceFormatter();
-
- foreach (var missingMethod in missingMethods)
- {
- injectFormatter.AppendCodeLine(0, $"using (var context = new {contextClass.Name}(_connectionString))");
- injectFormatter.AppendCodeLine(0, "{");
-
- switch (missingMethod.Name)
- {
- case "AddAsync":
- injectFormatter.AppendCodeLine(1, $"var model = {efEntity.Name}.CreateDataModel({poco.Name.GenerateCSharpCamelCase()});");
- injectFormatter.AppendCodeLine(1, "await context.AddAsync(model);");
- injectFormatter.AppendCodeLine(1, "await context.SaveChangesAsync();");
- injectFormatter.AppendCodeLine(1, "result = model.CreatePocoModel();");
- break;
-
- case "UpdateAsync":
- injectFormatter.AppendCodeLine(1, $"var model = {efEntity.Name}.CreateDataModel({poco.Name.GenerateCSharpCamelCase()});");
- injectFormatter.AppendCodeLine(1, "context.Update(model);");
- injectFormatter.AppendCodeLine(1, "await context.SaveChangesAsync();");
- injectFormatter.AppendCodeLine(1, "result = model.CreatePocoModel();");
- break;
-
- case "DeleteAsync":
- injectFormatter.AppendCodeLine(1, $"var model = {efEntity.Name}.CreateDataModel({poco.Name.GenerateCSharpCamelCase()});");
- injectFormatter.AppendCodeLine(1, "context.Remove(model);");
- injectFormatter.AppendCodeLine(1, "await context.SaveChangesAsync();");
- break;
+ ///
+ /// Automation logic to create or update a data repository that supports a target entity hosted in Entity Framework.
+ ///
+ public static class RepositoryBuilder
+ {
+ ///
+ /// Creates or updates a repository that uses entity framework for management of data.
+ ///
+ /// CodeFactory automation for Visual Studio for Windows.
+ /// The name of the repository to refresh.
+ /// Entity framework entity.
+ /// POCO model that supports the repository.
+ /// Project the contract will be added to.
+ /// Optional, project folder contracts will be stored in, default is null.
+ /// Optional, additional namespaces to add to the contract definition, default is null.
+ /// Optional, the name of the logger field if logging is supported, default value is '_logger'
+ /// Optional, the target logging level for logging messages, default value is Information.
+ /// Repository project.
+ /// EF context class for accessing entity framework.
+ /// Optional, flag that determines if the NDF libraries are used, default true.
+ /// Optional, flag that determines if logging is supported, default true.
+ /// Optional, project folder the repositories are stored in, default is null.
+ /// Optional, list of additional namespaces to update the repository with.
+ /// Created or updated repository.
+ /// Raised if required data to create or update the repository is missing.
+ public static async Task RefreshEFRepositoryWithCRUDAsync(this IVsActions source, string repositoryName, CsClass efEntity, VsProject repoProject,
+ VsProject contractProject, CsClass poco, CsClass contextClass, bool useNDF = true, bool supportLogging = true, VsProjectFolder repoFolder = null, VsProjectFolder contractFolder = null,
+ bool generateCrudOperations = true, List additionRepositoryNamespaces = null,
+ List additionalContractNamespaces = null, string loggerFieldName = "_logger", LogLevel logLevel = LogLevel.Information)
+ {
+
+ if (source == null)
+ throw new CodeFactoryException("CodeFactory automation was not provided, cannot refresh the EF repository.");
+
+ if (string.IsNullOrEmpty(repositoryName))
+ throw new CodeFactoryException("The repository name was not provided, cannot create the repository.");
+
+ if (efEntity == null)
+ throw new CodeFactoryException("The entity framework model was not provided, cannot refresh the EF repository.");
+
+ if (repoProject == null) throw new CodeFactoryException("The repository project was not provided, cannot refresh the EF repository.");
+
+ if (contractProject == null)
+ throw new CodeFactoryException("The contract project for the repository was not provided, cannot refresh the EF repository.");
+
+ if (poco == null)
+ throw new CodeFactoryException("The POCO model was not provided, cannot refresh the EF repository.");
+
+ if (contextClass == null)
+ throw new CodeFactoryException("The entity framework data context class was not provided, cannot refresh the EF repository.");
+
+ 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)
+ contractInterface = (await source.CreateRepositoryContractAsync(contractName, efEntity, contractProject, poco,
+ contractFolder, additionalContractNamespaces, generateCrudOperations)) ?? throw new CodeFactoryException("Could not create a repos");
+
+ var repoName = repositoryName;
+
+ CsSource repoSource = repoFolder != null
+ ? (await repoFolder.FindCSharpSourceByClassNameAsync(repoName))?.SourceCode
+ : (await repoProject.FindCSharpSourceByClassNameAsync(repoName))?.SourceCode;
+
+ bool newRepo = false;
+
+ if (repoSource == null)
+ {
+ newRepo = true;
+ repoSource = await source.CreateEFRepositoryAsync(repositoryName, efEntity, repoProject, contractInterface, poco,
+ contextClass, useNDF, supportLogging, repoFolder, additionRepositoryNamespaces)
+ ?? throw new CodeFactoryException($"Could not create the repository '{repoName}'.");
+ }
+
+
+ var repoClass = await source.UpdateEFRepositoryAsync(efEntity, repoProject, repoSource, contractInterface, poco,
+ contextClass, useNDF, supportLogging, repoFolder, additionRepositoryNamespaces);
+
+ if (newRepo) await source.RegisterTransientClassesAsync(repoProject, false);
+
+ return repoClass;
+ }
+
+ ///
+ /// Create the repositories interface contract.
+ ///
+ /// CodeFactory automation for Visual Studio for Windows.
+ /// The name of the contract to be created.
+ /// Entity framework entity.
+ /// POCO model that supports the repository.
+ /// Project the contract will be added to.
+ /// Optional, project folder contracts will be stored in, default is null.
+ /// Optional, additional namespaces to add to the contract definition, default is null.
+ /// Contract interface definition
+ /// Raised if required data is missing to create the interface.
+ private static async Task CreateRepositoryContractAsync(this IVsActions source, string contractName, CsClass efEntity, VsProject contractProject,
+ CsClass poco, VsProjectFolder contractFolder = null, List additionalContractNamespaces = null, bool generateCrudOperations = true)
+ {
+ if (source == null)
+ throw new CodeFactoryException("CodeFactory automation was not provided, cannot create the repository contract.");
+
+ if (efEntity == null)
+ throw new CodeFactoryException("The entity framework model was not provided, cannot create the repository contract.");
+
+ if (contractProject == null)
+ throw new CodeFactoryException("The contract project for the repository was not provided, cannot create the repository contract.");
+
+ if (poco == null)
+ throw new CodeFactoryException("The POCO model was not provided, cannot create the repository contract.");
+
+
+ string defaultNamespace = contractFolder != null
+ ? await contractFolder.GetCSharpNamespaceAsync()
+ : contractProject.DefaultNamespace;
+
+ SourceFormatter contractFormatter = new SourceFormatter();
+
+ contractFormatter.AppendCodeLine(0, "using System;");
+ contractFormatter.AppendCodeLine(0, "using System.Collections.Generic;");
+ contractFormatter.AppendCodeLine(0, "using System.Text;");
+ contractFormatter.AppendCodeLine(0, "using System.Threading.Tasks;");
+
+ if (additionalContractNamespaces != null)
+ {
+ foreach (var additionalContractNamespace in additionalContractNamespaces)
+ {
+ contractFormatter.AppendCodeLine(0, additionalContractNamespace.HasAlias
+ ? $"using {additionalContractNamespace.Alias} = {additionalContractNamespace.ReferenceNamespace};"
+ : $"using {additionalContractNamespace.ReferenceNamespace};");
+ }
+ }
+
+ contractFormatter.AppendCodeLine(0, $"using {poco?.Namespace};");
+ contractFormatter.AppendCodeLine(0, $"namespace {defaultNamespace}");
+ contractFormatter.AppendCodeLine(0, "{");
+ contractFormatter.AppendCodeLine(1, "/// ");
+ contractFormatter.AppendCodeLine(1, $"/// Contract for implementing a repository that supports the model ");
+ contractFormatter.AppendCodeLine(1, "/// ");
+ contractFormatter.AppendCodeLine(1, $"public interface {contractName}");
+ contractFormatter.AppendCodeLine(1, "{");
+
+ if (generateCrudOperations)
+ {
+ contractFormatter.AppendCodeLine(1);
+ contractFormatter.AppendCodeLine(2, "/// ");
+ contractFormatter.AppendCodeLine(2, $"/// Adds a new instance of the model.");
+ contractFormatter.AppendCodeLine(2, "/// ");
+ contractFormatter.AppendCodeLine(2, $"Task<{poco?.Name}> AddAsync({poco?.Name} {poco?.Name.GenerateCSharpCamelCase()});");
+ contractFormatter.AppendCodeLine(2);
+ contractFormatter.AppendCodeLine(2, "/// ");
+ contractFormatter.AppendCodeLine(2, $"/// Updates a instance of the model.");
+ contractFormatter.AppendCodeLine(2, "/// ");
+ contractFormatter.AppendCodeLine(2, $"Task<{poco?.Name}> UpdateAsync({poco?.Name} {poco?.Name.GenerateCSharpCamelCase()});");
+ contractFormatter.AppendCodeLine(2);
+ contractFormatter.AppendCodeLine(2, "/// ");
+ contractFormatter.AppendCodeLine(2, $"/// Deletes the instance of the model.");
+ contractFormatter.AppendCodeLine(2, "/// ");
+ contractFormatter.AppendCodeLine(2, $"Task DeleteAsync({poco?.Properties[0].PropertyType.Name}? {poco?.Properties[0].Name.GenerateCSharpCamelCase()});");
+ }
+
+ contractFormatter.AppendCodeLine(2);
+ contractFormatter.AppendCodeLine(1, "}");
+ contractFormatter.AppendCodeLine(0, "}");
+
+ var doc = contractFolder != null ? await contractFolder.AddDocumentAsync($"{contractName}.cs", contractFormatter.ReturnSource().TrimStartEndLines())
+ : await contractProject.AddDocumentAsync($"{contractName}.cs", contractFormatter.ReturnSource().TrimStartEndLines());
+
+ return doc == null
+ ? throw new CodeFactoryException($"Failed to create the repository contract '{contractName}' cannot upgrade the repository.")
+ : ((await doc.GetCSharpSourceModelAsync())?.Interfaces.FirstOrDefault());
+ }
+
+ ///
+ /// Creates a new repository that support entity framework.
+ ///
+ /// CodeFactory automation for Visual Studio for Windows.
+ /// Entity framework entity.
+ /// Repository project.
+ /// Repository contract.
+ /// POCO model that supports the repository.
+ /// EF context class for accessing entity framework.
+ /// Optional, flag that determines if the NDF libraries are used, default true.
+ /// Optional, flag that determines if logging is supported, default true.
+ /// Optional, project folder the repositories are stored in, default is null.
+ /// Optional, list of additional namespaces to update the repository with.
+ /// Optional, name of the logger field if logging is supported, default is '_logger'
+ /// Optional, prefix to assign to the name of the repository, default is null.
+ /// Optional, suffix to assign to the name of the repository, default is null.
+ /// Source for the created repository.
+ /// Raised if required data is missing to create the repository.
+ private static async Task CreateEFRepositoryAsync(this IVsActions source, string repositoryName, CsClass efEntity, VsProject repoProject,
+ CsInterface repoContract, CsClass poco, CsClass contextClass, bool useNDF = true, bool supportLogging = true, VsProjectFolder repoFolder = null,
+ List additionRepositoryNamespaces = null, string loggerFieldName = "_logger")
+ {
+ if (source == null)
+ throw new CodeFactoryException("CodeFactory automation was not provided, cannot create the repository.");
+
+ if (string.IsNullOrEmpty(repositoryName))
+ throw new CodeFactoryException("The repository name was not provided, cannot create the repository.");
+
+ if (efEntity == null)
+ throw new CodeFactoryException("The entity framework model was not provided, cannot create the repository.");
+
+ if (repoContract == null)
+ throw new CodeFactoryException("The repository interface was not provided, cannot create the repository.");
+
+ if (repoProject == null)
+ throw new CodeFactoryException("The repository project for the repository was not provided, cannot create the repository.");
+
+ if (poco == null)
+ throw new CodeFactoryException("The POCO model was not provided, cannot create the repository.");
+
+ if (contextClass == null)
+ throw new CodeFactoryException("No db contact class was provided, cannot create the repository.");
+
+ string defaultNamespace = repoFolder != null
+ ? await repoFolder.GetCSharpNamespaceAsync()
+ : repoProject.DefaultNamespace;
+
+ string repoName = repositoryName;
+
+ SourceFormatter repoFormatter = new SourceFormatter();
+
+ repoFormatter.AppendCodeLine(0, "using Microsoft.EntityFrameworkCore;");
+ repoFormatter.AppendCodeLine(0, "using Microsoft.Data.SqlClient;");
+
+ if (supportLogging)
+ repoFormatter.AppendCodeLine(0, "using Microsoft.Extensions.Logging;");
+ if (useNDF)
+ {
+ repoFormatter.AppendCodeLine(0, "using CodeFactory.NDF;");
+ repoFormatter.AppendCodeLine(0, "using CodeFactory.NDF.SQL;");
+ }
+
+ if (additionRepositoryNamespaces != null)
+ {
+ foreach (var repoNamespace in additionRepositoryNamespaces)
+ {
+ repoFormatter.AppendCodeLine(0, repoNamespace.HasAlias
+ ? $"using {repoNamespace.Alias} = {repoNamespace.ReferenceNamespace};"
+ : $"using {repoNamespace.ReferenceNamespace};");
+ }
+ }
+
+ repoFormatter.AppendCodeLine(0, $"using {efEntity.Namespace};");
+ repoFormatter.AppendCodeLine(0, $"using {poco.Namespace};");
+ repoFormatter.AppendCodeLine(0, $"using {repoContract.Namespace};");
+ repoFormatter.AppendCodeLine(0);
+ repoFormatter.AppendCodeLine(0, $"namespace {defaultNamespace}");
+ repoFormatter.AppendCodeLine(0, "{");
+ repoFormatter.AppendCodeLine(1, "/// ");
+ repoFormatter.AppendCodeLine(1, $"/// Repository implementation that supports the model ");
+ repoFormatter.AppendCodeLine(1, "/// ");
+ repoFormatter.AppendCodeLine(1, $"public class {repoName}:{repoContract.Name}");
+ repoFormatter.AppendCodeLine(1, "{");
+ repoFormatter.AppendCodeLine(2, "/// ");
+ repoFormatter.AppendCodeLine(2, "/// Connection string of the repository");
+ repoFormatter.AppendCodeLine(2, "/// ");
+ repoFormatter.AppendCodeLine(2, "private readonly string _connectionString;");
+ repoFormatter.AppendCodeLine(2);
+ repoFormatter.AppendCodeLine(2, "/// ");
+ repoFormatter.AppendCodeLine(2, "/// Logger used by the repository.");
+ repoFormatter.AppendCodeLine(2, "/// ");
+ repoFormatter.AppendCodeLine(2, $"private readonly ILogger {loggerFieldName};");
+ repoFormatter.AppendCodeLine(2);
+ repoFormatter.AppendCodeLine(2, "/// ");
+ repoFormatter.AppendCodeLine(2, "/// Creates a new instance of the repository.");
+ repoFormatter.AppendCodeLine(2, "/// ");
+ repoFormatter.AppendCodeLine(2, "/// Logger used with the repository.");
+ repoFormatter.AppendCodeLine(2, "/// The connection information for the repository.");
+ repoFormatter.AppendCodeLine(2, $"public {repoName}(ILogger<{repoName}> logger, IDBContextConnection<{contextClass.Name}> connection)");
+ repoFormatter.AppendCodeLine(2, "{");
+ repoFormatter.AppendCodeLine(3, $"{loggerFieldName} = logger;");
+ repoFormatter.AppendCodeLine(3, "_connectionString = connection.ConnectionString!;");
+ repoFormatter.AppendCodeLine(2, "}");
+ repoFormatter.AppendCodeLine(2);
+ repoFormatter.AppendCodeLine(1, "}");
+ repoFormatter.AppendCodeLine(0, "}");
+
+ var doc = repoFolder != null ? await repoFolder.AddDocumentAsync($"{repoName}.cs", repoFormatter.ReturnSource().TrimStartEndLines())
+ : await repoProject.AddDocumentAsync($"{repoName}.cs", repoFormatter.ReturnSource().TrimStartEndLines());
+
+ return doc == null
+ ? throw new CodeFactoryException($"Failed to create the repository '{repoName}' cannot upgrade the repository.")
+ : await doc.GetCSharpSourceModelAsync();
+ }
+
+ ///
+ /// Updates a repository with contract methods that are missing.
+ ///
+ /// CodeFactory automation for Visual Studio for Windows.
+ /// Entity framework entity.
+ /// Repository project.
+ /// Repository source
+ /// Repository contract.
+ /// POCO model that supports the repository.
+ /// EF context class for accessing entity framework.
+ /// Optional, flag that determines if the NDF libraries are used, default true.
+ /// Optional, flag that determines if logging is supported, default true.
+ /// Optional, project folder the repositories are stored in, default is null.
+ /// Optional, list of additional namespaces to update the repository with.
+ /// Optional, the name of the logger field if logging is supported, default value is '_logger'
+ /// Optional, the target logging level for logging messages, default value is Information.
+ ///
+ ///
+ private static async Task UpdateEFRepositoryAsync(this IVsActions source, CsClass efEntity, VsProject repoProject,
+ CsSource repoSource, CsInterface repoContract, CsClass poco, CsClass contextClass, bool useNDF = true, bool supportLogging = true, VsProjectFolder repoFolder = null,
+ List additionRepositoryNamespaces = null, string loggerFieldName = "_logger", LogLevel logLevel = LogLevel.Information)
+ {
+ if (source == null)
+ throw new CodeFactoryException("CodeFactory automation was not provided, cannot update the repository.");
+
+ if (efEntity == null)
+ throw new CodeFactoryException("The entity framework model was not provided, cannot update the repository.");
+
+ if (repoContract == null)
+ throw new CodeFactoryException("The repository interface was not provided, cannot update the repository.");
+
+ if (repoProject == null)
+ throw new CodeFactoryException("The repository project for the repository was not provided, cannot update the repository.");
+
+ if (poco == null)
+ throw new CodeFactoryException("The POCO model was not provided, cannot update the repository.");
+
+ if (contextClass == null)
+ throw new CodeFactoryException("No db contact class was provided, cannot update the repository.");
+
+ var repoClass = repoSource?.Classes?.FirstOrDefault();
+
+ if (repoClass == null)
+ throw new CodeFactoryException("Could not load the class model for the existing repository, cannot update the repository.");
+
+ var currentSource = repoSource;
+
+ var repoMethods = repoClass.Methods;
+
+ var contractMethods = repoContract.GetAllInterfaceMethods();
+
+ var missingMethods = contractMethods.Where(m =>
+ {
+ var contractHash = m.GetComparisonHashCode();
+ return repoMethods.All(c => c.GetComparisonHashCode() != contractHash);
+ }).ToList();
+
+ if (!missingMethods.Any()) return repoClass;
+
+ var repoManager = new SourceClassManager(currentSource, repoClass, source);
+
+ repoManager.LoadNamespaceManager();
+
+ ILoggerBlock logFormatter = null;
+
+ if (supportLogging)
+ {
+ if (useNDF) logFormatter = new LoggerBlockNDFLogger(loggerFieldName);
+ else logFormatter = new LoggerBlockMicrosoft(loggerFieldName);
+ }
+
+ var boundCheckBlocks = new List();
+ var catchBlocks = new List();
+
+ if (useNDF)
+ {
+ boundCheckBlocks.Add(new BoundsCheckBlockStringNDFException(true, logFormatter));
+ boundCheckBlocks.Add(new BoundsCheckBlockNullNDFException(true, logFormatter));
+
+ catchBlocks.Add(new CatchBlockManagedExceptionNDFException(logFormatter));
+ catchBlocks.Add(new CatchBlockDBUpdateExceptionNDFException(logFormatter));
+ catchBlocks.Add(new CatchBlockSqlExceptionNDFException(logFormatter));
+ catchBlocks.Add(new CatchBlockExceptionNDFException(logFormatter));
+ }
+ else
+ {
+ boundCheckBlocks.Add(new BoundsCheckBlockString(true, logFormatter));
+ boundCheckBlocks.Add(new BoundsCheckBlockNull(true, logFormatter));
+
+ catchBlocks.Add(new CatchBlockStandard(logFormatter));
+ }
+
+ var methodBuilder = new MethodBuilderStandard(logFormatter, boundCheckBlocks, new TryBlockStandard(logFormatter, catchBlocks));
+
+ SourceFormatter injectFormatter = new SourceFormatter();
+
+ foreach (var missingMethod in missingMethods)
+ {
+ injectFormatter.AppendCodeLine(0, $"using (var context = new {contextClass.Name}(_connectionString))");
+ injectFormatter.AppendCodeLine(0, "{");
+
+ switch (missingMethod.Name)
+ {
+ case "AddAsync":
+ injectFormatter.AppendCodeLine(1, $"var model = {efEntity.Name}.CreateDataModel({poco.Name.GenerateCSharpCamelCase()});");
+ injectFormatter.AppendCodeLine(1, "context.Entry(model).State = EntityState.Modified;");
+ injectFormatter.AppendCodeLine(1, "await context.AddAsync(model);");
+ injectFormatter.AppendCodeLine(1, "await context.SaveChangesAsync();");
+ injectFormatter.AppendCodeLine(1, "result = model!.CreatePocoModel();");
+ break;
+
+ case "UpdateAsync":
+ injectFormatter.AppendCodeLine(1, $"var model = {efEntity.Name}.CreateDataModel({poco.Name.GenerateCSharpCamelCase()});");
+ injectFormatter.AppendCodeLine(1, "context.Entry(model).State = EntityState.Modified;");
+ injectFormatter.AppendCodeLine(1, "context.Update(model);");
+ injectFormatter.AppendCodeLine(1, "await context.SaveChangesAsync();");
+ injectFormatter.AppendCodeLine(1, "result = model!.CreatePocoModel();");
+ break;
+
+ case "DeleteAsync":
+ injectFormatter.AppendCodeLine(1, $"var model = context!.{efEntity.Name.Pluralize()}.FirstOrDefault(m => m.{poco.Properties[0].Name} == {poco.Properties[0].Name.GenerateCSharpCamelCase()})!;");
+ injectFormatter.AppendCodeLine(1, "context.Entry(model).State = EntityState.Deleted;");
+ injectFormatter.AppendCodeLine(1, "context.Remove(model);");
+ injectFormatter.AppendCodeLine(1, "await context.SaveChangesAsync();");
+ break;
- default:
- injectFormatter.AppendCodeLine(0);
- break;
- }
+ default:
+ injectFormatter.AppendCodeLine(0);
+ break;
+ }
- injectFormatter.AppendCodeLine(0, "}");
+ injectFormatter.AppendCodeLine(0, "}");
- string syntax = injectFormatter.ReturnSource();
- await methodBuilder.InjectMethodAsync(missingMethod, repoManager, 2, syntax: syntax,defaultLogLevel:logLevel);
+ string syntax = injectFormatter.ReturnSource().TrimStartEndLines();
+ await methodBuilder.InjectMethodAsync(missingMethod, repoManager, 2, syntax: syntax, defaultLogLevel: logLevel);
- injectFormatter.ResetFormatter();
- }
+ injectFormatter.ResetFormatter();
+ }
- return repoManager.Container;
- }
+ return repoManager.Container;
+ }
- ///
- /// Checks the ef model property and determines if it should be included in a poco model implementation.
- ///
- /// Property to evaluate.
- /// True if the property should be included, false if not.
- public static bool UseSourceProperty(CsProperty source)
- {
- if (source == null) return false;
-
- bool useSource = (source.HasGet & source.HasSet & source.Security == CsSecurity.Public & !source.IsStatic);
-
- if (source.HasAttributes & useSource)
- {
- useSource = !source.Attributes.Any(a => a.Type.Namespace == "System.ComponentModel.DataAnnotations.Schema" & a.Type.Name == "ForeignKeyAttribute");
- if (useSource) useSource = !source.Attributes.Any(a => a.Type.Namespace == "System.ComponentModel.DataAnnotations.Schema" & a.Type.Name == "InversePropertyAttribute");
- }
-
- return useSource;
-
- }
- }
+ ///
+ /// Checks the ef model property and determines if it should be included in a poco model implementation.
+ ///
+ /// Property to evaluate.
+ /// True if the property should be included, false if not.
+ public static bool UseSourceProperty(CsProperty source)
+ {
+ if (source == null) return false;
+
+ bool useSource = (source.HasGet & source.HasSet & source.Security == CsSecurity.Public & !source.IsStatic);
+
+ if (source.HasAttributes & useSource)
+ {
+ useSource = !source.Attributes.Any(a => a.Type.Namespace == "System.ComponentModel.DataAnnotations.Schema" & a.Type.Name == "ForeignKeyAttribute");
+ if (useSource) useSource = !source.Attributes.Any(a => a.Type.Namespace == "System.ComponentModel.DataAnnotations.Schema" & a.Type.Name == "InversePropertyAttribute");
+ }
+
+ return useSource;
+
+ }
+ }
}
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/DependencyInjectionBuilder.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/DependencyInjectionBuilder.cs
index a99b625..7eb15e9 100644
--- a/src/Automation/CodeFactory.Automation.NDF.Logic/DependencyInjectionBuilder.cs
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/DependencyInjectionBuilder.cs
@@ -1,22 +1,22 @@
-using CodeFactory.WinVs.Logging;
-using CodeFactory.WinVs.Models.CSharp.Builder;
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs;
+using CodeFactory.WinVs.Logging;
using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.WinVs.Models.CSharp.Builder;
using CodeFactory.WinVs.Models.ProjectSystem;
-using CodeFactory.WinVs;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using CodeFactory.Automation.Standard.Logic;
namespace CodeFactory.Automation.NDF.Logic
{
- ///
- /// Automation class that will manage creation of dependency injection transient code in libraries that support NDF.
- ///
- public static class DependencyInjectionBuilder
+ ///
+ /// Automation class that will manage creation of dependency injection transient code in libraries that support NDF.
+ ///
+ public static class DependencyInjectionBuilder
{
//Logger used for code factory logging
// ReSharper disable once InconsistentNaming
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/General/FluentValidationBuilder.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/General/FluentValidationBuilder.cs
index e733c53..43e2206 100644
--- a/src/Automation/CodeFactory.Automation.NDF.Logic/General/FluentValidationBuilder.cs
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/General/FluentValidationBuilder.cs
@@ -1,19 +1,17 @@
-using CodeFactory.WinVs.Models.CSharp;
-using CodeFactory.WinVs.Models.ProjectSystem;
+using CodeFactory.Automation.Standard.Logic;
+using CodeFactory.Automation.Standard.Logic.Extensions;
using CodeFactory.WinVs;
-using System;
-using System.Collections.Generic;
+using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.WinVs.Models.ProjectSystem;
using System.Linq;
-using System.Text;
using System.Threading.Tasks;
-using CodeFactory.Automation.Standard.Logic;
namespace CodeFactory.Automation.NDF.Logic.General
{
- ///
- /// Automation logic for the creation and update of fluent validation classes.
- ///
- public static class FluentValidationBuilder
+ ///
+ /// Automation logic for the creation and update of fluent validation classes.
+ ///
+ public static class FluentValidationBuilder
{
///
/// Refreshes the definition of a fluent validation class.
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/General/ModelBuilder.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/General/ModelBuilder.cs
index f08fa17..3eadf4f 100644
--- a/src/Automation/CodeFactory.Automation.NDF.Logic/General/ModelBuilder.cs
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/General/ModelBuilder.cs
@@ -1,352 +1,312 @@
-using CodeFactory.WinVs.Logging;
-using CodeFactory.WinVs.Models.CSharp.Builder;
+using CodeFactory.Automation.Standard.Logic;
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs;
+using CodeFactory.WinVs.Logging;
using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.WinVs.Models.CSharp.Builder;
using CodeFactory.WinVs.Models.ProjectSystem;
-using CodeFactory.WinVs;
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Text;
using System.Threading.Tasks;
-using CodeFactory.Automation.Standard.Logic;
namespace CodeFactory.Automation.NDF.Logic.General
{
- ///
- /// Automation library that creates a model class that supports standard property implementation.
- ///
- public static class ModelBuilder
- {
- private static readonly ILogger _logger = LogManager.GetLogger(typeof(ModelBuilder));
-
- ///
- /// Refreshes the implementation of a plain old CLR object based on the definition of a provided class. This will also create the model if it does not exist.
- ///
- /// CodeFactory automation framework used to trigger the refresh.
- /// The source class model the model will be generated or updated from.
- /// The target project the model will be created in.
- /// The default namespaces to be added as using statements if they do not exist in the model class.
- /// Optional parameter that provides the details for how to format the name of the model class.
- /// Optional parameter that determines the target folder the model is to be located in if not on the root of the project.
- /// Optional parameter that sets the XML summary for the model class.
- /// Optional parameter that determines if properties should converted to non nullable types, default value is true.
- /// Optional parameter that maps source and target namespaces when creating the model, default value is null.
- /// Optional parameter that calls a delegate to confirm the property should be included with the model class.
- /// The loaded class model for the created model class.
- /// Raised if required data is missing or a processing error has occurred.
- public static async Task RefreshModelAsync(this IVsActions source, CsClass sourceClass,
- VsProject modelProject, List defaultNamespaces, NameManagement nameManagement = null, VsProjectFolder modelFolder = null,
- string modelSummary = null, bool convertNullableTypes = true, List mappedNamespaces = null, UseSourcePropertyDelegate useSourceProperty = null)
- {
- //Bounds checking
- if (source == null)
- throw new CodeFactoryException(
- "The CodeFactory automation was not provided cannot refresh the model class.");
- if (sourceClass == null)
- throw new CodeFactoryException("The source class was not provided cannot refresh the model class.");
- if (modelProject == null)
- throw new CodeFactoryException(
- "The target model project was not provided cannot refresh the model class.");
-
- CsSource modelSource = null;
-
- string modelName = null;
-
- modelName = nameManagement != null ? nameManagement.FormatName(sourceClass.Name) : sourceClass.Name;
-
- if(string.IsNullOrEmpty(modelName))
- throw new CodeFactoryException("Could not format the target model class name, cannot create the model class.");
-
- bool wasCreated = false;
-
- if (modelFolder == null)
- {
- modelSource = (await modelProject.FindCSharpSourceByClassNameAsync(modelName))?.SourceCode;
- }
- else
- {
- modelSource = (await modelFolder.FindCSharpSourceByClassNameAsync(modelName))?.SourceCode;
- }
-
- if (modelSource == null)
- {
- modelSource = await source.CreateModelAsync(sourceClass, modelProject,modelName, defaultNamespaces, modelFolder,
- modelSummary, convertNullableTypes)
- ?? throw new CodeFactoryException($"Could not create the model '{modelName}' cannot refresh the model.");
- }
-
-
-
- var modelClass = await source.UpdateModelAsync(sourceClass, modelProject, modelSource, defaultNamespaces,nameManagement, modelFolder,
- modelSummary, convertNullableTypes, mappedNamespaces);
-
-
- //if(wasCreated) await source.RegisterTransientClassesAsync(modelProject,false);
-
- return modelClass;
-
- }
-
- ///
- /// Creates the shell implementation of a plain old CLR object.
- ///
- /// CodeFactory automation framework used to trigger the refresh.
- /// The source class model the model will be generated or updated from.
- /// The target project the model will be created in.
- /// The default namespaces to be added as using statements if they do not exist in the model class.
- /// Optional parameter that determines the target folder the model is to be located in if not on the root of the project.
- /// Optional parameter that sets the XML summary for the model class.
- /// Optional parameter that determines if properties should converted to non nullable types, default value is true.
- /// The loaded class model for the created model class.
- /// Raised if required data is missing or a processing error has occurred.
- private static async Task CreateModelAsync(this IVsActions source, CsClass sourceClass,
- VsProject modelProject, string targetClassName, List defaultNamespaces, VsProjectFolder modelFolder = null,
- string modelSummary = null, bool convertNullableTypes = true)
- {
- if (source == null)
- throw new CodeFactoryException(
- "The CodeFactory automation was not provided cannot create the model class.");
- if (sourceClass == null)
- throw new CodeFactoryException("The source class was not provided cannot create the model class.");
- if (modelProject == null)
- throw new CodeFactoryException(
- "The target model project was not provided cannot create the model class.");
-
- CsSource result = null;
-
- try
- {
- SourceFormatter modelFormatter = new SourceFormatter();
-
- if (defaultNamespaces != null)
- {
- if (defaultNamespaces.Any())
- {
- foreach (var usingStatement in defaultNamespaces)
- {
- modelFormatter.AppendCodeLine(0,
- usingStatement.HasAlias
- ? $"using {usingStatement.Alias} = {usingStatement.ReferenceNamespace};"
- : $"using {usingStatement.ReferenceNamespace};");
- }
- }
- }
-
- modelFormatter.AppendCodeLine(0,
- modelFolder == null
- ? $"namespace {modelProject.DefaultNamespace}"
- : $"namespace {await modelFolder.GetCSharpNamespaceAsync()}");
- modelFormatter.AppendCodeLine(0, "{");
-
- modelFormatter.AppendCodeLine(1, "/// ");
-
- if (modelSummary == null)
- modelFormatter.AppendCodeLine(1, "/// Plain old CLR object (model) data class implementation.");
- else
- {
- var summaryLines = modelSummary.Split(Environment.NewLine.ToCharArray());
- foreach (var summaryLine in summaryLines) modelFormatter.AppendCodeLine(1, $"/// {summaryLine}");
- }
-
- modelFormatter.AppendCodeLine(1, "/// ");
- modelFormatter.AppendCodeLine(1, $"public class {targetClassName}");
- modelFormatter.AppendCodeLine(1, "{");
- modelFormatter.AppendCodeLine(1);
- modelFormatter.AppendCodeLine(1, "}");
-
- modelFormatter.AppendCodeLine(0, "}");
-
-
- VsDocument modelDoc = modelFolder == null
- ? await modelProject.AddDocumentAsync($"{targetClassName}.cs", modelFormatter.ReturnSource())
- : await modelFolder.AddDocumentAsync($"{targetClassName}.cs", modelFormatter.ReturnSource());
-
- if (modelDoc == null) throw new CodeFactoryException($"Error occurred saving the model to the project.");
-
- result = await modelDoc.GetCSharpSourceModelAsync();
- }
- catch (CodeFactoryException)
- {
- throw;
- }
- catch (Exception unhandledError)
- {
- _logger.Error("The following unhandled error occurred creating a model.", unhandledError);
- throw new CodeFactoryException(
- $"An unhandled error occurred while creating the model '{targetClassName}'. Check the code factory logs for details of what happened.");
- }
-
- return result;
- }
-
- ///
- /// Updates the implementation of a plain old CLR object from the definition of a source class.
- ///
- /// CodeFactory automation framework used to trigger the refresh.
- /// The source class model the model will be generated or updated from.
- /// The target project the model will be created in.
- /// The source model that holds the existing model implementation.
- /// The default namespaces to be added as using statements if they do not exist in the model class.
- /// Optional parameter that provides the details for how to format the name of the model class.
- /// Optional parameter that determines the target folder the model is to be located in if not on the root of the project.
- /// Optional parameter that sets the XML summary for the model class.
- /// Optional parameter that determines if properties should converted to non nullable types, default value is true.
- /// Optional parameter that maps source and target namespaces when creating the model, default value is null.
- /// Optional parameter that calls a delegate to confirm the property should be included with the model class.
- /// The loaded class model for the created model class.
- /// Raised if required data is missing or a processing error has occurred.
- private static async Task UpdateModelAsync(this IVsActions source, CsClass sourceClass,
- VsProject modelProject, CsSource modelSource, List defaultNamespaces,NameManagement nameManagement = null,
- VsProjectFolder modelFolder = null, string modelSummary = null, bool convertNullableTypes = true, List mappedNamespaces = null,UseSourcePropertyDelegate useSourceProperty = null)
- {
-
- if (source == null)
- throw new CodeFactoryException(
- "The CodeFactory automation was not provided cannot update the model class.");
- if (sourceClass == null)
- throw new CodeFactoryException("The source class was not provided cannot update the model class.");
- if (modelSource == null)
- throw new CodeFactoryException("The target model source was not provided cannot update the model class.");
-
- var sourcePoco = modelSource;
- var modelClass = sourcePoco.Classes.FirstOrDefault();
-
- if (modelClass == null)
- throw new CodeFactoryException(
- $"The call information for the model could not be loaded, cannot update the model class from source class '{sourceClass.Name}'.");
-
- var sourceProperties = useSourceProperty != null
- ? sourceClass.Properties.Where( p=> useSourceProperty(p)).ToList()
- : sourceClass.Properties;
-
- if (!sourceProperties.Any()) return modelClass;
-
- var modelProperties = modelClass.Properties
- .Where(p => p.HasGet & p.HasSet & p.Security == CsSecurity.Public & !p.IsStatic).ToList();
-
- var addList = sourceProperties.Where(s => modelProperties.All(p => p.Name != s.Name)).ToList();
- var checkList = sourceProperties.Where(s => modelProperties.Any(p => p.Name == s.Name)).ToList();
- var removeList = new List();
-
- bool hasChanges = false;
-
- var modelMappedNamespaces = mappedNamespaces ?? new List();
- modelMappedNamespaces.Add(new MapNamespace { Destination = modelClass.Namespace, Source = sourceClass.Namespace });
-
- foreach (var sourceProperty in checkList)
- {
- var modelProperty = modelProperties.FirstOrDefault(p => p.Name == sourceProperty.Name);
-
- if (modelProperty == null)
- {
- addList.Add(sourceProperty);
- continue;
- }
-
- if (sourceProperty.PropertyType.GenerateCSharpTypeName(mappedNamespaces: modelMappedNamespaces)
- != modelProperty.PropertyType.GenerateCSharpTypeName(mappedNamespaces: modelMappedNamespaces))
- {
- addList.Add(sourceProperty);
- removeList.Add(modelProperty);
- }
- }
-
- hasChanges = removeList.Any();
- if (!hasChanges) hasChanges = addList.Any();
-
- if (!hasChanges) return modelClass;
-
- if (defaultNamespaces != null)
- {
- foreach (var defaultNamespace in defaultNamespaces)
- {
- modelSource = await modelSource.AddUsingStatementAsync(defaultNamespace.ReferenceNamespace,
- defaultNamespace.Alias);
- }
- }
-
- var propBuilder = new PropertyBuilderTransformNullableTypes(convertNullableTypes);
-
- var modelUpdateManager = new SourceClassManager(modelSource, modelClass, source, mappedNamespaces: modelMappedNamespaces);
-
- modelUpdateManager.LoadNamespaceManager();
-
- foreach (var removeProperty in removeList)
- {
- if (removeProperty == null) continue;
-
- var propertyToRemove = modelUpdateManager.Container.GetModel(removeProperty.LookupPath);
-
- if (propertyToRemove != null) await modelUpdateManager.MemberCommentOut(propertyToRemove);
- }
-
- foreach (var propertyToAdd in addList)
- {
- if (propertyToAdd == null) continue;
-
- //Checking to make sure other source class objects used by the model also have model's created.
- if (propertyToAdd.PropertyType.TypeInTargetNamespace(sourceClass.Namespace))
- {
- if (propertyToAdd.PropertyType.Name != sourceClass.Name)
- {
- var targetPocoModel =
- await modelProject.FindCSharpSourceByClassNameAsync(propertyToAdd.PropertyType.Name);
-
- if (targetPocoModel == null)
- {
- var sourceModelClass = propertyToAdd.PropertyType.GetClassModel() ?? throw new CodeFactoryException(
- $"Could not load the data model '{propertyToAdd.PropertyType.Name}' could not refresh the model model, cannot update the model class '{modelClass.Name}'.");
-
- if (sourceModelClass.IsLoaded == false) throw new CodeFactoryException($"Could not load the data model '{propertyToAdd.PropertyType.Name}' could not refresh the model model, cannot update the model class '{modelClass.Name}'.");
-
- await source.RefreshModelAsync(sourceModelClass, modelProject, defaultNamespaces,nameManagement,
- modelFolder,modelSummary, convertNullableTypes,useSourceProperty:useSourceProperty);
- }
- }
- }
-
- if (propertyToAdd.PropertyType.IsGeneric)
- {
- //Checking to make sure other source class objects used by the model also have model's created.
- foreach (var propGenericType in propertyToAdd.PropertyType.GenericTypes)
- {
- //Checking to make sure other source class objects used by the model also have model's created.
- if (propGenericType.TypeInTargetNamespace(sourceClass.Namespace))
- {
- if (propGenericType.Name != sourceClass.Name)
- {
- var targetPocoModel =
- await modelProject.FindCSharpSourceByClassNameAsync(propGenericType.Name);
-
- if (targetPocoModel == null)
- {
- var sourceModelClass = propGenericType.GetClassModel() ?? throw new CodeFactoryException(
- $"Could not load the data model '{propertyToAdd.PropertyType.Name}' could not refresh the model model, cannot update the model class '{modelClass.Name}'.");
-
- if (sourceModelClass.IsLoaded == false) throw new CodeFactoryException($"Could not load the data model '{propertyToAdd.PropertyType.Name}' could not refresh the model model, cannot update the model class '{modelClass.Name}'.");
-
- await source.RefreshModelAsync(sourceModelClass, modelProject, defaultNamespaces,nameManagement,
- modelFolder,modelSummary,convertNullableTypes,useSourceProperty:useSourceProperty);
- }
- }
- }
- }
- }
-
- var propertySyntax = await propBuilder.BuildPropertyAsync(propertyToAdd, modelUpdateManager, 2);
-
- if (propertySyntax != null) await modelUpdateManager.PropertiesAddAfterAsync(propertySyntax);
-
- }
-
- return modelUpdateManager.Container;
- }
-
- ///
- /// Logic to check if a property should be included in a model class implementation.
- ///
- /// The property model to be evaluated.
- /// True if the property should be used, false if not.
- public delegate bool UseSourcePropertyDelegate(CsProperty source);
- }
+ ///
+ /// Automation library that creates a model class that supports standard property implementation.
+ ///
+ public static class ModelBuilder
+ {
+ private static readonly ILogger _logger = LogManager.GetLogger(typeof(ModelBuilder));
+
+ ///
+ /// Refreshes the implementation of a plain old CLR object based on the definition of a provided class. This will also create the model if it does not exist.
+ ///
+ /// CodeFactory automation framework used to trigger the refresh.
+ /// The source class model the model will be generated or updated from.
+ /// The target project the model will be created in.
+ /// The default namespaces to be added as using statements if they do not exist in the model class.
+ /// Optional parameter that provides the details for how to format the name of the model class.
+ /// Optional parameter that determines the target folder the model is to be located in if not on the root of the project.
+ /// Optional parameter that sets the XML summary for the model class.
+ /// Optional parameter that determines if properties should converted to non nullable types, default value is true.
+ /// Optional parameter that maps source and target namespaces when creating the model, default value is null.
+ /// Optional parameter that calls a delegate to confirm the property should be included with the model class.
+ /// The loaded class model for the created model class.
+ /// Raised if required data is missing or a processing error has occurred.
+ public static async Task RefreshModelWithoutDependenciesAsync(this IVsActions source, CsClass sourceClass,
+ VsProject modelProject, List defaultNamespaces, NameManagement nameManagement = null, VsProjectFolder modelFolder = null,
+ string modelSummary = null, bool convertNullableTypes = true, List mappedNamespaces = null, UseSourcePropertyDelegate useSourceProperty = null)
+ {
+ //Bounds checking
+ if (source == null)
+ throw new CodeFactoryException(
+ "The CodeFactory automation was not provided cannot refresh the model class.");
+ if (sourceClass == null)
+ throw new CodeFactoryException("The source class was not provided cannot refresh the model class.");
+ if (modelProject == null)
+ throw new CodeFactoryException(
+ "The target model project was not provided cannot refresh the model class.");
+
+ CsSource modelSource = null;
+
+ string modelName = null;
+
+ modelName = nameManagement != null ? nameManagement.FormatName(sourceClass.Name) : sourceClass.Name;
+
+ if (string.IsNullOrEmpty(modelName))
+ throw new CodeFactoryException("Could not format the target model class name, cannot create the model class.");
+
+ bool wasCreated = false;
+
+ if (modelFolder == null)
+ {
+ modelSource = (await modelProject.FindCSharpSourceByClassNameAsync(modelName))?.SourceCode;
+ }
+ else
+ {
+ modelSource = (await modelFolder.FindCSharpSourceByClassNameAsync(modelName))?.SourceCode;
+ }
+
+ if (modelSource == null)
+ {
+ modelSource = await source.CreateModelAsync(sourceClass, modelProject, modelName, defaultNamespaces, modelFolder,
+ modelSummary, convertNullableTypes)
+ ?? throw new CodeFactoryException($"Could not create the model '{modelName}' cannot refresh the model.");
+ }
+
+
+
+ var modelClass = await source.UpdateModelAsync(sourceClass, modelProject, modelSource, defaultNamespaces, nameManagement, modelFolder,
+ modelSummary, convertNullableTypes, mappedNamespaces);
+
+
+ //if(wasCreated) await source.RegisterTransientClassesAsync(modelProject,false);
+
+ return modelClass;
+
+ }
+
+ ///
+ /// Creates the shell implementation of a plain old CLR object.
+ ///
+ /// CodeFactory automation framework used to trigger the refresh.
+ /// The source class model the model will be generated or updated from.
+ /// The target project the model will be created in.
+ /// The default namespaces to be added as using statements if they do not exist in the model class.
+ /// Optional parameter that determines the target folder the model is to be located in if not on the root of the project.
+ /// Optional parameter that sets the XML summary for the model class.
+ /// Optional parameter that determines if properties should converted to non nullable types, default value is true.
+ /// The loaded class model for the created model class.
+ /// Raised if required data is missing or a processing error has occurred.
+ private static async Task CreateModelAsync(this IVsActions source, CsClass sourceClass,
+ VsProject modelProject, string targetClassName, List defaultNamespaces, VsProjectFolder modelFolder = null,
+ string modelSummary = null, bool convertNullableTypes = true)
+ {
+ if (source == null)
+ throw new CodeFactoryException(
+ "The CodeFactory automation was not provided cannot create the model class.");
+ if (sourceClass == null)
+ throw new CodeFactoryException("The source class was not provided cannot create the model class.");
+ if (modelProject == null)
+ throw new CodeFactoryException(
+ "The target model project was not provided cannot create the model class.");
+
+ CsSource result = null;
+
+ try
+ {
+ SourceFormatter modelFormatter = new SourceFormatter();
+
+ if (defaultNamespaces != null)
+ {
+ if (defaultNamespaces.Any())
+ {
+ foreach (var usingStatement in defaultNamespaces)
+ {
+ modelFormatter.AppendCodeLine(0,
+ usingStatement.HasAlias
+ ? $"using {usingStatement.Alias} = {usingStatement.ReferenceNamespace};"
+ : $"using {usingStatement.ReferenceNamespace};");
+ }
+ }
+ }
+
+ modelFormatter.AppendCodeLine(0,
+ modelFolder == null
+ ? $"namespace {modelProject.DefaultNamespace}"
+ : $"namespace {await modelFolder.GetCSharpNamespaceAsync()}");
+ modelFormatter.AppendCodeLine(0, "{");
+
+ modelFormatter.AppendCodeLine(1, "/// ");
+
+ if (modelSummary == null)
+ modelFormatter.AppendCodeLine(1, "/// Plain old CLR object (model) data class implementation.");
+ else
+ {
+ var summaryLines = modelSummary.Split(Environment.NewLine.ToCharArray());
+ foreach (var summaryLine in summaryLines) modelFormatter.AppendCodeLine(1, $"/// {summaryLine}");
+ }
+
+ modelFormatter.AppendCodeLine(1, "/// ");
+ modelFormatter.AppendCodeLine(1, $"public class {targetClassName}");
+ modelFormatter.AppendCodeLine(1, "{");
+ modelFormatter.AppendCodeLine(1);
+ modelFormatter.AppendCodeLine(1, "}");
+
+ modelFormatter.AppendCodeLine(0, "}");
+
+
+ VsDocument modelDoc = modelFolder == null
+ ? await modelProject.AddDocumentAsync($"{targetClassName}.cs", modelFormatter.ReturnSource().TrimStartEndLines())
+ : await modelFolder.AddDocumentAsync($"{targetClassName}.cs", modelFormatter.ReturnSource().TrimStartEndLines());
+
+ if (modelDoc == null) throw new CodeFactoryException($"Error occurred saving the model to the project.");
+
+ result = await modelDoc.GetCSharpSourceModelAsync();
+ }
+ catch (CodeFactoryException)
+ {
+ throw;
+ }
+ catch (Exception unhandledError)
+ {
+ _logger.Error("The following unhandled error occurred creating a model.", unhandledError);
+ throw new CodeFactoryException(
+ $"An unhandled error occurred while creating the model '{targetClassName}'. Check the code factory logs for details of what happened.");
+ }
+
+ return result;
+ }
+
+ ///
+ /// Updates the implementation of a plain old CLR object from the definition of a source class.
+ ///
+ /// CodeFactory automation framework used to trigger the refresh.
+ /// The source class model the model will be generated or updated from.
+ /// The target project the model will be created in.
+ /// The source model that holds the existing model implementation.
+ /// The default namespaces to be added as using statements if they do not exist in the model class.
+ /// Optional parameter that provides the details for how to format the name of the model class.
+ /// Optional parameter that determines the target folder the model is to be located in if not on the root of the project.
+ /// Optional parameter that sets the XML summary for the model class.
+ /// Optional parameter that determines if properties should converted to non nullable types, default value is true.
+ /// Optional parameter that maps source and target namespaces when creating the model, default value is null.
+ /// Optional parameter that calls a delegate to confirm the property should be included with the model class.
+ /// The loaded class model for the created model class.
+ /// Raised if required data is missing or a processing error has occurred.
+ private static async Task UpdateModelAsync(this IVsActions source, CsClass sourceClass,
+ VsProject modelProject, CsSource modelSource, List defaultNamespaces, NameManagement nameManagement = null,
+ VsProjectFolder modelFolder = null, string modelSummary = null, bool convertNullableTypes = true, List mappedNamespaces = null, UseSourcePropertyDelegate useSourceProperty = null)
+ {
+
+ if (source == null)
+ throw new CodeFactoryException(
+ "The CodeFactory automation was not provided cannot update the model class.");
+ if (sourceClass == null)
+ throw new CodeFactoryException("The source class was not provided cannot update the model class.");
+ if (modelSource == null)
+ throw new CodeFactoryException("The target model source was not provided cannot update the model class.");
+
+ var sourcePoco = modelSource;
+ var modelClass = sourcePoco.Classes.FirstOrDefault();
+
+ if (modelClass == null)
+ throw new CodeFactoryException(
+ $"The call information for the model could not be loaded, cannot update the model class from source class '{sourceClass.Name}'.");
+
+ var sourceProperties = useSourceProperty != null
+ ? sourceClass.Properties.Where(p => useSourceProperty(p)).ToList()
+ : sourceClass.Properties;
+
+ if (!sourceProperties.Any()) return modelClass;
+
+ var modelProperties = modelClass.Properties
+ .Where(p => p.HasGet & p.HasSet & p.Security == CsSecurity.Public & !p.IsStatic).ToList();
+
+ var addList = sourceProperties.Where(s => modelProperties.All(p => p.Name != s.Name)).ToList();
+ var checkList = sourceProperties.Where(s => modelProperties.Any(p => p.Name == s.Name)).ToList();
+ var removeList = new List();
+
+ bool hasChanges = false;
+
+ var modelMappedNamespaces = mappedNamespaces ?? new List();
+ modelMappedNamespaces.Add(new MapNamespace { Destination = modelClass.Namespace, Source = sourceClass.Namespace });
+
+ foreach (var sourceProperty in checkList)
+ {
+ var modelProperty = modelProperties.FirstOrDefault(p => p.Name == sourceProperty.Name);
+
+ if (modelProperty == null)
+ {
+ addList.Add(sourceProperty);
+ continue;
+ }
+
+ if (sourceProperty.PropertyType.GenerateCSharpTypeName(mappedNamespaces: modelMappedNamespaces)
+ != modelProperty.PropertyType.GenerateCSharpTypeName(mappedNamespaces: modelMappedNamespaces))
+ {
+ addList.Add(sourceProperty);
+ removeList.Add(modelProperty);
+ }
+ }
+
+ hasChanges = removeList.Any();
+ if (!hasChanges) hasChanges = addList.Any();
+
+ if (!hasChanges) return modelClass;
+
+ if (defaultNamespaces != null)
+ {
+ foreach (var defaultNamespace in defaultNamespaces)
+ {
+ modelSource = await modelSource.AddUsingStatementAsync(defaultNamespace.ReferenceNamespace,
+ defaultNamespace.Alias);
+ }
+ }
+
+ var propBuilder = new PropertyBuilderTransformNullableTypes(convertNullableTypes);
+
+ var modelUpdateManager = new SourceClassManager(modelSource, modelClass, source, mappedNamespaces: modelMappedNamespaces);
+
+ modelUpdateManager.LoadNamespaceManager();
+
+ //instead of removing, we need to exclude the remove from the add list.
+ addList = addList.Except(removeList).ToList();
+
+ foreach (var propertyToAdd in addList)
+ {
+ if (propertyToAdd == null) continue;
+
+ // Exclude properties with inverse attributes.
+ if (propertyToAdd.HasAttributes && propertyToAdd.Attributes.Any(a => a.Type.Name.ToLower() == "inversepropertyattribute")) continue;
+
+ var propertySyntax = propBuilder.BuildPropertyAsync(propertyToAdd, modelUpdateManager, 2).Result.TrimEndLines();
+ if (propertyToAdd.PropertyType.IsGeneric)
+ {
+ // Add suffix + prefix
+ var genericType = propertyToAdd.PropertyType.GenericTypes.FirstOrDefault().Name;
+ if (propertyToAdd.PropertyType.Name == "ICollection")
+ propertySyntax = propertySyntax.Replace($"<{genericType}>", $"<{nameManagement.AddPrefix}{genericType}{nameManagement.AddSuffix}>?");
+ }
+
+ if (propertyToAdd.Name == propertyToAdd.PropertyType.Name)
+ {
+ propertySyntax = propertySyntax.Replace($"{propertyToAdd.Name} {propertyToAdd.Name}", $"{nameManagement.AddPrefix}{propertyToAdd.Name}{nameManagement.AddSuffix}? {propertyToAdd.Name}");
+ }
+
+ if (propertySyntax != null && (!removeList.Any(x => x.Name == propertyToAdd.Name)) && !modelUpdateManager.Source.ToString().Contains($" {propertyToAdd.Name} "))
+ await modelUpdateManager.PropertiesAddAfterAsync(propertySyntax.TrimEndLines()).ConfigureAwait(false);
+ }
+ modelUpdateManager.Source.Classes.FirstOrDefault().ToString().TrimEndLines();
+ return modelUpdateManager.Container;
+ }
+
+ ///
+ /// Logic to check if a property should be included in a model class implementation.
+ ///
+ /// The property model to be evaluated.
+ /// True if the property should be used, false if not.
+ public delegate bool UseSourcePropertyDelegate(CsProperty source);
+ }
}
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/Node/Angular/LocalModelBuilder.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/Node/Angular/LocalModelBuilder.cs
new file mode 100644
index 0000000..78106b0
--- /dev/null
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/Node/Angular/LocalModelBuilder.cs
@@ -0,0 +1,53 @@
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs;
+using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.WinVs.Models.ProjectSystem;
+using System.Threading.Tasks;
+
+namespace CodeFactory.Automation.NDF.Logic.Node.Angular
+{
+ ///
+ /// Automation logic for creating and updated a local service within an angular project.
+ ///
+ public static class LocalModelBuilder
+ {
+ public static async Task RefreshAngularModel(this IVsActions source, CsClass controllerClass,
+ VsProject angularWebProject, string targetFileName, string targetClassName, VsProjectFolder modulesFolder = null, VsProjectFolder modelsFolder = null)
+ {
+ if (source == null)
+ throw new CodeFactoryException("CodeFactory automation was not provided cannot refresh the angular service.");
+
+ if (controllerClass == null)
+ throw new CodeFactoryException("Cannot load the controller class, cannot refresh the angular service.");
+
+ if (angularWebProject == null)
+ throw new CodeFactoryException("Cannot load the angular web project, cannot refresh the angular service.");
+
+ VsDocument modelsDocument = (await modelsFolder.FindTypscriptSourceByClassNameAsync(targetClassName))
+ ?? await source.CreateAngularModelAsync(controllerClass, angularWebProject, targetFileName, targetClassName, modulesFolder, modelsFolder);
+ return modelsDocument;
+ }
+
+ private static async Task CreateAngularModelAsync(this IVsActions source, CsClass modelClass,
+ VsProject angularWebProject, string targetFileName, string targetClassName, VsProjectFolder modulesFolder = null, VsProjectFolder modelsFolder = null)
+ {
+ if (source == null)
+ throw new CodeFactoryException("CodeFactory automation was not provided cannot refresh the angular service.");
+
+ if (modelClass == null)
+ throw new CodeFactoryException("Cannot load the model class, cannot refresh the angular service.");
+
+ if (angularWebProject == null)
+ throw new CodeFactoryException("Cannot load the angular web project, cannot refresh the angular service.");
+
+ // Convert the existing CSharp model class to Typescript
+ var typescriptModel = TypescriptConverter.ConvertCsModeltoTs(modelClass, targetClassName);
+
+ // Add the new Typescript document
+ var sourceDocument = (await modelsFolder.AddDocumentAsync($"{targetFileName}.ts", typescriptModel.TrimStartEndLines()))
+ ?? throw new CodeFactoryException($"Was not able to load the created angular service implementation for '{targetFileName}.ts'");
+
+ return sourceDocument;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/Node/Angular/LocalServiceBuilder.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/Node/Angular/LocalServiceBuilder.cs
new file mode 100644
index 0000000..a4cb549
--- /dev/null
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/Node/Angular/LocalServiceBuilder.cs
@@ -0,0 +1,171 @@
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs;
+using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.WinVs.Models.ProjectSystem;
+using System.Threading.Tasks;
+
+namespace CodeFactory.Automation.NDF.Logic.Node.Angular
+{
+ ///
+ /// Automation logic for creating and updated a local service within an angular project.
+ ///
+ public static class LocalServiceBuilder
+ {
+ public static async Task RefreshAngularService(this IVsActions source, CsClass controllerClass,
+ VsProject angularWebProject, VsProjectFolder modulesFolder = null, VsProjectFolder modelsFolder = null, VsProjectFolder servicesFolder = null)
+ {
+ if (source == null)
+ throw new CodeFactoryException("CodeFactory automation was not provided cannot refresh the angular service.");
+
+ if (controllerClass == null)
+ throw new CodeFactoryException("Cannot load the controller class, cannot refresh the angular service.");
+
+ if (angularWebProject == null)
+ throw new CodeFactoryException("Cannot load the angular web project, cannot refresh the angular service.");
+
+ // Build controller name
+ var controllerName = $"{controllerClass.Name.GenerateCSharpFormattedClassName().ToLower()}";
+
+ // Strip the suffix
+ var strippedName = controllerName.Replace("controller", "").GenerateCSharpProperCase();
+
+ // Get or create the service
+ var serviceFileName = $"{modulesFolder.Name}-{strippedName.ToLower()}-api.service";
+ var serviceClassName = $"{modulesFolder.Name}{strippedName}ApiService";
+ VsDocument serviceDocument = (await servicesFolder.FindTypscriptSourceByClassNameAsync(serviceClassName))
+ ?? await source.CreateAngularServiceAsync(controllerClass, angularWebProject, serviceFileName, serviceClassName, modulesFolder, modelsFolder, servicesFolder);
+
+ return serviceDocument;
+ }
+
+ private static async Task CreateAngularServiceAsync(this IVsActions source, CsClass controllerClass,
+ VsProject angularWebProject, string serviceFileName, string serviceClassName, VsProjectFolder modulesFolder = null, VsProjectFolder modelsFolder = null, VsProjectFolder servicesFolder = null)
+ {
+ if (source == null)
+ throw new CodeFactoryException("CodeFactory automation was not provided cannot refresh the angular service.");
+
+ if (controllerClass == null)
+ throw new CodeFactoryException("Cannot load the controller class, cannot refresh the angular service.");
+
+ if (angularWebProject == null)
+ throw new CodeFactoryException("Cannot load the angular web project, cannot refresh the angular service.");
+
+ var test = controllerClass.Methods[0].MethodType;
+ var sourceFormatter = new SourceFormatter();
+ sourceFormatter.AppendCodeLine(0, $"/* Autogenerated Angular Service */");
+ sourceFormatter.AppendCodeLine(0, "import { Injectable } from \"@angular/core\";");
+ sourceFormatter.AppendCodeLine(0, "import { HttpClient } from '@angular/common/http';");
+ sourceFormatter.AppendCodeLine(0, "import { Observable, map } from \"rxjs\";");
+
+ foreach (var method in controllerClass.Methods)
+ {
+ if (method.HasParameters)
+ {
+ foreach (var parameter in method.Parameters)
+ {
+ if (parameter.Name.ToLower().EndsWith("model") && !parameter.ParameterType.IsWellKnownType)
+ {
+ // Resolve the cs class model
+ var csClassModel = parameter.ParameterType.GetClassModel();
+
+ // Get the stripped Name
+ var strippedClassName = csClassModel.Name.Replace("Model", "").GenerateCSharpProperCase();
+
+ // Get or create the model
+ var modelClassName = $"{strippedClassName.ToKebabCase()}";
+ var modelFileName = $"{modulesFolder.Name}-{modelClassName}.model";
+ var importStatement = "import { " + strippedClassName + " }";
+
+ // Import the model reference if it hasn't already been imported
+ if (!sourceFormatter.ReturnSource().Contains(importStatement))
+ sourceFormatter.AppendCodeLine(0, importStatement + " from '../" + modelsFolder.Name + "/" + modelFileName + "';");
+ }
+ }
+ }
+ }
+
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(0, "@Injectable({ providedIn: 'root' })");
+ sourceFormatter.AppendCodeLine(0, "export class " + serviceClassName + " {");
+
+ // Add Constructor
+ sourceFormatter.AppendCodeLine(1, $"/* Constructor */");
+ sourceFormatter.AppendCodeLine(1, "constructor(private http: HttpClient) {");
+ sourceFormatter.AppendCodeLine(1, "}");
+ sourceFormatter.AppendCodeLine(0);
+
+ // Add Methods based-on the Controller in the BFF
+ foreach (var method in controllerClass.Methods)
+ {
+ // Skip if the method is not public
+ if (method.Security == CsSecurity.Public)
+ {
+ sourceFormatter.AppendCodeLine(0, $"/* Method to {method.Name.Prettify()} */");
+
+ // Get the action type
+ var action = "get";
+ // TODO Uncomment when ready to support posts
+ //if (method.HasAttributes && method.Attributes.Any(a => a.Type.Name == "HttpPostAttribute"))
+ //{
+ // action = "post";
+ //}
+
+ // Get the Return Type
+ var returnType = "";
+ if (method.ReturnType.HasStrongTypesInGenerics)
+ {
+ returnType = method.ReturnType.GenericTypes[0].Name;
+ if (returnType.EndsWith("Model"))
+ returnType = returnType.Replace("Model", "");
+ }
+ else
+ {
+ returnType = method.ReturnType.Name;
+ }
+
+ // Build Signature
+ var signatureParameters = "";
+ if (method.HasParameters)
+ {
+ // Build parameters
+ for (int i = 0; i < method.Parameters.Count; i++)
+ {
+ if(i > 0)
+ signatureParameters += ", ";
+
+ signatureParameters += $"{method.Parameters[i].Name}: {TypescriptConverter.ConvertToTypeScriptType(method.Parameters[i].ParameterType.Name)}";
+ }
+ }
+ sourceFormatter.AppendCodeLine(0, $"{method.Name.GenerateCSharpCamelCase()}({signatureParameters}): Observable<{TypescriptConverter.ConvertToTypeScriptType(returnType)}>");
+
+ // Add thie method body
+ sourceFormatter.AppendCodeLine(0, "{");
+ var urlParameters = "";
+ if (method.HasParameters)
+ {
+ // Build parameters
+ for (int i = 0; i < method.Parameters.Count; i++)
+ {
+ if (i > 0)
+ urlParameters += "&";
+ else
+ urlParameters += "?";
+
+ urlParameters += "{" + method.Parameters[i].Name + "}";
+ }
+ }
+ sourceFormatter.AppendCodeLine(1, "return this.http."+ action +"(`/api/v1/t1/"+ method.Name.ToLower() + "/$"+ urlParameters + "`).pipe(map(r => r as "+ returnType +"));");
+ sourceFormatter.AppendCodeLine(0, "}");
+ }
+ }
+
+ sourceFormatter.AppendCodeLine(0, "}");
+
+ // Add the document
+ var sourceDocument = (await servicesFolder.AddDocumentAsync($"{serviceFileName}.ts", sourceFormatter.ReturnSource().TrimStartEndLines()))
+ ?? throw new CodeFactoryException($"Was not able to load the created angular service implementation for '{serviceFileName}.ts'");
+
+ return sourceDocument;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/Node/Angular/TypescriptConverter.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/Node/Angular/TypescriptConverter.cs
new file mode 100644
index 0000000..93d4905
--- /dev/null
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/Node/Angular/TypescriptConverter.cs
@@ -0,0 +1,128 @@
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs.Models.CSharp;
+
+namespace CodeFactory.Automation.NDF.Logic.Node.Angular
+{
+ ///
+ /// Automation library that creates a model class that supports standard node angular implementation.
+ ///
+ public static class TypescriptConverter
+ {
+ //
+ // Converts a C# class model to an Angular model class in TypeScript.
+ //
+ // Parameters:
+ // - csharpModel: The C# class model to be converted.
+ //
+ // Returns:
+ // - A string representing the TypeScript model class.
+ //
+ public static string ConvertCsModeltoTs(CsClass csharpModel, string targetModelName)
+ {
+ // Create a StringBuilder to store the TypeScript model class.
+ var sourceFormatter = new SourceFormatter();
+ sourceFormatter.AppendCodeLine(0, $"/* Autogenerated model file */");
+ sourceFormatter.AppendCodeLine(0, $"export class {targetModelName}");
+ sourceFormatter.AppendCodeLine(0, "{");
+
+ // Iterate through each property of the C# model.
+ foreach (var property in csharpModel.Properties)
+ {
+ // Convert the C# property type to TypeScript.
+ string tsType = ConvertToTypeScriptType(property.PropertyType.Name);
+
+ // Append the TypeScript property to the model class.
+ sourceFormatter.AppendCodeLine(2, $"{property.Name}?: {tsType};");
+ }
+
+ // Add the default model.
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(1, "static default() {");
+ sourceFormatter.AppendCodeLine(2, $"const def = new {targetModelName}();");
+ foreach (var property in csharpModel.Properties)
+ {
+ sourceFormatter.AppendCodeLine(2, $"def.{property.Name} = {GetDefaultValueFromTypeScriptType(property.PropertyType.Name)};");
+ }
+
+ sourceFormatter.AppendCodeLine(1, "}");
+ sourceFormatter.AppendCodeLine(0, "}");
+
+ // Return the TypeScript model class.
+ return sourceFormatter.ReturnSource().TrimStartEndLines();
+ }
+
+ //
+ // Converts a C# property type to the equivalent TypeScript type.
+ //
+ // Parameters:
+ // - csharpType: The C# property type to be converted.
+ //
+ // Returns:
+ // - A string representing the equivalent TypeScript type.
+ //
+ public static string ConvertToTypeScriptType(string csharpType)
+ {
+ switch (csharpType.ToLower())
+ {
+ case "int":
+ case "int32":
+ case "int64":
+ case "long":
+ case "float":
+ case "decimal":
+ case "double":
+ return "number";
+ case "task":
+ case "list":
+ case "collection":
+ case "ienumeration":
+ return "any";
+ case "boolean":
+ return "boolean";
+ case "string":
+ return "string";
+ case "datetime":
+ return "Date";
+ default:
+ return csharpType;
+ }
+ }
+
+ //
+ // Gets a C# property type, Typescript equivelant, default value.
+ //
+ // Parameters:
+ // - csharpType: The C# property type.
+ //
+ // Returns:
+ // - A string representing the equivalent TypeScript type default value.
+ //
+ public static string GetDefaultValueFromTypeScriptType(string csharpType)
+ {
+ switch (csharpType.ToLower())
+ {
+ case "int":
+ case "int32":
+ case "int64":
+ case "long":
+ case "float":
+ case "decimal":
+ case "double":
+ return "0";
+ case "task":
+ case "list":
+ case "collection":
+ case "ienumerable":
+ return "{}";
+ case "boolean":
+ return "false";
+ case "string":
+ return "\"\"";
+ case "datetime":
+ return "new Date()";
+ default:
+ return "null";
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/ProjectExtensions.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/ProjectExtensions.cs
index 2e1285f..2699424 100644
--- a/src/Automation/CodeFactory.Automation.NDF.Logic/ProjectExtensions.cs
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/ProjectExtensions.cs
@@ -1,16 +1,13 @@
-using CodeFactory.WinVs.Models.ProjectSystem;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs.Models.ProjectSystem;
using System.Threading.Tasks;
-using CodeFactory.Automation.Standard.Logic;
+
namespace CodeFactory.Automation.NDF.Logic
{
- ///
- /// Extension methods that support the
- ///
- public static class ProjectExtensions
+ ///
+ /// Extension methods that support the
+ ///
+ public static class ProjectExtensions
{
///
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/Testing/BUnit/IntegrationTestBuilder.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/Testing/BUnit/IntegrationTestBuilder.cs
new file mode 100644
index 0000000..dcb8342
--- /dev/null
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/Testing/BUnit/IntegrationTestBuilder.cs
@@ -0,0 +1,462 @@
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs;
+using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.WinVs.Models.CSharp.Builder;
+using CodeFactory.WinVs.Models.ProjectSystem;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeFactory.Automation.NDF.Logic.Testing.BUnit
+{
+ ///
+ /// Automation logic that supports integration testing using the BUnit unit test framework.
+ ///
+ public static class IntegrationTestBuilder
+ {
+ ///
+ /// Automation to refresh the integration test implementation.
+ ///
+ /// CodeFactory automation access to Visual Studio.
+ /// The name of the test class to be refreshed.
+ /// The target contract to implement testing for.
+ /// The target project the target logic is implemented in.
+ ///
+ public static async Task RefreshIntegrationTestAsync(this IVsActions source, string testName, CsInterface contract, VsProject testProject)
+ {
+ if (source == null) throw new CodeFactoryException("Could not access the CodeFactory automation for visual studio cannot refresh the integration test.");
+
+ if (string.IsNullOrEmpty(testName)) throw new CodeFactoryException("No test name was provided cannot update the integration test.");
+
+ if (contract == null) throw new CodeFactoryException("No contract was provided cannot update the integration test.");
+
+ if (testProject == null) throw new CodeFactoryException($"No test project was provided cannot refresh the integration tests that support the contract '{contract.Name}'");
+
+ var isTestProject = await testProject.TestProjectIsConfiguredBUnitAsync(true);
+
+ await testProject.CreateTestLoaderAsync();
+
+ var testClassName = testName;
+
+ if (string.IsNullOrEmpty(testClassName)) throw new CodeFactoryException("Could not load the test class name. Cannot refresh the integration tests.");
+
+ var testSource = await testProject.FindCSharpSourceByClassNameAsync(testClassName, false);
+
+ if (testSource == null) await source.CreateTestAsync(contract, testProject, testClassName);
+ else await source.UpdateTestAsync(contract, testSource.SourceCode);
+
+ }
+
+ ///
+ /// Creates a new integration test;
+ ///
+ /// CodeFactory automation for Visual Studio.
+ /// The target interface to be tested.
+ /// The target project the test should be created in.
+ /// The name of the target test class.
+ /// Raised if required data is missing.
+ public static async Task CreateTestAsync(this IVsActions source, CsInterface contract, VsProject testProject, string testClassName)
+ {
+ if (source == null) throw new CodeFactoryException("Could not access the CodeFactory automation for visual studio cannot refresh the tests.");
+
+ if (contract == null) throw new CodeFactoryException("No contract was provided cannot create the tests.");
+
+ if (testProject == null) throw new CodeFactoryException($"No test project was provided cannot create the tests that support the contract '{contract.Name}'");
+
+ if (string.IsNullOrEmpty(testClassName)) throw new CodeFactoryException($"The test class name was not provided cannot create the tests that support the contract '{contract.Name}'");
+
+ SourceFormatter testFormatter = new SourceFormatter();
+
+ testFormatter.AppendCodeLine(0, "using System;");
+ testFormatter.AppendCodeLine(0, "using System.Collections.Generic;");
+ testFormatter.AppendCodeLine(0, "using System.Linq;");
+ testFormatter.AppendCodeLine(0, "using System.Linq.Expressions;");
+ testFormatter.AppendCodeLine(0, "using System.Text;");
+ testFormatter.AppendCodeLine(0, "using System.Threading.Tasks;");
+ testFormatter.AppendCodeLine(0, "using Xunit;");
+ testFormatter.AppendCodeLine(0, $"using {contract.Namespace};");
+ testFormatter.AppendCodeLine(0);
+
+ testFormatter.AppendCodeLine(0, $"namespace {testProject.DefaultNamespace}");
+ testFormatter.AppendCodeLine(0, "{");
+
+ testFormatter.AppendCodeLine(1, "/// ");
+ testFormatter.AppendCodeLine(1, $"/// Integration test class that tests the contract ");
+ testFormatter.AppendCodeLine(1, "/// ");
+ testFormatter.AppendCodeLine(1, $"public class {testClassName}");
+ testFormatter.AppendCodeLine(1, "{");
+
+ testFormatter.AppendCodeLine(2, "/// ");
+ testFormatter.AppendCodeLine(2, $"/// The contract being tested.");
+ testFormatter.AppendCodeLine(2, "/// ");
+ testFormatter.AppendCodeLine(2, $"private readonly {contract.Name} _contract;");
+ testFormatter.AppendCodeLine(0);
+ testFormatter.AppendCodeLine(2, "/// ");
+ testFormatter.AppendCodeLine(2, $"/// Creates a new instances of the intergration test class for testing.");
+ testFormatter.AppendCodeLine(2, "/// ");
+ testFormatter.AppendCodeLine(2, $"public {testClassName}()");
+ testFormatter.AppendCodeLine(2, "{");
+ testFormatter.AppendCodeLine(3, $"_contract = TestLoader.GetRequiredService<{contract.Name}>();");
+ testFormatter.AppendCodeLine(2, "}");
+ testFormatter.AppendCodeLine(0);
+
+ testFormatter.AppendCodeLine(1, "}");
+
+ testFormatter.AppendCodeLine(0, "}");
+
+ var doc = await testProject.AddDocumentAsync($"{testClassName}.cs", testFormatter.ReturnSource().TrimStartEndLines());
+
+ var testSourceCode = await doc.GetCSharpSourceModelAsync();
+
+ await source.UpdateTestAsync(contract, testSourceCode);
+ }
+
+ ///
+ /// Updates a test class that performs intergration tests on the target contract.
+ ///
+ /// CodeFactory automation for visual studio.
+ /// The target contract that is being tested.
+ /// The target source code that is to be updated.
+ /// Throw if required data is missing.
+ public static async Task UpdateTestAsync(this IVsActions source, CsInterface contract, CsSource testClassSource)
+ {
+ if (source == null) throw new CodeFactoryException("Could not access the CodeFactory automation for visual studio cannot refresh the integration tests.");
+
+ if (contract == null) throw new CodeFactoryException("No contract was provided cannot update the integration tests.");
+
+ if (testClassSource == null) throw new CodeFactoryException($"The test class source was not provided cannot update the integration tests that support the contract '{contract.Name}'");
+
+ var currentSource = testClassSource;
+
+ var testClass = currentSource.Classes.FirstOrDefault();
+
+ if (testClass == null) throw new CodeFactoryException($"The test class could not be loaded from the provided source. cannot update the integrationn tests that support the contract '{contract.Name}'");
+
+ var contractMethods = contract.GetAllInterfaceMethods();
+
+ if (!contractMethods.Any()) return;
+
+ var sourceMethods = testClass.Methods ?? new List();
+
+ var AddTests = new List();
+
+ foreach (var contractMethod in contractMethods)
+ {
+ var testMethodName = contractMethod.FormatTestMethodName();
+
+ if (string.IsNullOrEmpty(testMethodName)) continue;
+
+ if (!sourceMethods.Any(m => m.Name == testMethodName)) AddTests.Add(contractMethod);
+ }
+
+ if (!AddTests.Any()) return;
+
+ var sourceManager = new SourceClassManager(currentSource, testClass, source);
+
+ foreach (var addMethod in AddTests)
+ {
+ bool hasReturnType = !addMethod.IsVoid;
+ bool isAsync = false;
+ bool hasParameters = addMethod.HasParameters;
+ StringBuilder parameterBuilder = new StringBuilder();
+ var testMethodFormatter = new SourceFormatter();
+
+ if (hasReturnType)
+ {
+ isAsync = addMethod.ReturnType.IsTaskType();
+
+ if (isAsync) hasReturnType = !addMethod.ReturnType.IsTaskOnlyType();
+ }
+
+ testMethodFormatter.AppendCodeLine(0);
+ testMethodFormatter.AppendCodeLine(2, "/// ");
+ testMethodFormatter.AppendCodeLine(2, $"/// Integration test that tests the contract method \"{addMethod.Name}\"");
+ testMethodFormatter.AppendCodeLine(2, "/// ");
+ testMethodFormatter.AppendCodeLine(2, "[Fact]");
+
+ if (isAsync) testMethodFormatter.AppendCodeLine(2, $"public async Task {addMethod.FormatTestMethodName()}()");
+ else testMethodFormatter.AppendCodeLine(2, $"public void {addMethod.FormatTestMethodName()}()");
+ testMethodFormatter.AppendCodeLine(2, "{");
+ testMethodFormatter.AppendCodeLine(3, "//Arrange");
+
+ if (hasParameters)
+ {
+ bool firstParameter = true;
+
+ foreach (var testParameter in addMethod.Parameters)
+ {
+ if (firstParameter)
+ {
+ parameterBuilder.Append($"{testParameter.Name}");
+ firstParameter = false;
+ }
+ else parameterBuilder.Append($", {testParameter.Name}");
+
+ string defaultValue = testParameter.ParameterType.GenerateCSharpDefaultValue();
+ testMethodFormatter.AppendCodeLine(3,
+ defaultValue != null
+ ? $"{testParameter.ParameterType.GenerateCSharpTypeName()} {testParameter.Name} = new {testParameter.ParameterType.GenerateCSharpTypeName()}();"
+ : $"{testParameter.ParameterType.GenerateCSharpTypeName()} {testParameter.Name};");
+ testMethodFormatter.AppendCodeLine(0);
+ }
+ }
+ testMethodFormatter.AppendCodeLine(3, "using var ctx = new TestContext();");
+ testMethodFormatter.AppendCodeLine(0);
+
+ testMethodFormatter.AppendCodeLine(3, "try");
+ testMethodFormatter.AppendCodeLine(3, "{");
+
+ if (hasReturnType)
+ {
+ testMethodFormatter.AppendCodeLine(4, "//Act");
+ var returnType = isAsync ? addMethod.ReturnType.GenericParameters.First().Type : addMethod.ReturnType;
+
+ var defaultValue = returnType.GenerateCSharpDefaultValue();
+
+ testMethodFormatter.AppendCodeLine(4, defaultValue != null
+ ? $"{returnType.GenerateCSharpTypeName()}? result = new {returnType.GenerateCSharpTypeName()}();"
+ : $"{returnType.GenerateCSharpTypeName()} result;");
+ testMethodFormatter.AppendCodeLine(4);
+
+ }
+
+ string awaitStatement = isAsync ? " await " : " ";
+ string asyncStatement = isAsync ? "async" : "";
+
+ var methodParameters = hasParameters ? parameterBuilder.ToString() : "";
+
+ testMethodFormatter.AppendCodeLine(4, hasReturnType
+ ? $"result ={awaitStatement}_contract.{addMethod.Name}({methodParameters});"
+ : $"{awaitStatement}_contract.{addMethod.Name}({methodParameters});");
+ testMethodFormatter.AppendCodeLine(4);
+
+ if (hasReturnType)
+ {
+ testMethodFormatter.AppendCodeLine(4, "//Assert");
+ testMethodFormatter.AppendCodeLine(4, "Assert.NotNull(result);");
+ }
+
+ else
+ {
+ testMethodFormatter.AppendCodeLine(4, "//Act and Assert");
+ testMethodFormatter.AppendCodeLine(4, $"var ex = Assert.Throws(()=> {awaitStatement} _contract.{addMethod.Name}({methodParameters}));");
+ testMethodFormatter.AppendCodeLine(4, $"Assert.NotNull(ex);");
+ }
+
+ testMethodFormatter.AppendCodeLine(4);
+ testMethodFormatter.AppendCodeLine(3, "}");
+ testMethodFormatter.AppendCodeLine(3, "catch (Exception unhandled)");
+ testMethodFormatter.AppendCodeLine(3, "{");
+ testMethodFormatter.AppendCodeLine(4, "Assert.Fail($\"The following unhandled exception occurred '{unhandled.Message}' \");");
+ testMethodFormatter.AppendCodeLine(3, "}");
+
+ testMethodFormatter.AppendCodeLine(3, "finally");
+ testMethodFormatter.AppendCodeLine(3, "{");
+ testMethodFormatter.AppendCodeLine(4, "//Cleanup");
+ testMethodFormatter.AppendCodeLine(4, "// Add any cleanup code if necessary");
+ testMethodFormatter.AppendCodeLine(3, "}");
+ testMethodFormatter.AppendCodeLine(0);
+
+ testMethodFormatter.AppendCodeLine(2, "}");
+
+ await sourceManager.ConstructorsAddAfterAsync(testMethodFormatter.ReturnSource());
+
+ testMethodFormatter.ResetFormatter();
+ }
+
+ return;
+ }
+
+ ///
+ /// Formats the repositories test class name.
+ ///
+ /// Source interface to convert to the test class name.
+ /// Formatted test class name or null if it was not found.
+ private static string FormatTestClassName(this CsInterface source)
+ {
+ if (source == null) return null;
+
+ return $"{source.Name.GenerateCSharpFormattedClassName()}Test";
+ }
+
+ ///
+ /// Formats the name of the test method for the contract.
+ ///
+ /// Method model to generate the test method name from.
+ /// Formatted method name or null if it is not found.
+ private static string FormatTestMethodName(this CsMethod source)
+ {
+ if (source == null) return null;
+
+ if (!source.HasParameters) return source.Name;
+
+ StringBuilder testMethodBuilder = new StringBuilder();
+
+ testMethodBuilder.Append($"{source.Name}By");
+
+ foreach (var parameter in source.Parameters)
+ testMethodBuilder.Append(parameter.Name.GenerateCSharpProperCase());
+
+ return testMethodBuilder.ToString();
+ }
+
+ ///
+ /// Creates a new instance of the 'TestLoader' class.
+ ///
+ /// Target project to add the test loader to.
+ public static async Task CreateTestLoaderAsync(this VsProject testProject)
+ {
+ var sourceFile = await testProject.FindCSharpSourceByClassNameAsync("TestLoader", false);
+
+ if (sourceFile != null) return;
+
+ var sourceFormatter = new SourceFormatter();
+
+ sourceFormatter.AppendCodeLine(0, "using Microsoft.Extensions.Configuration;");
+ sourceFormatter.AppendCodeLine(0, "using Microsoft.Extensions.DependencyInjection;");
+ sourceFormatter.AppendCodeLine(0, "using Microsoft.Extensions.Logging;");
+ sourceFormatter.AppendCodeLine(0, "using Microsoft.Extensions.Logging.Abstractions;");
+ sourceFormatter.AppendCodeLine(0, "using System;");
+ sourceFormatter.AppendCodeLine(0, "using System.Collections.Generic;");
+ sourceFormatter.AppendCodeLine(0, "using System.Linq;");
+ sourceFormatter.AppendCodeLine(0, "using System.Text;");
+ sourceFormatter.AppendCodeLine(0, "using System.Threading.Tasks;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(0, $"namespace {testProject.DefaultNamespace}");
+ sourceFormatter.AppendCodeLine(0, "{");
+ sourceFormatter.AppendCodeLine(1, "/// ");
+ sourceFormatter.AppendCodeLine(1, "/// Loader class that is used by testing to load required services.");
+ sourceFormatter.AppendCodeLine(1, "/// ");
+ sourceFormatter.AppendCodeLine(1, "public static class TestLoader");
+ sourceFormatter.AppendCodeLine(1, "{");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Backing field for the property.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "private static readonly IConfiguration _configuration;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Backing field for the property.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "private static readonly IServiceProvider _serviceProvider;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Logging factory used for testing");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "private static readonly ILoggerFactory _loggerFactory;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Constructor that gets called when the class is accessed for the first time.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "static TestLoader()");
+ sourceFormatter.AppendCodeLine(2, "{");
+ sourceFormatter.AppendCodeLine(3, "//Loading the configuration");
+ sourceFormatter.AppendCodeLine(3, "var configuration = new ConfigurationBuilder()");
+ sourceFormatter.AppendCodeLine(3, " .AddJsonFile(\"appsettings.local.json\", true)");
+ sourceFormatter.AppendCodeLine(3, " .AddEnvironmentVariables().Build();");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "//Setting the config property.");
+ sourceFormatter.AppendCodeLine(3, "_configuration = configuration;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "//Creating the service container");
+ sourceFormatter.AppendCodeLine(3, "var services = new ServiceCollection();");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "//Adding access to configuration from the service container");
+ sourceFormatter.AppendCodeLine(3, "services.AddSingleton(Configuration);");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "//Loading the libraries into the service collection.");
+ sourceFormatter.AppendCodeLine(3, "LoadLibraries(services, _configuration);");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "_loggerFactory = new NullLoggerFactory();");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "services.AddSingleton(_loggerFactory);");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "services.AddLogging();");
+ sourceFormatter.AppendCodeLine(3, "//Building the service provider.");
+ sourceFormatter.AppendCodeLine(3, "_serviceProvider = services.BuildServiceProvider();");
+ sourceFormatter.AppendCodeLine(0);
+
+ sourceFormatter.AppendCodeLine(2, "}");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// The loaded configuration to be used with testing.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "public static IConfiguration Configuration => _configuration;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Service provider for the loaded dependency configuration.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "public static IServiceProvider ServiceProvider => _serviceProvider;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Gets the required service to use with testing.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Type of the service to be loaded.");
+ sourceFormatter.AppendCodeLine(2, "/// Instance of the service");
+ sourceFormatter.AppendCodeLine(2, "public static T GetRequiredService() where T : notnull");
+ sourceFormatter.AppendCodeLine(2, "{");
+ sourceFormatter.AppendCodeLine(3, "return _serviceProvider.GetRequiredService();");
+
+ sourceFormatter.AppendCodeLine(2, "}");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Loads the libraries into service collection");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Service collection to load.");
+ sourceFormatter.AppendCodeLine(2, "/// The configuration to be provided to services.");
+ sourceFormatter.AppendCodeLine(2, "public static void LoadLibraries(IServiceCollection services, IConfiguration configuration)");
+ sourceFormatter.AppendCodeLine(2, "{");
+ sourceFormatter.AppendCodeLine(3, "// Load Default Library Loader.");
+ sourceFormatter.AppendCodeLine(3, "var libraryLoader = new LibraryLoader();");
+ sourceFormatter.AppendCodeLine(3, "libraryLoader.Load(services, configuration);");
+ sourceFormatter.AppendCodeLine(2, "}");
+
+
+ sourceFormatter.AppendCodeLine(1, "}");
+ sourceFormatter.AppendCodeLine(0, "}");
+
+ await testProject.AddDocumentAsync("TestLoader.cs", sourceFormatter.ReturnSource().TrimStartEndLines());
+ }
+
+ ///
+ /// Helper method that checks a project to make sure all required project references exist before building a test.
+ ///
+ /// Project to check.
+ /// Optional flag that determines if an exception should be thrown if the project is not configred, default is false.
+ /// True if configured with BUnit Tests, false if not.
+ /// Thrown if required data is missing.
+ public static async Task TestProjectIsConfiguredBUnitAsync(this VsProject project, bool throwError = false)
+ {
+ if (project == null) throw new CodeFactoryException("No test project was provided cannot build integration tests.");
+
+ var projectRefs = await project.GetProjectReferencesAsync();
+
+ if (!projectRefs.Any(r => r.Name == "Microsoft.Extensions.Logging.Abstractions"))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'Microsoft.Extensions.Logging.Abstractions'");
+ return false;
+ }
+ if (!projectRefs.Any(r => r.Name == "Microsoft.Extensions.Configuration"))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'Microsoft.Extensions.Configuration'");
+ return false;
+ }
+ if (!projectRefs.Any(r => r.Name == "Microsoft.Extensions.DependencyInjection"))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'Microsoft.Extensions.DependencyInjection'");
+ return false;
+ }
+ if (!projectRefs.Any(r => r.Name == "Microsoft.VisualStudio.TestPlatform.ObjectModel"))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'Microsoft.VisualStudio.TestPlatform.ObjectModel'");
+ return false;
+ }
+ if (!projectRefs.Any(r => r.Name.ToLower().Contains("bunit.")))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'bunit'");
+ return false;
+ }
+ return true;
+ }
+ }
+}
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/Testing/MSTest/IntegrationTestBuilder.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/Testing/MSTest/IntegrationTestBuilder.cs
index 325bb37..3ebf1c0 100644
--- a/src/Automation/CodeFactory.Automation.NDF.Logic/Testing/MSTest/IntegrationTestBuilder.cs
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/Testing/MSTest/IntegrationTestBuilder.cs
@@ -1,19 +1,19 @@
-using CodeFactory.WinVs.Models.CSharp.Builder;
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs;
using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.WinVs.Models.CSharp.Builder;
using CodeFactory.WinVs.Models.ProjectSystem;
-using CodeFactory.WinVs;
-using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
-using CodeFactory.Automation.Standard.Logic;
+
namespace CodeFactory.Automation.NDF.Logic.Testing.MSTest
{
- ///
- /// Automation logic that supports integration testing using the MSTest unit test framework.
- ///
- public static class IntegrationTestBuilder
+ ///
+ /// Automation logic that supports integration testing using the MSTest unit test framework.
+ ///
+ public static class IntegrationTestBuilder
{
///
/// Automation to refresh the integration test implementation.
@@ -33,7 +33,7 @@ public static async Task RefreshMSTestIntegrationTestAsync(this IVsActions sourc
if (testProject == null) throw new CodeFactoryException($"No test project was provided cannot refresh the integration tests that support the contract '{contract.Name}'");
- var isTestProject = await testProject.TestProjectIsConfiguredAsync(true);
+ var isTestProject = await testProject.TestProjectIsConfiguredMSTestAsync(true);
await testProject.CreateTestLoaderAsync();
@@ -398,7 +398,7 @@ public static async Task CreateTestLoaderAsync(this VsProject testProject)
/// Optional flag that determines if an exception should be thrown if the project is not configred, default is false.
/// True if configured false if not.
/// Thrown if required data is missing.
- public static async Task TestProjectIsConfiguredAsync(this VsProject project, bool throwError = false)
+ public static async Task TestProjectIsConfiguredMSTestAsync(this VsProject project, bool throwError = false)
{
if (project == null) throw new CodeFactoryException("No test project was provided cannot build integration tests.");
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/Testing/NUnit/IntegrationTestBuilder.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/Testing/NUnit/IntegrationTestBuilder.cs
new file mode 100644
index 0000000..4e3a0fe
--- /dev/null
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/Testing/NUnit/IntegrationTestBuilder.cs
@@ -0,0 +1,461 @@
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs;
+using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.WinVs.Models.CSharp.Builder;
+using CodeFactory.WinVs.Models.ProjectSystem;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeFactory.Automation.NDF.Logic.Testing.NUnit
+{
+ ///
+ /// Automation logic that supports integration testing using the NUnit unit test framework.
+ ///
+ public static class IntegrationTestBuilder
+ {
+ ///
+ /// Automation to refresh the integration test implementation.
+ ///
+ /// CodeFactory automation access to Visual Studio.
+ /// The name of the test class to be refreshed.
+ /// The target contract to implement testing for.
+ /// The target project the target logic is implemented in.
+ ///
+ public static async Task RefreshIntegrationTestAsync(this IVsActions source, string testName, CsInterface contract, VsProject testProject)
+ {
+ if (source == null) throw new CodeFactoryException("Could not access the CodeFactory automation for visual studio cannot refresh the integration test.");
+
+ if (string.IsNullOrEmpty(testName)) throw new CodeFactoryException("No test name was provided cannot update the integration test.");
+
+ if (contract == null) throw new CodeFactoryException("No contract was provided cannot update the integration test.");
+
+ if (testProject == null) throw new CodeFactoryException($"No test project was provided cannot refresh the integration tests that support the contract '{contract.Name}'");
+
+ var isTestProject = await testProject.TestProjectIsConfiguredNUnitAsync(true);
+
+ await testProject.CreateTestLoaderAsync();
+
+ var testClassName = testName;
+
+ if (string.IsNullOrEmpty(testClassName)) throw new CodeFactoryException("Could not load the test class name. Cannot refresh the integration tests.");
+
+ var testSource = await testProject.FindCSharpSourceByClassNameAsync(testClassName, false);
+
+ if (testSource == null) await source.CreateTestAsync(contract, testProject, testClassName);
+ else await source.UpdateTestAsync(contract, testSource.SourceCode);
+
+ }
+
+ ///
+ /// Creates a new integration test;
+ ///
+ /// CodeFactory automation for Visual Studio.
+ /// The target interface to be tested.
+ /// The target project the test should be created in.
+ /// The name of the target test class.
+ /// Raised if required data is missing.
+ public static async Task CreateTestAsync(this IVsActions source, CsInterface contract, VsProject testProject, string testClassName)
+ {
+ if (source == null) throw new CodeFactoryException("Could not access the CodeFactory automation for visual studio cannot refresh the tests.");
+
+ if (contract == null) throw new CodeFactoryException("No contract was provided cannot create the tests.");
+
+ if (testProject == null) throw new CodeFactoryException($"No test project was provided cannot create the tests that support the contract '{contract.Name}'");
+
+ if (string.IsNullOrEmpty(testClassName)) throw new CodeFactoryException($"The test class name was not provided cannot create the tests that support the contract '{contract.Name}'");
+
+ SourceFormatter testFormatter = new SourceFormatter();
+
+ testFormatter.AppendCodeLine(0, "using System;");
+ testFormatter.AppendCodeLine(0, "using System.Collections.Generic;");
+ testFormatter.AppendCodeLine(0, "using System.Linq;");
+ testFormatter.AppendCodeLine(0, "using System.Linq.Expressions;");
+ testFormatter.AppendCodeLine(0, "using System.Text;");
+ testFormatter.AppendCodeLine(0, "using System.Threading.Tasks;");
+ testFormatter.AppendCodeLine(0, "using Microsoft.VisualStudio.TestTools.UnitTesting;");
+ testFormatter.AppendCodeLine(0, $"using {contract.Namespace};");
+ testFormatter.AppendCodeLine(0);
+
+ testFormatter.AppendCodeLine(0, $"namespace {testProject.DefaultNamespace}");
+ testFormatter.AppendCodeLine(0, "{");
+
+ testFormatter.AppendCodeLine(1, "/// ");
+ testFormatter.AppendCodeLine(1, $"/// Integration test class that tests the contract ");
+ testFormatter.AppendCodeLine(1, "/// ");
+ testFormatter.AppendCodeLine(1, "[TestFixture]");
+ testFormatter.AppendCodeLine(1, $"public class {testClassName}");
+ testFormatter.AppendCodeLine(1, "{");
+
+ testFormatter.AppendCodeLine(2, "/// ");
+ testFormatter.AppendCodeLine(2, $"/// The contract being tested.");
+ testFormatter.AppendCodeLine(2, "/// ");
+ testFormatter.AppendCodeLine(2, $"private readonly {contract.Name} _contract;");
+ testFormatter.AppendCodeLine(0);
+ testFormatter.AppendCodeLine(2, "/// ");
+ testFormatter.AppendCodeLine(2, $"/// Creates a new instances of the intergration test class for testing.");
+ testFormatter.AppendCodeLine(2, "/// ");
+ testFormatter.AppendCodeLine(2, $"public {testClassName}()");
+ testFormatter.AppendCodeLine(2, "{");
+ testFormatter.AppendCodeLine(3, $"_contract = TestLoader.GetRequiredService<{contract.Name}>();");
+ testFormatter.AppendCodeLine(2, "}");
+ testFormatter.AppendCodeLine(0);
+
+ testFormatter.AppendCodeLine(1, "}");
+
+ testFormatter.AppendCodeLine(0, "}");
+
+ var doc = await testProject.AddDocumentAsync($"{testClassName}.cs", testFormatter.ReturnSource().TrimStartEndLines());
+
+ var testSourceCode = await doc.GetCSharpSourceModelAsync();
+
+ await source.UpdateTestAsync(contract, testSourceCode);
+ }
+
+ ///
+ /// Updates a test class that performs intergration tests on the target contract.
+ ///
+ /// CodeFactory automation for visual studio.
+ /// The target contract that is being tested.
+ /// The target source code that is to be updated.
+ /// Throw if required data is missing.
+ public static async Task UpdateTestAsync(this IVsActions source, CsInterface contract, CsSource testClassSource)
+ {
+ if (source == null) throw new CodeFactoryException("Could not access the CodeFactory automation for visual studio cannot refresh the integration tests.");
+
+ if (contract == null) throw new CodeFactoryException("No contract was provided cannot update the integration tests.");
+
+ if (testClassSource == null) throw new CodeFactoryException($"The test class source was not provided cannot update the integration tests that support the contract '{contract.Name}'");
+
+ var currentSource = testClassSource;
+
+ var testClass = currentSource.Classes.FirstOrDefault();
+
+ if (testClass == null) throw new CodeFactoryException($"The test class could not be loaded from the provided source. cannot update the integrationn tests that support the contract '{contract.Name}'");
+
+ var contractMethods = contract.GetAllInterfaceMethods();
+
+ if (!contractMethods.Any()) return;
+
+ var sourceMethods = testClass.Methods ?? new List();
+
+ var AddTests = new List();
+
+ foreach (var contractMethod in contractMethods)
+ {
+ var testMethodName = contractMethod.FormatTestMethodName();
+
+ if (string.IsNullOrEmpty(testMethodName)) continue;
+
+ if (!sourceMethods.Any(m => m.Name == testMethodName)) AddTests.Add(contractMethod);
+ }
+
+ if (!AddTests.Any()) return;
+
+ var sourceManager = new SourceClassManager(currentSource, testClass, source);
+
+ foreach (var addMethod in AddTests)
+ {
+ bool hasReturnType = !addMethod.IsVoid;
+ bool isAsync = false;
+ bool hasParameters = addMethod.HasParameters;
+ StringBuilder parameterBuilder = new StringBuilder();
+ var testMethodFormatter = new SourceFormatter();
+
+ if (hasReturnType)
+ {
+ isAsync = addMethod.ReturnType.IsTaskType();
+
+ if (isAsync) hasReturnType = !addMethod.ReturnType.IsTaskOnlyType();
+ }
+
+ testMethodFormatter.AppendCodeLine(0);
+ testMethodFormatter.AppendCodeLine(2, "/// ");
+ testMethodFormatter.AppendCodeLine(2, $"/// Integration test that tests the contract method \"{addMethod.Name}\"");
+ testMethodFormatter.AppendCodeLine(2, "/// ");
+ testMethodFormatter.AppendCodeLine(2, "[Test]");
+
+ if (isAsync) testMethodFormatter.AppendCodeLine(2, $"public async Task {addMethod.FormatTestMethodName()}()");
+ else testMethodFormatter.AppendCodeLine(2, $"public void {addMethod.FormatTestMethodName()}()");
+ testMethodFormatter.AppendCodeLine(2, "{");
+ testMethodFormatter.AppendCodeLine(3, "//Arrange");
+
+ if (hasParameters)
+ {
+ bool firstParameter = true;
+
+ foreach (var testParameter in addMethod.Parameters)
+ {
+ if (firstParameter)
+ {
+ parameterBuilder.Append($"{testParameter.Name}");
+ firstParameter = false;
+ }
+ else parameterBuilder.Append($", {testParameter.Name}");
+
+ string defaultValue = testParameter.ParameterType.GenerateCSharpDefaultValue();
+ testMethodFormatter.AppendCodeLine(3,
+ defaultValue != null
+ ? $"{testParameter.ParameterType.GenerateCSharpTypeName()} {testParameter.Name} = new {testParameter.ParameterType.GenerateCSharpTypeName()}();"
+ : $"{testParameter.ParameterType.GenerateCSharpTypeName()} {testParameter.Name};");
+ testMethodFormatter.AppendCodeLine(0);
+ }
+ }
+
+ testMethodFormatter.AppendCodeLine(3, "try");
+ testMethodFormatter.AppendCodeLine(3, "{");
+
+ if (hasReturnType)
+ {
+ testMethodFormatter.AppendCodeLine(4, "//Act");
+ var returnType = isAsync ? addMethod.ReturnType.GenericParameters.First().Type : addMethod.ReturnType;
+
+ var defaultValue = returnType.GenerateCSharpDefaultValue();
+
+ testMethodFormatter.AppendCodeLine(4, defaultValue != null
+ ? $"{returnType.GenerateCSharpTypeName()}? result = new {returnType.GenerateCSharpTypeName()}();"
+ : $"{returnType.GenerateCSharpTypeName()} result;");
+ testMethodFormatter.AppendCodeLine(4);
+
+ }
+
+ string awaitStatement = isAsync ? " await " : " ";
+ string asyncStatement = isAsync ? "async" : "";
+
+ var methodParameters = hasParameters ? parameterBuilder.ToString() : "";
+
+ testMethodFormatter.AppendCodeLine(4, hasReturnType
+ ? $"result ={awaitStatement}_contract.{addMethod.Name}({methodParameters});"
+ : $"{awaitStatement}_contract.{addMethod.Name}({methodParameters});");
+ testMethodFormatter.AppendCodeLine(4);
+
+ if (hasReturnType)
+ {
+ testMethodFormatter.AppendCodeLine(4, "//Assert");
+ testMethodFormatter.AppendCodeLine(4, "Assert.IsNotNull(result);");
+ }
+
+ else
+ {
+ testMethodFormatter.AppendCodeLine(4, "//Act and Assert");
+ testMethodFormatter.AppendCodeLine(4, $"await Assert.ThrowsAsync({asyncStatement} ()=> {awaitStatement} _contract.{addMethod.Name}({methodParameters}));");
+ }
+
+ testMethodFormatter.AppendCodeLine(4);
+ testMethodFormatter.AppendCodeLine(3, "}");
+
+ testMethodFormatter.AppendCodeLine(3, "catch (Exception unhandled)");
+ testMethodFormatter.AppendCodeLine(3, "{");
+ testMethodFormatter.AppendCodeLine(4, "Assert.Fail($\"The following unhandled exception occurred '{unhandled.Message}' \");");
+ testMethodFormatter.AppendCodeLine(3, "}");
+
+ testMethodFormatter.AppendCodeLine(3, "finally");
+ testMethodFormatter.AppendCodeLine(3, "{");
+ testMethodFormatter.AppendCodeLine(4, "//Cleanup");
+ testMethodFormatter.AppendCodeLine(4, "// Add any cleanup code if necessary");
+ testMethodFormatter.AppendCodeLine(3, "}");
+ testMethodFormatter.AppendCodeLine(0);
+
+ testMethodFormatter.AppendCodeLine(2, "}");
+
+ await sourceManager.ConstructorsAddAfterAsync(testMethodFormatter.ReturnSource());
+
+ testMethodFormatter.ResetFormatter();
+ }
+
+ return;
+ }
+
+ ///
+ /// Formats the repositories test class name.
+ ///
+ /// Source interface to convert to the test class name.
+ /// Formatted test class name or null if it was not found.
+ private static string FormatTestClassName(this CsInterface source)
+ {
+ if (source == null) return null;
+
+ return $"{source.Name.GenerateCSharpFormattedClassName()}Test";
+ }
+
+ ///
+ /// Formats the name of the test method for the contract.
+ ///
+ /// Method model to generate the test method name from.
+ /// Formatted method name or null if it is not found.
+ private static string FormatTestMethodName(this CsMethod source)
+ {
+ if (source == null) return null;
+
+ if (!source.HasParameters) return source.Name;
+
+ StringBuilder testMethodBuilder = new StringBuilder();
+
+ testMethodBuilder.Append($"{source.Name}By");
+
+ foreach (var parameter in source.Parameters)
+ testMethodBuilder.Append(parameter.Name.GenerateCSharpProperCase());
+
+ return testMethodBuilder.ToString();
+ }
+
+ ///
+ /// Creates a new instance of the 'TestLoader' class.
+ ///
+ /// Target project to add the test loader to.
+ public static async Task CreateTestLoaderAsync(this VsProject testProject)
+ {
+ var sourceFile = await testProject.FindCSharpSourceByClassNameAsync("TestLoader", false);
+
+ if (sourceFile != null) return;
+
+ var sourceFormatter = new SourceFormatter();
+
+ sourceFormatter.AppendCodeLine(0, "using Microsoft.Extensions.Configuration;");
+ sourceFormatter.AppendCodeLine(0, "using Microsoft.Extensions.DependencyInjection;");
+ sourceFormatter.AppendCodeLine(0, "using Microsoft.Extensions.Logging;");
+ sourceFormatter.AppendCodeLine(0, "using Microsoft.Extensions.Logging.Abstractions;");
+ sourceFormatter.AppendCodeLine(0, "using System;");
+ sourceFormatter.AppendCodeLine(0, "using System.Collections.Generic;");
+ sourceFormatter.AppendCodeLine(0, "using System.Linq;");
+ sourceFormatter.AppendCodeLine(0, "using System.Text;");
+ sourceFormatter.AppendCodeLine(0, "using System.Threading.Tasks;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(0, $"namespace {testProject.DefaultNamespace}");
+ sourceFormatter.AppendCodeLine(0, "{");
+ sourceFormatter.AppendCodeLine(1, "/// ");
+ sourceFormatter.AppendCodeLine(1, "/// Loader class that is used by testing to load required services.");
+ sourceFormatter.AppendCodeLine(1, "/// ");
+ sourceFormatter.AppendCodeLine(1, "public static class TestLoader");
+ sourceFormatter.AppendCodeLine(1, "{");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Backing field for the property.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "private static readonly IConfiguration _configuration;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Backing field for the property.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "private static readonly IServiceProvider _serviceProvider;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Logging factory used for testing");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "private static readonly ILoggerFactory _loggerFactory;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Constructor that gets called when the class is accessed for the first time.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "static TestLoader()");
+ sourceFormatter.AppendCodeLine(2, "{");
+ sourceFormatter.AppendCodeLine(3, "//Loading the configuration");
+ sourceFormatter.AppendCodeLine(3, "var configuration = new ConfigurationBuilder()");
+ sourceFormatter.AppendCodeLine(3, " .AddJsonFile(\"appsettings.local.json\", true)");
+ sourceFormatter.AppendCodeLine(3, " .AddEnvironmentVariables().Build();");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "//Setting the config property.");
+ sourceFormatter.AppendCodeLine(3, "_configuration = configuration;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "//Creating the service container");
+ sourceFormatter.AppendCodeLine(3, "var services = new ServiceCollection();");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "//Adding access to configuration from the service container");
+ sourceFormatter.AppendCodeLine(3, "services.AddSingleton(Configuration);");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "//Loading the libraries into the service collection.");
+ sourceFormatter.AppendCodeLine(3, "LoadLibraries(services, _configuration);");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "_loggerFactory = new NullLoggerFactory();");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "services.AddSingleton(_loggerFactory);");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "services.AddLogging();");
+ sourceFormatter.AppendCodeLine(3, "//Building the service provider.");
+ sourceFormatter.AppendCodeLine(3, "_serviceProvider = services.BuildServiceProvider();");
+ sourceFormatter.AppendCodeLine(0);
+
+ sourceFormatter.AppendCodeLine(2, "}");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// The loaded configuration to be used with testing.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "public static IConfiguration Configuration => _configuration;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Service provider for the loaded dependency configuration.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "public static IServiceProvider ServiceProvider => _serviceProvider;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Gets the required service to use with testing.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Type of the service to be loaded.");
+ sourceFormatter.AppendCodeLine(2, "/// Instance of the service");
+ sourceFormatter.AppendCodeLine(2, "public static T GetRequiredService() where T : notnull");
+ sourceFormatter.AppendCodeLine(2, "{");
+ sourceFormatter.AppendCodeLine(3, "return _serviceProvider.GetRequiredService();");
+
+ sourceFormatter.AppendCodeLine(2, "}");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Loads the libraries into service collection");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Service collection to load.");
+ sourceFormatter.AppendCodeLine(2, "/// The configuration to be provided to services.");
+ sourceFormatter.AppendCodeLine(2, "public static void LoadLibraries(IServiceCollection services, IConfiguration configuration)");
+ sourceFormatter.AppendCodeLine(2, "{");
+ sourceFormatter.AppendCodeLine(3, "// Load Default Library Loader.");
+ sourceFormatter.AppendCodeLine(3, "var libraryLoader = new LibraryLoader();");
+ sourceFormatter.AppendCodeLine(3, "libraryLoader.Load(services, configuration);");
+ sourceFormatter.AppendCodeLine(2, "}");
+
+
+ sourceFormatter.AppendCodeLine(1, "}");
+ sourceFormatter.AppendCodeLine(0, "}");
+
+ await testProject.AddDocumentAsync("TestLoader.cs", sourceFormatter.ReturnSource().TrimStartEndLines());
+ }
+
+ ///
+ /// Helper method that checks a project to make sure all required project references exist before building a test.
+ ///
+ /// Project to check.
+ /// Optional flag that determines if an exception should be thrown if the project is not configred, default is false.
+ /// True if configured with NUnit Tests, false if not.
+ /// Thrown if required data is missing.
+ public static async Task TestProjectIsConfiguredNUnitAsync(this VsProject project, bool throwError = false)
+ {
+ if (project == null) throw new CodeFactoryException("No test project was provided cannot build integration tests.");
+
+ var projectRefs = await project.GetProjectReferencesAsync();
+
+ if (!projectRefs.Any(r => r.Name == "Microsoft.Extensions.Logging.Abstractions"))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'Microsoft.Extensions.Logging.Abstractions'");
+ return false;
+ }
+ if (!projectRefs.Any(r => r.Name == "Microsoft.Extensions.Configuration"))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'Microsoft.Extensions.Configuration'");
+ return false;
+ }
+ if (!projectRefs.Any(r => r.Name == "Microsoft.Extensions.DependencyInjection"))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'Microsoft.Extensions.DependencyInjection'");
+ return false;
+ }
+ if (!projectRefs.Any(r => r.Name == "Microsoft.VisualStudio.TestPlatform.ObjectModel"))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'Microsoft.VisualStudio.TestPlatform.ObjectModel'");
+ return false;
+ }
+ if (!projectRefs.Any(r => r.Name.ToLower().Contains("nunit.")))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'NUnit'");
+ return false;
+ }
+ return true;
+ }
+ }
+}
diff --git a/src/Automation/CodeFactory.Automation.NDF.Logic/Testing/XUnit/IntegrationTestBuilder.cs b/src/Automation/CodeFactory.Automation.NDF.Logic/Testing/XUnit/IntegrationTestBuilder.cs
new file mode 100644
index 0000000..5eb2bb9
--- /dev/null
+++ b/src/Automation/CodeFactory.Automation.NDF.Logic/Testing/XUnit/IntegrationTestBuilder.cs
@@ -0,0 +1,573 @@
+using CodeFactory.Automation.NDF.Logic.AspNetCore.Service.Rest.Json;
+using CodeFactory.Automation.Standard.Logic.Extensions;
+using CodeFactory.WinVs;
+using CodeFactory.WinVs.Models.CSharp;
+using CodeFactory.WinVs.Models.CSharp.Builder;
+using CodeFactory.WinVs.Models.ProjectSystem;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CodeFactory.Automation.NDF.Logic.Testing.XUnit
+{
+ ///
+ /// Automation logic that supports integration testing using the XUnit unit test framework.
+ ///
+ public static class IntegrationTestBuilder
+ {
+ ///
+ /// Automation to refresh the integration test implementation.
+ ///
+ /// CodeFactory automation access to Visual Studio.
+ /// The name of the test class to be refreshed.
+ /// The target contract to implement testing for.
+ /// The target project the target logic is implemented in.
+ ///
+ public static async Task RefreshIntegrationTestAsync(this IVsActions source, string testName, CsInterface contract, VsProject testProject)
+ {
+ if (source == null) throw new CodeFactoryException("Could not access the CodeFactory automation for visual studio cannot refresh the integration test.");
+
+ if (string.IsNullOrEmpty(testName)) throw new CodeFactoryException("No test name was provided cannot update the integration test.");
+
+ if (contract == null) throw new CodeFactoryException("No contract was provided cannot update the integration test.");
+
+ if (testProject == null) throw new CodeFactoryException($"No test project was provided cannot refresh the integration tests that support the contract '{contract.Name}'");
+
+ var isTestProject = await testProject.TestProjectIsConfiguredXUnitAsync(true);
+
+ await testProject.CreateTestLoaderAsync();
+
+ var testClassName = testName;
+
+ if (string.IsNullOrEmpty(testClassName)) throw new CodeFactoryException("Could not load the test class name. Cannot refresh the integration tests.");
+
+ var testSource = await testProject.FindCSharpSourceByClassNameAsync(testClassName, false);
+
+ if (testSource == null) await source.CreateTestAsync(contract, testProject, testClassName);
+ else await source.UpdateTestAsync(contract, testSource.SourceCode);
+
+ }
+
+ ///
+ /// Creates a new integration test;
+ ///
+ /// CodeFactory automation for Visual Studio.
+ /// The target interface to be tested.
+ /// The target project the test should be created in.
+ /// The name of the target test class.
+ /// Raised if required data is missing.
+ public static async Task CreateTestAsync(this IVsActions source, CsInterface contract, VsProject testProject, string testClassName)
+ {
+ if (source == null) throw new CodeFactoryException("Could not access the CodeFactory automation for visual studio cannot refresh the tests.");
+
+ if (contract == null) throw new CodeFactoryException("No contract was provided cannot create the tests.");
+
+ if (testProject == null) throw new CodeFactoryException($"No test project was provided cannot create the tests that support the contract '{contract.Name}'");
+
+ if (string.IsNullOrEmpty(testClassName)) throw new CodeFactoryException($"The test class name was not provided cannot create the tests that support the contract '{contract.Name}'");
+
+ SourceFormatter testFormatter = new SourceFormatter();
+
+ testFormatter.AppendCodeLine(0, "using System;");
+ testFormatter.AppendCodeLine(0, "using System.Collections.Generic;");
+ testFormatter.AppendCodeLine(0, "using System.Linq;");
+ testFormatter.AppendCodeLine(0, "using System.Linq.Expressions;");
+ testFormatter.AppendCodeLine(0, "using System.Text;");
+ testFormatter.AppendCodeLine(0, "using System.Threading.Tasks;");
+ testFormatter.AppendCodeLine(0, "using Xunit;");
+ testFormatter.AppendCodeLine(0, "using CodeFactory.NDF;");
+ testFormatter.AppendCodeLine(0, $"using {contract.Namespace};");
+ testFormatter.AppendCodeLine(0);
+
+ testFormatter.AppendCodeLine(0, $"namespace {testProject.DefaultNamespace}");
+ testFormatter.AppendCodeLine(0, "{");
+
+ testFormatter.AppendCodeLine(1, "/// ");
+ testFormatter.AppendCodeLine(1, $"/// Integration test class that tests the contract ");
+ testFormatter.AppendCodeLine(1, "/// ");
+ testFormatter.AppendCodeLine(1, $"public class {testClassName}");
+ testFormatter.AppendCodeLine(1, "{");
+
+ testFormatter.AppendCodeLine(2, "/// ");
+ testFormatter.AppendCodeLine(2, $"/// The contract being tested.");
+ testFormatter.AppendCodeLine(2, "/// ");
+ testFormatter.AppendCodeLine(2, $"private readonly {contract.Name} _contract;");
+ testFormatter.AppendCodeLine(0);
+ testFormatter.AppendCodeLine(2, "/// ");
+ testFormatter.AppendCodeLine(2, $"/// Creates a new instances of the intergration test class for testing.");
+ testFormatter.AppendCodeLine(2, "/// ");
+ testFormatter.AppendCodeLine(2, $"public {testClassName}()");
+ testFormatter.AppendCodeLine(2, "{");
+ testFormatter.AppendCodeLine(3, $"_contract = TestLoader.GetRequiredService<{contract.Name}>();");
+ testFormatter.AppendCodeLine(2, "}");
+ testFormatter.AppendCodeLine(0);
+
+ testFormatter.AppendCodeLine(1, "}");
+
+ testFormatter.AppendCodeLine(0, "}");
+
+ var doc = await testProject.AddDocumentAsync($"{testClassName}.cs", testFormatter.ReturnSource().TrimStartEndLines());
+
+ var testSourceCode = await doc.GetCSharpSourceModelAsync();
+
+ await source.UpdateTestAsync(contract, testSourceCode);
+ }
+
+ ///
+ /// Updates a test class that performs intergration tests on the target contract.
+ ///
+ /// CodeFactory automation for visual studio.
+ /// The target contract that is being tested.
+ /// The target source code that is to be updated.
+ /// Throw if required data is missing.
+ public static async Task UpdateTestAsync(this IVsActions source, CsInterface contract, CsSource testClassSource)
+ {
+ if (source == null) throw new CodeFactoryException("Could not access the CodeFactory automation for visual studio cannot refresh the integration tests.");
+
+ if (contract == null) throw new CodeFactoryException("No contract was provided cannot update the integration tests.");
+
+ if (testClassSource == null) throw new CodeFactoryException($"The test class source was not provided cannot update the integration tests that support the contract '{contract.Name}'");
+
+ var currentSource = testClassSource;
+
+ var testClass = currentSource.Classes.FirstOrDefault();
+
+ if (testClass == null) throw new CodeFactoryException($"The test class could not be loaded from the provided source. cannot update the integrationn tests that support the contract '{contract.Name}'");
+
+ var contractMethods = contract.GetAllInterfaceMethods();
+
+ if (!contractMethods.Any()) return;
+
+ var sourceMethods = testClass.Methods ?? new List();
+
+ var AddTests = new List();
+
+ foreach (var contractMethod in contractMethods)
+ {
+ var testMethodName = contractMethod.FormatTestMethodName();
+
+ if (string.IsNullOrEmpty(testMethodName)) continue;
+
+ if (!sourceMethods.Any(m => m.Name == testMethodName)) AddTests.Add(contractMethod);
+ }
+
+ if (!AddTests.Any()) return;
+
+ var sourceManager = new SourceClassManager(currentSource, testClass, source);
+
+ foreach (var addMethod in AddTests)
+ {
+ StringBuilder parameterBuilder = new StringBuilder();
+ var testMethodFormatter = new SourceFormatter();
+
+ testMethodFormatter.AppendCodeLine(0);
+ testMethodFormatter.AppendCodeLine(2, "/// ");
+ testMethodFormatter.AppendCodeLine(2, $"/// Integration test that tests the contract method \"{addMethod.Name}\"");
+ testMethodFormatter.AppendCodeLine(2, "/// ");
+ testMethodFormatter.AppendCodeLine(2, "[Fact]");
+
+ if (addMethod.IsAsync()) testMethodFormatter.AppendCodeLine(2, $"public async Task {addMethod.FormatTestMethodName()}()");
+ else testMethodFormatter.AppendCodeLine(2, $"public void {addMethod.FormatTestMethodName()}()");
+ testMethodFormatter.AppendCodeLine(2, "{");
+
+ // Build Arrange Section.
+ testMethodFormatter.AppendCode(ArrangeSectionBuilder(addMethod, AddTests));
+
+ testMethodFormatter.AppendCodeLine(3, "try");
+ testMethodFormatter.AppendCodeLine(3, "{");
+
+ // Build Act Section
+ testMethodFormatter.AppendCode(ActSectionBuilder(addMethod, AddTests));
+ testMethodFormatter.AppendCodeLine(0);
+
+ // Build Assert Section.
+ testMethodFormatter.AppendCode(AssertSectionBuilder(addMethod, AddTests));
+
+ testMethodFormatter.AppendCodeLine(3, "}");
+ testMethodFormatter.AppendCodeLine(3, "catch (Exception unhandled)");
+ testMethodFormatter.AppendCodeLine(3, "{");
+ testMethodFormatter.AppendCodeLine(4, "Assert.Fail($\"The following unhandled exception occurred '{unhandled.Message}' \");");
+ testMethodFormatter.AppendCodeLine(3, "}");
+
+ testMethodFormatter.AppendCodeLine(3, "finally");
+ testMethodFormatter.AppendCodeLine(3, "{");
+
+ // Build Finally Clause section.
+ testMethodFormatter.AppendCode(FinallyClauseSectionBuilder(addMethod, AddTests));
+
+ testMethodFormatter.AppendCodeLine(3, "}");
+ testMethodFormatter.AppendCodeLine(0);
+
+ testMethodFormatter.AppendCodeLine(2, "}");
+
+ await sourceManager.ConstructorsAddAfterAsync(testMethodFormatter.ReturnSource());
+
+ testMethodFormatter.ResetFormatter();
+ }
+
+ return;
+ }
+
+ ///
+ /// Builds the Arange body contents of a test.
+ ///
+ /// Source formatter of the test.
+ /// Formatted test contents.
+ private static string ArrangeSectionBuilder(CsMethod method, List allMethods)
+ {
+ SourceFormatter source = new SourceFormatter();
+ if (method == null) return source.ReturnSource();
+
+ var addMethod = allMethods.FirstOrDefault(x => x.Name.ToLower() == "addasync");
+
+ // Initialize parameter variables
+ source.AppendCodeLine(3, "// Arrange");
+ source = method.GenerateNewTestObjectsFromParameters(source);
+
+ // Initialize objects depending on CRUD methods.
+ if (method.Name.ToLower().StartsWith("getasync"))
+ {
+ // If AddAsync exist, new up an object so that we can use it later as a parameter to add a test record.
+ if (addMethod != null)
+ {
+ source = addMethod.GenerateNewTestObjectsFromParameters(source);
+ }
+ }
+
+ // Initialize the result object
+ if (method.HasReturnType())
+ {
+ var defaultValue = method.GetReturnType().GenerateCSharpDefaultValue();
+
+ source.AppendCodeLine(3, defaultValue != null
+ ? $"{method.GetReturnType().GenerateCSharpTypeName()}? result = new {method.GetReturnType().GenerateCSharpTypeName()}();"
+ : $"{method.GetReturnType().GenerateCSharpTypeName()} result;");
+ source.AppendCodeLine(4);
+ }
+
+ return source.ReturnSource().TrimEndLines();
+ }
+
+ ///
+ /// Builds the Act body contents of a test.
+ ///
+ /// Source formatter of the test.
+ /// Formatted test contents.
+ private static string ActSectionBuilder(CsMethod method, List allMethods)
+ {
+ SourceFormatter source = new SourceFormatter();
+ if (method == null) return source.ReturnSource();
+
+ var addMethod = allMethods.FirstOrDefault(x => x.Name.ToLower() == "addasync");
+ var getMethod = allMethods.FirstOrDefault(x => x.Name.ToLower().StartsWith("getasync"));
+ var updateMethod = allMethods.FirstOrDefault(x => x.Name.ToLower() == "updateasync");
+
+ if (!method.HasReturnType())
+ return source.ReturnSource();
+
+ source.AppendCodeLine(4, "// Act");
+
+ // If AddAsync exist, call AddAsync to create a test record.
+ if (addMethod != null && method.Name.ToLower() == "addasync")
+ {
+ source.AppendCodeLine(4, $"result = await _contract.AddAsync({addMethod.GenerateParameterListAsCamelCased()});");
+ }
+
+ // If GetAsync exist, call GetAsync to get a record.
+ else if (getMethod != null && method.Name.ToLower() == "getasync" && addMethod != null)
+ {
+ source.AppendCodeLine(4, $"result = await _contract.AddAsync({addMethod.GenerateParameterListAsCamelCased()});");
+ source.AppendCodeLine(4, $"result = await _contract.GetAsync({getMethod.GenerateParameterListAsProperCased()});");
+ }
+
+ // If AddAsync exist, call AddAsync to create a test record.
+ else if (updateMethod != null && method.Name.ToLower() == "updateasync" && addMethod != null)
+ {
+ source.AppendCodeLine(4, $"result = await _contract.AddAsync({addMethod.GenerateParameterListAsCamelCased()});");
+ source.AppendCodeLine(4, $"result.{updateMethod.Parameters[0].ParameterType.GetFirstStringTypePropertyName()} += \" Update\";");
+ source.AppendCodeLine(4, $"result = await _contract.UpdateAsync(result);");
+ }
+ else
+ {
+ source.AppendCodeLine(4, method.HasReturnType()
+ ? $"result = {method.GenerateStringAwaitStatement()}_contract.{method.Name}({method.GenerateParameterListAsCamelCased()});"
+ : $"{method.GenerateStringAwaitStatement()}_contract.{method.Name}({method.GenerateParameterListAsCamelCased()});");
+
+ }
+
+ source.AppendCodeLine(0);
+ return source.ReturnSource().TrimEndLines();
+ }
+
+ ///
+ /// Builds the Assert body contents of a test.
+ ///
+ /// Source formatter of the test.
+ /// Formatted test contents.
+ private static string AssertSectionBuilder(CsMethod method, List allMethods)
+ {
+ SourceFormatter source = new SourceFormatter();
+ if (method == null) return source.ReturnSource();
+
+ var addMethod = allMethods.FirstOrDefault(x => x.Name.ToLower() == "addasync");
+ var updateMethod = allMethods.FirstOrDefault(x => x.Name.ToLower() == "updateasync");
+ var getMethod = allMethods.FirstOrDefault(x => x.Name.ToLower() == "getasync");
+
+ if (method.HasReturnType())
+ {
+ source.AppendCodeLine(0);
+ source.AppendCodeLine(4, "// Assert");
+ source.AppendCodeLine(4, "Assert.NotNull(result);");
+ }
+ else
+ {
+ source.AppendCodeLine(4, "// Act and Assert");
+
+ // If DeleteAsync
+ if (method.Name.ToLower() == "deleteasync")
+ {
+ if (addMethod != null)
+ {
+ source.AppendCodeLine(4, $"var result = await _contract.AddAsync({addMethod.GenerateParameterListAsCamelCased()});");
+ source.AppendCodeLine(4, $"await _contract.DeleteAsync(result);");
+ source.AppendCodeLine(0);
+ }
+ }
+
+ source.AppendCodeLine(4, $"await Assert.ThrowsAsync({getMethod.GenerateStringAsyncStatement()} ()=> {getMethod.GenerateStringAwaitStatement()}_contract.{getMethod.Name}({getMethod.GenerateParameterListAsProperCased()}));");
+ }
+
+ // If AddAsync, check the result to make sure the id is not 0.
+ if (addMethod != null && method.Name.ToLower() == "addasync")
+ {
+ source.AppendCodeLine(4, $"Assert.NotEqual(0, result.{addMethod.Parameters[0].ParameterType.GetPrimaryKeyName()});");
+ }
+
+ // If UpdateAsync, check that the result of the update call during the Act was updated with a " update" text on the first string property.
+ if (updateMethod != null && method.Name.ToLower() == "updateasync")
+ {
+ source.AppendCodeLine(4, $"Assert.Equal({updateMethod.Parameters[0].Name.GenerateCSharpCamelCase()}.{updateMethod.Parameters[0].ParameterType.GetFirstStringTypePropertyName()} + \" Update\", result.{updateMethod.Parameters[0].ParameterType.GetFirstStringTypePropertyName()});");
+ }
+
+ return source.ReturnSource().TrimStartEndLines();
+ }
+
+ ///
+ /// Builds the body for the finally clause in the try catch.
+ ///
+ /// Source formatter of the test.
+ /// Formatted test contents.
+ private static string FinallyClauseSectionBuilder(CsMethod method, List allMethods)
+ {
+ SourceFormatter source = new SourceFormatter();
+ if (method == null) return source.ReturnSource();
+
+ source.AppendCodeLine(4, $"// Cleanup");
+ var deleteMethod = allMethods.FirstOrDefault(x => x.Name.ToLower() == "deleteasync");
+
+ // Add additional assert checks.
+ if (method.Name.ToLower() == "updateasync" || method.Name.ToLower() == "getasync" || method.Name.ToLower() == "addasync")
+ {
+ // Delete the record that was created as a test, if one exist.
+ source.AppendCodeLine(4, $"await _contract.DeleteAsync(result!);");
+ }
+
+ source.AppendCodeLine(0);
+ source.AppendCodeLine(4, $"// Add any cleanup code if necessary");
+
+ return source.ReturnSource().TrimEndLines();
+ }
+
+ ///
+ /// Formats the repositories test class name.
+ ///
+ /// Source interface to convert to the test class name.
+ /// Formatted test class name or null if it was not found.
+ private static string FormatTestClassName(this CsInterface source)
+ {
+ if (source == null) return null;
+
+ return $"{source.Name.GenerateCSharpFormattedClassName()}Test";
+ }
+
+ ///
+ /// Formats the name of the test method for the contract.
+ ///
+ /// Method model to generate the test method name from.
+ /// Formatted method name or null if it is not found.
+ private static string FormatTestMethodName(this CsMethod source)
+ {
+ if (source == null) return null;
+
+ if (!source.HasParameters) return source.Name;
+
+ StringBuilder testMethodBuilder = new StringBuilder();
+
+ testMethodBuilder.Append($"{source.Name}By");
+
+ foreach (var parameter in source.Parameters)
+ testMethodBuilder.Append(parameter.Name.GenerateCSharpProperCase());
+
+ return testMethodBuilder.ToString();
+ }
+
+ ///
+ /// Creates a new instance of the 'TestLoader' class.
+ ///
+ /// Target project to add the test loader to.
+ public static async Task CreateTestLoaderAsync(this VsProject testProject)
+ {
+ var sourceFile = await testProject.FindCSharpSourceByClassNameAsync("TestLoader", false);
+
+ if (sourceFile != null) return;
+
+ var sourceFormatter = new SourceFormatter();
+
+ sourceFormatter.AppendCodeLine(0, "using Microsoft.Extensions.Configuration;");
+ sourceFormatter.AppendCodeLine(0, "using Microsoft.Extensions.DependencyInjection;");
+ sourceFormatter.AppendCodeLine(0, "using Microsoft.Extensions.Logging;");
+ sourceFormatter.AppendCodeLine(0, "using Microsoft.Extensions.Logging.Abstractions;");
+ sourceFormatter.AppendCodeLine(0, "using System;");
+ sourceFormatter.AppendCodeLine(0, "using System.Collections.Generic;");
+ sourceFormatter.AppendCodeLine(0, "using System.Linq;");
+ sourceFormatter.AppendCodeLine(0, "using System.Text;");
+ sourceFormatter.AppendCodeLine(0, "using System.Threading.Tasks;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(0, $"namespace {testProject.DefaultNamespace}");
+ sourceFormatter.AppendCodeLine(0, "{");
+ sourceFormatter.AppendCodeLine(1, "/// ");
+ sourceFormatter.AppendCodeLine(1, "/// Loader class that is used by testing to load required services.");
+ sourceFormatter.AppendCodeLine(1, "/// ");
+ sourceFormatter.AppendCodeLine(1, "public static class TestLoader");
+ sourceFormatter.AppendCodeLine(1, "{");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Backing field for the property.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "private static readonly IConfiguration _configuration;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Backing field for the property.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "private static readonly IServiceProvider _serviceProvider;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Logging factory used for testing");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "private static readonly ILoggerFactory _loggerFactory;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Constructor that gets called when the class is accessed for the first time.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "static TestLoader()");
+ sourceFormatter.AppendCodeLine(2, "{");
+ sourceFormatter.AppendCodeLine(3, "//Loading the configuration");
+ sourceFormatter.AppendCodeLine(3, "var configuration = new ConfigurationBuilder()");
+ sourceFormatter.AppendCodeLine(3, " .AddJsonFile(\"appsettings.local.json\", true)");
+ sourceFormatter.AppendCodeLine(3, " .AddEnvironmentVariables().Build();");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "//Setting the config property.");
+ sourceFormatter.AppendCodeLine(3, "_configuration = configuration;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "//Creating the service container");
+ sourceFormatter.AppendCodeLine(3, "var services = new ServiceCollection();");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "//Adding access to configuration from the service container");
+ sourceFormatter.AppendCodeLine(3, "services.AddSingleton(Configuration);");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "//Loading the libraries into the service collection.");
+ sourceFormatter.AppendCodeLine(3, "LoadLibraries(services, _configuration);");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "_loggerFactory = new NullLoggerFactory();");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "services.AddSingleton(_loggerFactory);");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(3, "services.AddLogging();");
+ sourceFormatter.AppendCodeLine(3, "//Building the service provider.");
+ sourceFormatter.AppendCodeLine(3, "_serviceProvider = services.BuildServiceProvider();");
+ sourceFormatter.AppendCodeLine(0);
+
+ sourceFormatter.AppendCodeLine(2, "}");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// The loaded configuration to be used with testing.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "public static IConfiguration Configuration => _configuration;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Service provider for the loaded dependency configuration.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "public static IServiceProvider ServiceProvider => _serviceProvider;");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Gets the required service to use with testing.");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Type of the service to be loaded.");
+ sourceFormatter.AppendCodeLine(2, "/// Instance of the service");
+ sourceFormatter.AppendCodeLine(2, "public static T GetRequiredService() where T : notnull");
+ sourceFormatter.AppendCodeLine(2, "{");
+ sourceFormatter.AppendCodeLine(3, "return _serviceProvider.GetRequiredService();");
+
+ sourceFormatter.AppendCodeLine(2, "}");
+ sourceFormatter.AppendCodeLine(0);
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Loads the libraries into service collection");
+ sourceFormatter.AppendCodeLine(2, "/// ");
+ sourceFormatter.AppendCodeLine(2, "/// Service collection to load.");
+ sourceFormatter.AppendCodeLine(2, "/// The configuration to be provided to services.");
+ sourceFormatter.AppendCodeLine(2, "public static void LoadLibraries(IServiceCollection services, IConfiguration configuration)");
+ sourceFormatter.AppendCodeLine(2, "{");
+ sourceFormatter.AppendCodeLine(3, "// Load Default Library Loader.");
+ sourceFormatter.AppendCodeLine(3, "var libraryLoader = new LibraryLoader();");
+ sourceFormatter.AppendCodeLine(3, "libraryLoader.Load(services, configuration);");
+ sourceFormatter.AppendCodeLine(2, "}");
+
+
+ sourceFormatter.AppendCodeLine(1, "}");
+ sourceFormatter.AppendCodeLine(0, "}");
+
+ await testProject.AddDocumentAsync("TestLoader.cs", sourceFormatter.ReturnSource().TrimStartEndLines());
+ }
+
+ ///
+ /// Helper method that checks a project to make sure all required project references exist before building a test.
+ ///
+ /// Project to check.
+ /// Optional flag that determines if an exception should be thrown if the project is not configred, default is false.
+ /// True if configured with xUnit Tests, false if not.
+ /// Thrown if required data is missing.
+ public static async Task TestProjectIsConfiguredXUnitAsync(this VsProject project, bool throwError = false)
+ {
+ if (project == null) throw new CodeFactoryException("No test project was provided cannot build integration tests.");
+
+ var projectRefs = await project.GetProjectReferencesAsync();
+
+ if (!projectRefs.Any(r => r.Name == "Microsoft.Extensions.Logging.Abstractions"))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'Microsoft.Extensions.Logging.Abstractions'");
+ return false;
+ }
+ if (!projectRefs.Any(r => r.Name == "Microsoft.Extensions.Configuration"))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'Microsoft.Extensions.Configuration'");
+ return false;
+ }
+ if (!projectRefs.Any(r => r.Name == "Microsoft.Extensions.DependencyInjection"))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'Microsoft.Extensions.DependencyInjection'");
+ return false;
+ }
+ if (!projectRefs.Any(r => r.Name == "Microsoft.VisualStudio.TestPlatform.ObjectModel"))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'Microsoft.VisualStudio.TestPlatform.ObjectModel'");
+ return false;
+ }
+ if (!projectRefs.Any(r => r.Name.Contains("xunit.")))
+ {
+ if (throwError) throw new CodeFactoryException("The test project must reference 'xunit'");
+ return false;
+ }
+ return true;
+ }
+ }
+}
diff --git a/src/Automation/CodeFactory.Automation.Standard.Logic/CodeFactory.Automation.Standard.Logic.csproj b/src/Automation/CodeFactory.Automation.Standard.Logic/CodeFactory.Automation.Standard.Logic.csproj
index 32ab3ec..025ebf0 100644
--- a/src/Automation/CodeFactory.Automation.Standard.Logic/CodeFactory.Automation.Standard.Logic.csproj
+++ b/src/Automation/CodeFactory.Automation.Standard.Logic/CodeFactory.Automation.Standard.Logic.csproj
@@ -50,18 +50,27 @@
-
-
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
+