diff --git a/.vscode/launch.json b/.vscode/launch.json
index 47f3a49..92ba1f8 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -10,7 +10,8 @@
"args": ["i"],
"cwd": "${workspaceFolder}",
"console": "externalTerminal",
- "stopAtEntry": false
+ "stopAtEntry": false,
+ "brokeredServicePipeName": "undefined",
}
]
}
\ No newline at end of file
diff --git a/Directory.Build.props b/Directory.Build.props
index 80b9f53..3a94d5e 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,13 +1,15 @@
- Kentico
- Eric Dugre
- 4.0.0
- Xperience by Kentico
- 12.0
- enable
- enable
-
+ Kentico
+ Eric Dugre
+ 5.0.0
+ Xperience by Kentico
+ 12.0
+ enable
+ enable
+ https://github.com/Kentico/xperience-by-kentico-manager
+ git
+
@@ -19,4 +21,4 @@
http://timestamp.digicert.com
-
\ No newline at end of file
+
diff --git a/README.md b/README.md
index 9c938e4..32ba280 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,7 @@ The following commands can be executed using the `xman` tool name:
- `?`, `help`
- [`i`, `install`](#installing-a-new-project)
- [`u`, `update`](#updating-a-project-version)
+- [`d`, `delete`](#deleting-a-project)
- [`m`, `macros`](#re-signing-macros)
- [`b`, `build`](#building-projects)
- [`g`, `generate`](#generating-code-for-object-types)
@@ -103,6 +104,8 @@ The installation wizard will automatically generate an administrator password fo
xman install
```
+Installing a new project automatically includes a database as well. If you want to _only_ install a database and not the project files, use the __db__ parameter: `xman install db`.
+
### Updating a project version
1. (optional) Select a profile with the [`profile`](#managing-profiles) command
@@ -112,6 +115,17 @@ The installation wizard will automatically generate an administrator password fo
xman update
```
+### Deleting a project
+
+> :warning: The `delete` command will drop the database and delete the files. Use with caution!
+
+1. (optional) Select a profile with the [`profile`](#managing-profiles) command
+1. Run the `delete` command from the directory containing the [configuration file](#configuration-file):
+
+ ```bash
+ xman delete
+ ```
+
### Modifying appsettings.json
This tool can assist with changing the _CMSConnectionString_, supported [configuration keys](https://docs.xperience.io/xp/developers-and-admins/configuration/reference-configuration-keys), and the [headless API](https://docs.xperience.io/xp/developers-and-admins/configuration/headless-channel-management#Headlesschannelmanagement-ConfiguretheheadlessAPI).
diff --git a/img/icon.png b/img/icon.png
new file mode 100644
index 0000000..046463d
Binary files /dev/null and b/img/icon.png differ
diff --git a/src/Commands/Base/AbstractCommand.cs b/src/Commands/Base/AbstractCommand.cs
index 2c9f73c..4ebda89 100644
--- a/src/Commands/Base/AbstractCommand.cs
+++ b/src/Commands/Base/AbstractCommand.cs
@@ -54,7 +54,7 @@ public virtual Task PreExecute(ToolProfile? profile, string? action)
///
- /// A handler which can be assigned to to handler errors.
+ /// A handler which can be assigned to to handle errors.
///
protected void ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
@@ -80,7 +80,7 @@ protected void LogError(string message, Process? process = null)
}
- protected void PrintCurrentProfile(ToolProfile? profile)
+ protected static void PrintCurrentProfile(ToolProfile? profile)
{
AnsiConsole.Write(new Rule("Current profile:") { Justification = Justify.Left });
AnsiConsole.MarkupLineInterpolated($"Name: [{Constants.EMPHASIS_COLOR}]{profile?.ProjectName ?? "None"}[/]");
diff --git a/src/Commands/BuildCommand.cs b/src/Commands/BuildCommand.cs
index d7ceda9..a01ebba 100644
--- a/src/Commands/BuildCommand.cs
+++ b/src/Commands/BuildCommand.cs
@@ -17,7 +17,7 @@ public class BuildCommand : AbstractCommand
public override IEnumerable Keywords => ["b", "build"];
- public override IEnumerable Parameters => Enumerable.Empty();
+ public override IEnumerable Parameters => [];
public override string Description => "Builds a project";
diff --git a/src/Commands/CodeGenerateCommand.cs b/src/Commands/CodeGenerateCommand.cs
index 892b655..98a424c 100644
--- a/src/Commands/CodeGenerateCommand.cs
+++ b/src/Commands/CodeGenerateCommand.cs
@@ -20,7 +20,7 @@ public class CodeGenerateCommand : AbstractCommand
public override IEnumerable Keywords => ["g", "generate"];
- public override IEnumerable Parameters => Enumerable.Empty();
+ public override IEnumerable Parameters => [];
public override string Description => "Generates code files for Xperience objects";
diff --git a/src/Commands/DeleteCommand.cs b/src/Commands/DeleteCommand.cs
new file mode 100644
index 0000000..7e58cd0
--- /dev/null
+++ b/src/Commands/DeleteCommand.cs
@@ -0,0 +1,145 @@
+using Spectre.Console;
+
+using Xperience.Manager.Configuration;
+using Xperience.Manager.Options;
+using Xperience.Manager.Services;
+
+namespace Xperience.Manager.Commands
+{
+ ///
+ /// A command which deletes an Xperience by Kentico project.
+ ///
+ public class DeleteCommand : AbstractCommand
+ {
+ private bool deleteConfirmed;
+ private readonly IShellRunner shellRunner;
+ private readonly IScriptBuilder scriptBuilder;
+ private readonly IConfigManager configManager;
+ private readonly IAppSettingsManager appSettingsManager;
+
+
+ public override IEnumerable Keywords => ["d", "delete"];
+
+
+ public override IEnumerable Parameters => [];
+
+
+ public override string Description => "Deletes a project and its database";
+
+
+ public override bool RequiresProfile => true;
+
+
+ ///
+ /// Do not use. Workaround for circular dependency in when commands are injected
+ /// into the constuctor.
+ ///
+#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
+ internal DeleteCommand()
+#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
+ {
+ }
+
+
+ public DeleteCommand(IShellRunner shellRunner, IScriptBuilder scriptBuilder, IConfigManager configManager, IAppSettingsManager appSettingsManager)
+ {
+ this.shellRunner = shellRunner;
+ this.scriptBuilder = scriptBuilder;
+ this.configManager = configManager;
+ this.appSettingsManager = appSettingsManager;
+ }
+
+
+ public override async Task Execute(ToolProfile? profile, string? action)
+ {
+ deleteConfirmed = AnsiConsole.Confirm($"This will [{Constants.ERROR_COLOR}]delete[/] the current profile's physical folder and database!\nDo you want to continue?", false);
+ if (!deleteConfirmed)
+ {
+ return;
+ }
+
+ AnsiConsole.WriteLine();
+ await DropDatabase(profile);
+ await UninstallFiles(profile);
+ if (!StopProcessing)
+ {
+ await configManager.RemoveProfile(profile);
+ }
+ }
+
+
+ public override async Task PostExecute(ToolProfile? profile, string? action)
+ {
+ if (!deleteConfirmed)
+ {
+ AnsiConsole.MarkupLineInterpolated($"[{Constants.EMPHASIS_COLOR}]Delete cancelled[/]\n");
+ }
+ else if (!Errors.Any())
+ {
+ AnsiConsole.MarkupLineInterpolated($"[{Constants.SUCCESS_COLOR}]Delete complete![/]\n");
+ }
+
+ await base.PostExecute(profile, action);
+ }
+
+
+ private async Task DropDatabase(ToolProfile? profile)
+ {
+ if (StopProcessing)
+ {
+ return;
+ }
+
+ AnsiConsole.MarkupLineInterpolated($"[{Constants.EMPHASIS_COLOR}]Deleting database...[/]");
+
+ string? connString = await appSettingsManager.GetConnectionString(profile, "CMSConnectionString");
+ if (connString is null)
+ {
+ LogError("Couldn't load connection string.");
+ return;
+ }
+
+ // Find "Initial Catalog" in connection string
+ IEnumerable parts = connString.Split(';').ToList();
+ string? initialCatalogPart = parts.FirstOrDefault(p => p.ToLower().StartsWith("initial catalog"));
+ if (initialCatalogPart is null)
+ {
+ LogError("Couldn't find database name.");
+ return;
+ }
+
+ // Remove "Initial Catalog" from connection string, or trying to delete will throw "in use" error
+ parts = parts.Where(p => !p.Equals(initialCatalogPart, StringComparison.OrdinalIgnoreCase));
+ connString = string.Join(';', parts);
+ string databaseName = initialCatalogPart.Split('=')[1].Trim();
+
+ var options = new RunSqlOptions()
+ {
+ SqlQuery = $"DROP DATABASE {databaseName}",
+ ConnString = connString
+ };
+ string dbScript = scriptBuilder.SetScript(ScriptType.ExecuteSql).WithPlaceholders(options).Build();
+ await shellRunner.Execute(new(dbScript)
+ {
+ ErrorHandler = ErrorDataReceived
+ }).WaitForExitAsync();
+ }
+
+
+ private async Task UninstallFiles(ToolProfile? profile)
+ {
+ if (StopProcessing)
+ {
+ return;
+ }
+
+ AnsiConsole.MarkupLineInterpolated($"[{Constants.EMPHASIS_COLOR}]Deleting local files...[/]");
+
+ string uninstallScript = scriptBuilder.SetScript(ScriptType.DeleteDirectory).WithPlaceholders(profile).Build();
+ await shellRunner.Execute(new(uninstallScript)
+ {
+ ErrorHandler = ErrorDataReceived
+ }).WaitForExitAsync();
+ }
+ }
+}
diff --git a/src/Commands/HelpCommand.cs b/src/Commands/HelpCommand.cs
index 7a20fc5..691f1ea 100644
--- a/src/Commands/HelpCommand.cs
+++ b/src/Commands/HelpCommand.cs
@@ -19,6 +19,7 @@ public class HelpCommand : AbstractCommand
[
new ProfileCommand(),
new InstallCommand(),
+ new DeleteCommand(),
new UpdateCommand(),
new ContinuousIntegrationCommand(),
new ContinuousDeploymentCommand(),
@@ -32,7 +33,7 @@ public class HelpCommand : AbstractCommand
public override IEnumerable Keywords => ["?", "help"];
- public override IEnumerable Parameters => Enumerable.Empty();
+ public override IEnumerable Parameters => [];
public override string Description => "Displays the help menu (this screen)";
@@ -51,7 +52,7 @@ public override async Task Execute(ToolProfile? profile, string? action)
AnsiConsole.WriteLine($" v{v.Major}.{v.Minor}.{v.Build}");
}
- AnsiConsole.MarkupInterpolated($" [{Constants.EMPHASIS_COLOR}]https://github.com/kentico/xperience-manager[/]\n");
+ AnsiConsole.MarkupInterpolated($" [{Constants.EMPHASIS_COLOR}]https://github.com/Kentico/xperience-by-kentico-manager[/]\n");
var table = new Table()
.AddColumn("Command")
diff --git a/src/Commands/InstallCommand.cs b/src/Commands/InstallCommand.cs
index bcd05e6..3e7e108 100644
--- a/src/Commands/InstallCommand.cs
+++ b/src/Commands/InstallCommand.cs
@@ -3,6 +3,7 @@
using System.Diagnostics;
using Xperience.Manager.Configuration;
+using Xperience.Manager.Helpers;
using Xperience.Manager.Options;
using Xperience.Manager.Services;
using Xperience.Manager.Wizards;
@@ -14,20 +15,22 @@ namespace Xperience.Manager.Commands
///
public class InstallCommand : AbstractCommand
{
+ private const string DATABASE = "db";
private readonly ToolProfile newInstallationProfile = new();
private readonly IShellRunner shellRunner;
private readonly IConfigManager configManager;
private readonly IScriptBuilder scriptBuilder;
- private readonly IWizard wizard;
+ private readonly IWizard projectWizard;
+ private readonly IWizard dbWizard;
public override IEnumerable Keywords => ["i", "install"];
- public override IEnumerable Parameters => Enumerable.Empty();
+ public override IEnumerable Parameters => [DATABASE];
- public override string Description => "Installs a new XbK instance";
+ public override string Description => "Installs a new XbK instance. The 'db' parameter installs only a database";
///
@@ -41,9 +44,15 @@ internal InstallCommand()
}
- public InstallCommand(IShellRunner shellRunner, IScriptBuilder scriptBuilder, IWizard wizard, IConfigManager configManager)
+ public InstallCommand(
+ IShellRunner shellRunner,
+ IScriptBuilder scriptBuilder,
+ IWizard projectWizard,
+ IWizard dbWizard,
+ IConfigManager configManager)
{
- this.wizard = wizard;
+ this.dbWizard = dbWizard;
+ this.projectWizard = projectWizard;
this.shellRunner = shellRunner;
this.configManager = configManager;
this.scriptBuilder = scriptBuilder;
@@ -57,22 +66,39 @@ public override async Task Execute(ToolProfile? profile, string? action)
return;
}
- // Override default values of InstallOptions with values from config file
- wizard.Options = await configManager.GetDefaultInstallOptions();
- var options = await wizard.Run();
- AnsiConsole.WriteLine();
+ // Override default values of InstallDatabaseOptions and InstallProjectOptions with values from config file
+ dbWizard.Options = await configManager.GetDefaultInstallDatabaseOptions();
+ projectWizard.Options = await configManager.GetDefaultInstallProjectOptions();
+
+ // Only install database if "db" argument is passed
+ InstallDatabaseOptions? dbOptions = null;
+ if (!string.IsNullOrEmpty(action) && action.Equals(DATABASE))
+ {
+ dbOptions = await dbWizard.Run(InstallDatabaseWizard.SKIP_EXISTINGDB_STEP);
+ await InstallDatabaseTool();
+ await CreateDatabase(dbOptions, true);
+
+ return;
+ }
+
+ var projectOptions = await projectWizard.Run();
+ if (!IsAdminTemplate(projectOptions))
+ {
+ dbOptions = await dbWizard.Run();
+ }
- newInstallationProfile.ProjectName = options.ProjectName;
- newInstallationProfile.WorkingDirectory = $"{options.InstallRootPath}\\{options.ProjectName}";
+ newInstallationProfile.ProjectName = projectOptions.ProjectName;
+ newInstallationProfile.WorkingDirectory = $"{projectOptions.InstallRootPath}\\{projectOptions.ProjectName}";
+ AnsiConsole.WriteLine();
await CreateWorkingDirectory();
- await InstallTemplate(options);
- await CreateProjectFiles(options);
+ await InstallTemplate(projectOptions);
+ await CreateProjectFiles(projectOptions);
// Admin boilerplate project doesn't require database install or profile
- if (!IsAdminTemplate(options))
+ if (!IsAdminTemplate(projectOptions) && dbOptions is not null)
{
- await CreateDatabase(options);
+ await CreateDatabase(dbOptions, false);
await configManager.AddProfile(newInstallationProfile);
// Select new profile
@@ -113,7 +139,7 @@ private async Task CreateWorkingDirectory()
}
- private async Task CreateDatabase(InstallOptions options)
+ private async Task CreateDatabase(InstallDatabaseOptions options, bool isDatabaseOnly)
{
if (StopProcessing)
{
@@ -125,6 +151,12 @@ private async Task CreateDatabase(InstallOptions options)
string databaseScript = scriptBuilder.SetScript(ScriptType.DatabaseInstall)
.WithPlaceholders(options)
.Build();
+ // Database-only install requires removal of "dotnet" from the script to run global tool
+ if (isDatabaseOnly)
+ {
+ databaseScript = databaseScript.Replace("dotnet", "");
+ }
+
await shellRunner.Execute(new(databaseScript)
{
ErrorHandler = ErrorDataReceived,
@@ -133,7 +165,7 @@ await shellRunner.Execute(new(databaseScript)
}
- private async Task CreateProjectFiles(InstallOptions options)
+ private async Task CreateProjectFiles(InstallProjectOptions options)
{
if (StopProcessing)
{
@@ -167,8 +199,43 @@ await shellRunner.Execute(new(installScript)
}).WaitForExitAsync();
}
+ private async Task InstallDatabaseTool()
+ {
+ if (StopProcessing)
+ {
+ return;
+ }
+
+ // Get desired database tool version
+ var versions = await NuGetVersionHelper.GetPackageVersions(Constants.DATABASE_TOOL);
+ var filtered = versions.Where(v => !v.IsPrerelease && !v.IsLegacyVersion && v.Major >= 25)
+ .Select(v => v.Version)
+ .OrderByDescending(v => v);
+ var toolVersion = AnsiConsole.Prompt(new SelectionPrompt()
+ .Title($"Which [{Constants.PROMPT_COLOR}]version[/]?")
+ .PageSize(10)
+ .UseConverter(v => $"{v.Major}.{v.Minor}.{v.Build}")
+ .MoreChoicesText("Scroll for more...")
+ .AddChoices(filtered));
+
+ AnsiConsole.WriteLine();
+ AnsiConsole.MarkupLineInterpolated($"[{Constants.EMPHASIS_COLOR}]Uninstalling previous database tool...[/]");
+
+ string uninstallScript = scriptBuilder.SetScript(ScriptType.UninstallDatabaseTool).Build();
+ // Don't use base error handler for uninstall script as it throws when no tool is installed
+ // Just skip uninstall step in case of error and try to continue
+ await shellRunner.Execute(new(uninstallScript)).WaitForExitAsync();
+
+ AnsiConsole.MarkupLineInterpolated($"[{Constants.EMPHASIS_COLOR}]Installing database tool version {toolVersion}...[/]");
+
+ string installScript = scriptBuilder.SetScript(ScriptType.InstallDatabaseTool)
+ .AppendVersion(toolVersion)
+ .Build();
+ await shellRunner.Execute(new(installScript) { ErrorHandler = ErrorDataReceived }).WaitForExitAsync();
+ }
+
- private async Task InstallTemplate(InstallOptions options)
+ private async Task InstallTemplate(InstallProjectOptions options)
{
if (StopProcessing)
{
@@ -180,8 +247,7 @@ private async Task InstallTemplate(InstallOptions options)
string uninstallScript = scriptBuilder.SetScript(ScriptType.TemplateUninstall).Build();
// Don't use base error handler for uninstall script as it throws when no templates are installed
// Just skip uninstall step in case of error and try to continue
- var uninstallCmd = shellRunner.Execute(new(uninstallScript));
- await uninstallCmd.WaitForExitAsync();
+ await shellRunner.Execute(new(uninstallScript)).WaitForExitAsync();
AnsiConsole.MarkupLineInterpolated($"[{Constants.EMPHASIS_COLOR}]Installing template version {options.Version}...[/]");
@@ -189,11 +255,11 @@ private async Task InstallTemplate(InstallOptions options)
.WithPlaceholders(options)
.AppendVersion(options.Version)
.Build();
- var installCmd = shellRunner.Execute(new(installScript) { ErrorHandler = ErrorDataReceived });
- await installCmd.WaitForExitAsync();
+ await shellRunner.Execute(new(installScript) { ErrorHandler = ErrorDataReceived }).WaitForExitAsync();
}
- private bool IsAdminTemplate(InstallOptions options) => options?.Template.Equals(Constants.TEMPLATE_ADMIN, StringComparison.OrdinalIgnoreCase) ?? false;
+ private static bool IsAdminTemplate(InstallProjectOptions options) =>
+ options?.Template.Equals(Constants.TEMPLATE_ADMIN, StringComparison.OrdinalIgnoreCase) ?? false;
}
}
diff --git a/src/Commands/MacroCommand.cs b/src/Commands/MacroCommand.cs
index 07e6dfe..502fc89 100644
--- a/src/Commands/MacroCommand.cs
+++ b/src/Commands/MacroCommand.cs
@@ -20,7 +20,7 @@ public class MacroCommand : AbstractCommand
public override IEnumerable Keywords => ["m", "macros"];
- public override IEnumerable Parameters => Enumerable.Empty();
+ public override IEnumerable Parameters => [];
public override string Description => "Re-signs macro signatures";
@@ -92,10 +92,9 @@ private async Task ResignMacros(ProgressTask task, ToolProfile? profile, MacroOp
}
string originalDescription = task.Description;
- string? salt = string.IsNullOrEmpty(options.OldSalt) ? options.NewSalt : options.OldSalt;
string macroScript = scriptBuilder.SetScript(ScriptType.ResignMacros)
.AppendSignAll(options.SignAll, options.UserName)
- .AppendSalt(salt, !string.IsNullOrEmpty(options.OldSalt))
+ .AppendSalts(options.OldSalt, options.NewSalt)
.Build();
await shellRunner.Execute(new(macroScript)
{
diff --git a/src/Commands/SettingsCommand.cs b/src/Commands/SettingsCommand.cs
index 0c4015d..2f8ae9c 100644
--- a/src/Commands/SettingsCommand.cs
+++ b/src/Commands/SettingsCommand.cs
@@ -23,7 +23,7 @@ public class SettingsCommand : AbstractCommand
public override IEnumerable Keywords => ["s", "settings"];
- public override IEnumerable Parameters => Enumerable.Empty();
+ public override IEnumerable Parameters => [];
public override string Description => "Configures the appsettings.json of a project";
@@ -57,12 +57,6 @@ public override async Task Execute(ToolProfile? profile, string? action)
return;
}
- if (profile is null)
- {
- LogError("No active profile.");
- return;
- }
-
var options = await wizard.Run();
switch (options.SettingToChange)
{
@@ -90,7 +84,7 @@ public override async Task PostExecute(ToolProfile? profile, string? action)
}
- private async Task ConfigureHeadlessOptions(ToolProfile profile)
+ private async Task ConfigureHeadlessOptions(ToolProfile? profile)
{
if (StopProcessing)
{
@@ -136,7 +130,7 @@ private async Task ConfigureHeadlessOptions(ToolProfile profile)
}
- private async Task ConfigureConnectionString(ToolProfile profile)
+ private async Task ConfigureConnectionString(ToolProfile? profile)
{
if (StopProcessing)
{
@@ -160,7 +154,7 @@ private async Task ConfigureConnectionString(ToolProfile profile)
}
- private async Task ConfigureKeys(ToolProfile profile)
+ private async Task ConfigureKeys(ToolProfile? profile)
{
if (StopProcessing)
{
@@ -222,7 +216,7 @@ private async Task ConfigureKeys(ToolProfile profile)
}
- private async Task TryUpdateHeadlessOption(CmsHeadlessConfiguration headlessConfiguration, PropertyInfo propToUpdate, ToolProfile profile)
+ private async Task TryUpdateHeadlessOption(CmsHeadlessConfiguration headlessConfiguration, PropertyInfo propToUpdate, ToolProfile? profile)
{
bool isCachingKey = headlessConfiguration.Caching.GetType().GetProperties().Contains(propToUpdate);
object? value = isCachingKey ? propToUpdate.GetValue(headlessConfiguration.Caching) : propToUpdate.GetValue(headlessConfiguration);
@@ -264,7 +258,7 @@ private async Task TryUpdateHeadlessOption(CmsHeadlessConfiguration headle
}
- private string? Truncate(string? value, int maxLength, string truncationSuffix = "...") => value?.Length > maxLength
+ private static string? Truncate(string? value, int maxLength, string truncationSuffix = "...") => value?.Length > maxLength
? value[..maxLength] + truncationSuffix
: value;
}
diff --git a/src/Commands/UpdateCommand.cs b/src/Commands/UpdateCommand.cs
index 5dabd57..fb01f71 100644
--- a/src/Commands/UpdateCommand.cs
+++ b/src/Commands/UpdateCommand.cs
@@ -30,7 +30,7 @@ public class UpdateCommand : AbstractCommand
public override IEnumerable Keywords => ["u", "update"];
- public override IEnumerable Parameters => Enumerable.Empty();
+ public override IEnumerable Parameters => [];
public override string Description => "Updates a project's NuGet packages and database version";
diff --git a/src/Configuration/ToolConfiguration.cs b/src/Configuration/ToolConfiguration.cs
index 844102c..c00c8d5 100644
--- a/src/Configuration/ToolConfiguration.cs
+++ b/src/Configuration/ToolConfiguration.cs
@@ -26,9 +26,15 @@ public class ToolConfiguration
///
- /// The stored in the configuration file.
+ /// The stored in the configuration file.
///
- public InstallOptions? DefaultInstallOptions { get; set; }
+ public InstallProjectOptions? DefaultInstallProjectOptions { get; set; }
+
+
+ ///
+ /// The stored in the configuration file.
+ ///
+ public InstallDatabaseOptions? DefaultInstallDatabaseOptions { get; set; }
///
diff --git a/src/Constants.cs b/src/Constants.cs
index 55be231..8fb41e7 100644
--- a/src/Constants.cs
+++ b/src/Constants.cs
@@ -15,6 +15,7 @@ public static class Constants
public const string EMPHASIS_COLOR = "deepskyblue3_1";
public const string PROMPT_COLOR = "lightgoldenrod2_2";
+ public const string DATABASE_TOOL = "Kentico.Xperience.DbManager";
public const string TEMPLATES_PACKAGE = "kentico.xperience.templates";
public const string TEMPLATE_SAMPLE = "kentico-xperience-sample-mvc";
public const string TEMPLATE_BLANK = "kentico-xperience-mvc";
diff --git a/src/Kentico.Xperience.Manager.csproj b/src/Kentico.Xperience.Manager.csproj
index f5cc8fc..6df0b9a 100644
--- a/src/Kentico.Xperience.Manager.csproj
+++ b/src/Kentico.Xperience.Manager.csproj
@@ -13,6 +13,7 @@
+
diff --git a/src/Options/InstallOptions.cs b/src/Options/InstallDatabaseOptions.cs
similarity index 68%
rename from src/Options/InstallOptions.cs
rename to src/Options/InstallDatabaseOptions.cs
index ab732e3..0103923 100644
--- a/src/Options/InstallOptions.cs
+++ b/src/Options/InstallDatabaseOptions.cs
@@ -5,45 +5,20 @@
namespace Xperience.Manager.Options
{
///
- /// The options used to install Xperience by Kentico project files and databases, used by .
+ /// The options used to install Xperience by Kentico databases, used by .
///
- public class InstallOptions : IWizardOptions
+ public class InstallDatabaseOptions : IWizardOptions
{
///
- /// The version of the Xperience by Kentico templates and database to install.
- ///
- public Version? Version { get; set; }
-
-
- ///
- /// The name of the template to install.
- ///
- public string Template { get; set; } = "kentico-xperience-sample-mvc";
-
-
- ///
- /// The name of the Xperience by Kentico project.
- ///
- public string ProjectName { get; set; } = "xbk";
-
-
- ///
- /// The absolute path of the parent directory to install the project within. E.g. if set to
- /// "C:\inetpub\wwwroot" a new installation will be created in "C:\inetpub\wwwroot\projectname."
- ///
- public string InstallRootPath { get; set; } = Environment.CurrentDirectory;
-
-
- ///
- /// If true, the "--cloud" parameter is used during installation.
+ /// The name of the new database.
///
- public bool UseCloud { get; set; } = false;
+ public string DatabaseName { get; set; } = "xperience";
///
- /// The name of the new database.
+ /// If true, a new database will not be installed and the will be used.
///
- public string DatabaseName { get; set; } = "xperience";
+ public bool UseExistingDatabase { get; set; } = false;
///
diff --git a/src/Options/InstallProjectOptions.cs b/src/Options/InstallProjectOptions.cs
new file mode 100644
index 0000000..363df28
--- /dev/null
+++ b/src/Options/InstallProjectOptions.cs
@@ -0,0 +1,40 @@
+using Xperience.Manager.Commands;
+
+namespace Xperience.Manager.Options
+{
+ ///
+ /// The options used to install Xperience by Kentico project files, used by .
+ ///
+ public class InstallProjectOptions : IWizardOptions
+ {
+ ///
+ /// The version of the Xperience by Kentico templates and database to install.
+ ///
+ public Version? Version { get; set; }
+
+
+ ///
+ /// The name of the template to install.
+ ///
+ public string Template { get; set; } = "kentico-xperience-sample-mvc";
+
+
+ ///
+ /// The name of the Xperience by Kentico project.
+ ///
+ public string ProjectName { get; set; } = "xbk";
+
+
+ ///
+ /// The absolute path of the parent directory to install the project within. E.g. if set to
+ /// "C:\inetpub\wwwroot" a new installation will be created in "C:\inetpub\wwwroot\projectname."
+ ///
+ public string InstallRootPath { get; set; } = Environment.CurrentDirectory;
+
+
+ ///
+ /// If true, the "--cloud" parameter is used during installation.
+ ///
+ public bool UseCloud { get; set; } = false;
+ }
+}
diff --git a/src/Options/RunSqlOptions.cs b/src/Options/RunSqlOptions.cs
new file mode 100644
index 0000000..b72d741
--- /dev/null
+++ b/src/Options/RunSqlOptions.cs
@@ -0,0 +1,21 @@
+using Xperience.Manager.Services;
+
+namespace Xperience.Manager.Options
+{
+ ///
+ /// The options used to run a SQL query via the script.
+ ///
+ public class RunSqlOptions : IWizardOptions
+ {
+ ///
+ /// The connection string which determines the database to connect to.
+ ///
+ public string? ConnString { get; set; }
+
+
+ ///
+ /// The query to execute.
+ ///
+ public string? SqlQuery { get; set; }
+ }
+}
diff --git a/src/Services/AppSettingsManager.cs b/src/Services/AppSettingsManager.cs
index 51d7dc5..b6663e3 100644
--- a/src/Services/AppSettingsManager.cs
+++ b/src/Services/AppSettingsManager.cs
@@ -10,7 +10,7 @@ public class AppSettingsManager : IAppSettingsManager
private readonly string cmsHeadlessSection = "CMSHeadless";
- public async Task GetConnectionString(ToolProfile profile, string name)
+ public async Task GetConnectionString(ToolProfile? profile, string name)
{
var appSettings = await LoadSettings(profile);
var connectionStrings = appSettings["ConnectionStrings"];
@@ -23,7 +23,7 @@ public class AppSettingsManager : IAppSettingsManager
}
- public async Task GetCmsHeadlessConfiguration(ToolProfile profile)
+ public async Task GetCmsHeadlessConfiguration(ToolProfile? profile)
{
var appSettings = await LoadSettings(profile);
var headlessConfig = appSettings.GetValue(cmsHeadlessSection)?.ToObject();
@@ -36,7 +36,7 @@ public async Task GetCmsHeadlessConfiguration(ToolProf
}
- public async Task> GetConfigurationKeys(ToolProfile profile)
+ public async Task> GetConfigurationKeys(ToolProfile? profile)
{
var appSettings = await LoadSettings(profile);
var populatedKeys = Constants.ConfigurationKeys
@@ -50,7 +50,7 @@ public async Task> GetConfigurationKeys(ToolProfil
}
- public async Task SetCmsHeadlessConfiguration(ToolProfile profile, CmsHeadlessConfiguration headlessConfiguration)
+ public async Task SetCmsHeadlessConfiguration(ToolProfile? profile, CmsHeadlessConfiguration headlessConfiguration)
{
var appSettings = await LoadSettings(profile);
appSettings[cmsHeadlessSection] = JToken.FromObject(headlessConfiguration);
@@ -59,7 +59,7 @@ public async Task SetCmsHeadlessConfiguration(ToolProfile profile, CmsHeadlessCo
}
- public async Task SetConnectionString(ToolProfile profile, string name, string connectionString)
+ public async Task SetConnectionString(ToolProfile? profile, string name, string connectionString)
{
var appSettings = await LoadSettings(profile);
var connectionStrings = appSettings["ConnectionStrings"] ?? throw new InvalidOperationException("ConnectionStrings section not found.");
@@ -69,7 +69,7 @@ public async Task SetConnectionString(ToolProfile profile, string name, string c
}
- public async Task SetKeyValue(ToolProfile profile, string keyName, object value)
+ public async Task SetKeyValue(ToolProfile? profile, string keyName, object value)
{
var appSettings = await LoadSettings(profile);
appSettings[keyName] = JToken.FromObject(value);
@@ -78,10 +78,21 @@ public async Task SetKeyValue(ToolProfile profile, string keyName, object value)
}
- private string GetAppSettingsPath(ToolProfile profile) => $"{profile.WorkingDirectory}/appsettings.json";
+ private static string GetAppSettingsPath(ToolProfile profile) => $"{profile.WorkingDirectory}/appsettings.json";
- private async Task LoadSettings(ToolProfile profile)
+ private static Task LoadSettings(ToolProfile? profile)
+ {
+ if (profile is null)
+ {
+ throw new ArgumentNullException(nameof(profile));
+ }
+
+ return LoadSettingsInternal(profile);
+ }
+
+
+ private static async Task LoadSettingsInternal(ToolProfile profile)
{
string settingsPath = GetAppSettingsPath(profile);
if (!File.Exists(settingsPath))
@@ -95,7 +106,18 @@ private async Task LoadSettings(ToolProfile profile)
}
- private async Task WriteAppSettings(ToolProfile profile, JObject appSettings)
+ private static Task WriteAppSettings(ToolProfile? profile, JObject appSettings)
+ {
+ if (profile is null)
+ {
+ throw new ArgumentNullException(nameof(profile));
+ }
+
+ return WriteAppSettingsInternal(profile, appSettings);
+ }
+
+
+ private static async Task WriteAppSettingsInternal(ToolProfile profile, JObject appSettings)
{
string settingsPath = GetAppSettingsPath(profile);
diff --git a/src/Services/ConfigManager.cs b/src/Services/ConfigManager.cs
index ae6ff9b..0ea26f7 100644
--- a/src/Services/ConfigManager.cs
+++ b/src/Services/ConfigManager.cs
@@ -1,6 +1,7 @@
using System.Reflection;
using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
using Xperience.Manager.Configuration;
using Xperience.Manager.Options;
@@ -9,25 +10,25 @@ namespace Xperience.Manager.Services
{
public class ConfigManager : IConfigManager
{
- public async Task AddProfile(ToolProfile profile)
+ public Task AddProfile(ToolProfile? profile)
{
- var config = await GetConfig();
- if (config.Profiles.Any(p => p.ProjectName?.Equals(profile.ProjectName, StringComparison.OrdinalIgnoreCase) ?? false))
+ if (profile is null)
{
- throw new InvalidOperationException($"There is already a profile named '{profile.ProjectName}.'");
+ throw new ArgumentNullException(nameof(profile));
}
- config.Profiles.Add(profile);
-
- await WriteConfig(config);
+ return AddProfileInternal(profile);
}
- public async Task SetCurrentProfile(ToolProfile profile)
+ public Task SetCurrentProfile(ToolProfile? profile)
{
- var config = await GetConfig();
- config.CurrentProfile = profile.ProjectName;
- await WriteConfig(config);
+ if (profile is null)
+ {
+ throw new ArgumentNullException(nameof(profile));
+ }
+
+ return SetCurrentProfileInternal(profile);
}
@@ -69,7 +70,8 @@ public async Task GetConfig()
public async Task EnsureConfigFile()
{
- var toolVersion = Assembly.GetExecutingAssembly().GetName().Version ?? throw new InvalidOperationException("The tool version couldn't be retrieved.");
+ var toolVersion = Assembly.GetExecutingAssembly().GetName().Version ??
+ throw new InvalidOperationException("The tool version couldn't be retrieved.");
if (File.Exists(Constants.CONFIG_FILENAME))
{
await MigrateConfig(toolVersion);
@@ -79,28 +81,48 @@ public async Task EnsureConfigFile()
await WriteConfig(new ToolConfiguration
{
Version = toolVersion,
- DefaultInstallOptions = new()
+ DefaultInstallProjectOptions = new(),
+ DefaultInstallDatabaseOptions = new()
});
}
- public async Task GetDefaultInstallOptions()
+ public async Task GetDefaultInstallProjectOptions()
{
var config = await GetConfig();
- return config.DefaultInstallOptions ?? new();
+ return config.DefaultInstallProjectOptions ?? new();
}
- public async Task RemoveProfile(ToolProfile profile)
+ public async Task GetDefaultInstallDatabaseOptions()
{
var config = await GetConfig();
- // For some reason Profiles.Remove() didn't work, make a new list
- var newProfiles = new List();
- newProfiles.AddRange(config.Profiles.Where(p => !p.ProjectName?.Equals(profile.ProjectName, StringComparison.OrdinalIgnoreCase) ?? true));
+ return config.DefaultInstallDatabaseOptions ?? new();
+ }
- config.Profiles = newProfiles;
+
+ public Task RemoveProfile(ToolProfile? profile)
+ {
+ if (profile is null)
+ {
+ throw new ArgumentNullException(nameof(profile));
+ }
+
+ return RemoveProfileInternal(profile);
+ }
+
+
+ private async Task AddProfileInternal(ToolProfile profile)
+ {
+ var config = await GetConfig();
+ if (config.Profiles.Any(p => p.ProjectName?.Equals(profile.ProjectName, StringComparison.OrdinalIgnoreCase) ?? false))
+ {
+ throw new InvalidOperationException($"There is already a profile named '{profile.ProjectName}.'");
+ }
+
+ config.Profiles.Add(profile);
await WriteConfig(config);
}
@@ -115,12 +137,67 @@ private async Task MigrateConfig(Version toolVersion)
}
// Perform any migrations from old config version to new version here
+ string text = await File.ReadAllTextAsync(Constants.CONFIG_FILENAME);
+ var json = JsonConvert.DeserializeObject(text) ??
+ throw new InvalidOperationException("Unable to read configuration file for migration.");
+ if ((config.Version?.ToString().Equals("4.0.0.0") ?? false) && toolVersion.ToString().Equals("5.0.0.0"))
+ {
+ Migrate40To50(json, config);
+ }
+
config.Version = toolVersion;
await WriteConfig(config);
}
- private Task WriteConfig(ToolConfiguration config) => File.WriteAllTextAsync(Constants.CONFIG_FILENAME, JsonConvert.SerializeObject(config, Formatting.Indented));
+ private async Task RemoveProfileInternal(ToolProfile profile)
+ {
+ var config = await GetConfig();
+
+ // For some reason Profiles.Remove() didn't work, make a new list
+ var newProfiles = new List();
+ newProfiles.AddRange(config.Profiles.Where(p => !p.ProjectName?.Equals(profile.ProjectName, StringComparison.OrdinalIgnoreCase) ?? true));
+
+ config.Profiles = newProfiles;
+
+ await WriteConfig(config);
+ }
+
+
+ private async Task SetCurrentProfileInternal(ToolProfile profile)
+ {
+ var config = await GetConfig();
+ config.CurrentProfile = profile.ProjectName;
+ await WriteConfig(config);
+ }
+
+
+ private static Task WriteConfig(ToolConfiguration config) =>
+ File.WriteAllTextAsync(Constants.CONFIG_FILENAME, JsonConvert.SerializeObject(config, Formatting.Indented));
+
+
+ private static void Migrate40To50(JObject oldConfig, ToolConfiguration newConfig)
+ {
+ var oldInstallOptions = oldConfig["DefaultInstallOptions"];
+
+ var dbOptions = new InstallDatabaseOptions();
+ dbOptions.DatabaseName = oldInstallOptions?["DatabaseName"]?.ToString() ?? dbOptions.DatabaseName;
+ dbOptions.ServerName = oldInstallOptions?["ServerName"]?.ToString() ?? dbOptions.ServerName;
+ newConfig.DefaultInstallDatabaseOptions = dbOptions;
+
+ var projectOptions = new InstallProjectOptions();
+ projectOptions.Template = oldInstallOptions?["Template"]?.ToString() ?? projectOptions.Template;
+ projectOptions.ProjectName = oldInstallOptions?["ProjectName"]?.ToString() ?? projectOptions.ProjectName;
+ projectOptions.InstallRootPath = oldInstallOptions?["InstallRootPath"]?.ToString() ?? projectOptions.InstallRootPath;
+ projectOptions.UseCloud = bool.Parse(oldInstallOptions?["UseCloud"]?.ToString() ?? projectOptions.UseCloud.ToString());
+ string? oldVersion = oldInstallOptions?["Version"]?.ToString();
+ if (!string.IsNullOrEmpty(oldVersion))
+ {
+ projectOptions.Version = Version.Parse(oldVersion);
+ }
+
+ newConfig.DefaultInstallProjectOptions = projectOptions;
+ }
}
}
diff --git a/src/Services/Interfaces/IAppSettingsManager.cs b/src/Services/Interfaces/IAppSettingsManager.cs
index 62ab046..4302acf 100644
--- a/src/Services/Interfaces/IAppSettingsManager.cs
+++ b/src/Services/Interfaces/IAppSettingsManager.cs
@@ -10,38 +10,38 @@ public interface IAppSettingsManager : IService
///
/// Gets the value of the connection string specified by the .
///
- public Task GetConnectionString(ToolProfile profile, string name);
+ public Task GetConnectionString(ToolProfile? profile, string name);
///
/// Gets the headless options. See
/// .
///
- public Task GetCmsHeadlessConfiguration(ToolProfile profile);
+ public Task GetCmsHeadlessConfiguration(ToolProfile? profile);
///
/// Gets configurations keys. See
/// .
///
- public Task> GetConfigurationKeys(ToolProfile profile);
+ public Task> GetConfigurationKeys(ToolProfile? profile);
///
/// Writes the headless configuration to the appsettings.json.
///
- public Task SetCmsHeadlessConfiguration(ToolProfile profile, CmsHeadlessConfiguration headlessConfiguration);
+ public Task SetCmsHeadlessConfiguration(ToolProfile? profile, CmsHeadlessConfiguration headlessConfiguration);
///
/// Writes the connection string to the appsettings.json.
///
- public Task SetConnectionString(ToolProfile profile, string name, string connectionString);
+ public Task SetConnectionString(ToolProfile? profile, string name, string connectionString);
///
/// Writes a configuration key value to the appsettings.json.
///
- public Task SetKeyValue(ToolProfile profile, string keyName, object value);
+ public Task SetKeyValue(ToolProfile? profile, string keyName, object value);
}
}
diff --git a/src/Services/Interfaces/IConfigManager.cs b/src/Services/Interfaces/IConfigManager.cs
index 38cb9d1..b095e2f 100644
--- a/src/Services/Interfaces/IConfigManager.cs
+++ b/src/Services/Interfaces/IConfigManager.cs
@@ -17,7 +17,7 @@ public interface IConfigManager : IService
///
/// Adds a profile to the .
///
- public Task AddProfile(ToolProfile profile);
+ public Task AddProfile(ToolProfile? profile);
///
@@ -34,21 +34,28 @@ public interface IConfigManager : IService
///
- /// Gets the specified by the tool configuration file, or a new instance if
+ /// Gets the specified by the tool configuration file, or a new instance if
/// the configuration can't be read.
///
- public Task GetDefaultInstallOptions();
+ public Task GetDefaultInstallProjectOptions();
+
+
+ ///
+ /// Gets the specified by the tool configuration file, or a new instance if
+ /// the configuration can't be read.
+ ///
+ public Task GetDefaultInstallDatabaseOptions();
///
/// Removes a profile to the .
///
- public Task RemoveProfile(ToolProfile profile);
+ public Task RemoveProfile(ToolProfile? profile);
///
/// Sets the currently active profile.
///
- public Task SetCurrentProfile(ToolProfile profile);
+ public Task SetCurrentProfile(ToolProfile? profile);
}
}
diff --git a/src/Services/Interfaces/IScriptBuilder.cs b/src/Services/Interfaces/IScriptBuilder.cs
index 95d61a2..382788f 100644
--- a/src/Services/Interfaces/IScriptBuilder.cs
+++ b/src/Services/Interfaces/IScriptBuilder.cs
@@ -26,11 +26,11 @@ public interface IScriptBuilder : IService
///
- /// Appends "--old-salt" or "--new-salt" to the script if the script is .
+ /// Appends "--old-salt" and/or "--new-salt" to the script if the script is .
///
- /// The salt value appended to the script. If not provided, the salt from appsettings is used.
- /// If true, "--old-salt" is appended.
- public IScriptBuilder AppendSalt(string? salt, bool isOld);
+ /// The old salt value appended to the script.
+ /// The new salt value appended to the script. If not provided, the salt from appsettings is used.
+ public IScriptBuilder AppendSalts(string? oldSalt, string? newSalt);
///
@@ -64,6 +64,6 @@ public interface IScriptBuilder : IService
/// Replaces script placeholders with the values of the object properties. If a property is null or emtpy,
/// the placeholder remains in the script.
///
- public IScriptBuilder WithPlaceholders(object dataObject);
+ public IScriptBuilder WithPlaceholders(object? dataObject);
}
}
diff --git a/src/Services/ScriptBuilder.cs b/src/Services/ScriptBuilder.cs
index 2a6fa01..42a2e59 100644
--- a/src/Services/ScriptBuilder.cs
+++ b/src/Services/ScriptBuilder.cs
@@ -10,12 +10,14 @@ public class ScriptBuilder : IScriptBuilder
private const string BUILD_SCRIPT = "dotnet build";
private const string MKDIR_SCRIPT = $"mkdir";
- private const string INSTALL_PROJECT_SCRIPT = $"dotnet new {nameof(InstallOptions.Template)} -n {nameof(InstallOptions.ProjectName)}";
- private const string INSTALL_DATABASE_SCRIPT = $"dotnet kentico-xperience-dbmanager -- -s \"{nameof(InstallOptions.ServerName)}\" -d \"{nameof(InstallOptions.DatabaseName)}\" -a \"{nameof(InstallOptions.AdminPassword)}\"";
- private const string UNINSTALL_TEMPLATE_SCRIPT = "dotnet new uninstall kentico.xperience.templates";
- private const string INSTALL_TEMPLATE_SCRIPT = "dotnet new install kentico.xperience.templates";
+ private const string INSTALL_PROJECT_SCRIPT = $"dotnet new {nameof(InstallProjectOptions.Template)} -n {nameof(InstallProjectOptions.ProjectName)}";
+ private const string INSTALL_DATABASE_SCRIPT = $"dotnet kentico-xperience-dbmanager -- -s \"{nameof(InstallDatabaseOptions.ServerName)}\" -d \"{nameof(InstallDatabaseOptions.DatabaseName)}\" -a \"{nameof(InstallDatabaseOptions.AdminPassword)}\" --use-existing-database {nameof(InstallDatabaseOptions.UseExistingDatabase)}";
+ private const string UNINSTALL_TEMPLATE_SCRIPT = $"dotnet new uninstall {Constants.TEMPLATES_PACKAGE}";
+ private const string INSTALL_TEMPLATE_SCRIPT = $"dotnet new install {Constants.TEMPLATES_PACKAGE}";
private const string UPDATE_PACKAGE_SCRIPT = $"dotnet add package {nameof(UpdateOptions.PackageName)}";
private const string UPDATE_DATABASE_SCRIPT = "dotnet run --no-build --kxp-update -- --skip-confirmation";
+ private const string INSTALL_DBTOOL_SCRIPT = $"dotnet tool install {Constants.DATABASE_TOOL} -g";
+ private const string UNINSTALL_DBTOOL_SCRIPT = $"dotnet tool uninstall {Constants.DATABASE_TOOL} -g";
private const string CI_STORE_SCRIPT = "dotnet run --no-build --kxp-ci-store";
private const string CI_RESTORE_SCRIPT = "dotnet run --no-build --kxp-ci-restore";
private const string CD_NEW_CONFIG_SCRIPT = $"dotnet run --no-build -- --kxp-cd-config --path \"{nameof(ContinuousDeploymentConfig.ConfigPath)}\"";
@@ -23,6 +25,8 @@ public class ScriptBuilder : IScriptBuilder
private const string CD_RESTORE_SCRIPT = $"dotnet run -- --kxp-cd-restore --repository-path \"{nameof(ContinuousDeploymentConfig.RepositoryPath)}\"";
private const string MACRO_SCRIPT = "dotnet run --no-build -- --kxp-resign-macros";
private const string CODEGEN_SCRIPT = $"dotnet run -- --kxp-codegen --skip-confirmation --type \"{nameof(CodeGenerateOptions.Type)}\" --location \"{nameof(CodeGenerateOptions.Location)}\" --include \"{nameof(CodeGenerateOptions.Include)}\" --exclude \"{nameof(CodeGenerateOptions.Exclude)}\" --with-provider-class {nameof(CodeGenerateOptions.WithProviderClass)}";
+ private const string DELETE_FOLDER_SCRIPT = $"rm \"{nameof(ToolProfile.WorkingDirectory)}\" -r -Force";
+ private const string RUN_SQL_QUERY = $"Invoke-Sqlcmd -ConnectionString \"{nameof(RunSqlOptions.ConnString)}\" -Query \"{nameof(RunSqlOptions.SqlQuery)}\"";
public IScriptBuilder AppendCloud(bool useCloud)
@@ -58,14 +62,22 @@ public IScriptBuilder AppendNamespace(string? nameSpace)
}
- public IScriptBuilder AppendSalt(string? salt, bool isOld)
+ public IScriptBuilder AppendSalts(string? oldSalt, string? newSalt)
{
- if (string.IsNullOrEmpty(salt) || !currentScriptType.Equals(ScriptType.ResignMacros))
+ if (!currentScriptType.Equals(ScriptType.ResignMacros))
{
return this;
}
- currentScript += $" {(isOld ? "--old-salt" : "--new-salt")} \"{salt}\"";
+ if (!string.IsNullOrEmpty(oldSalt))
+ {
+ currentScript += $" --old-salt \"{oldSalt}\"";
+ }
+
+ if (!string.IsNullOrEmpty(newSalt))
+ {
+ currentScript += $" --new-salt \"{newSalt}\"";
+ }
return this;
}
@@ -95,7 +107,7 @@ public IScriptBuilder AppendVersion(Version? version)
{
currentScript += $"::{version}";
}
- else if (currentScriptType.Equals(ScriptType.PackageUpdate))
+ else if (currentScriptType.Equals(ScriptType.PackageUpdate) || currentScriptType.Equals(ScriptType.InstallDatabaseTool))
{
currentScript += $" --version {version}";
}
@@ -106,17 +118,22 @@ public IScriptBuilder AppendVersion(Version? version)
public string Build()
{
- if (!ValidateScript())
+ if (string.IsNullOrEmpty(currentScript))
{
- throw new InvalidOperationException("The script is empty or contains placeholder values.");
+ throw new InvalidOperationException("The script is empty.");
}
return currentScript;
}
- public IScriptBuilder WithPlaceholders(object dataObject)
+ public IScriptBuilder WithPlaceholders(object? dataObject)
{
+ if (dataObject is null)
+ {
+ return this;
+ }
+
// Replace all placeholders in script with object values if non-null or empty
foreach (var prop in dataObject.GetType().GetProperties())
{
@@ -156,20 +173,16 @@ public IScriptBuilder SetScript(ScriptType type)
ScriptType.ContinuousDeploymentRestore => CD_RESTORE_SCRIPT,
ScriptType.ResignMacros => MACRO_SCRIPT,
ScriptType.GenerateCode => CODEGEN_SCRIPT,
+ ScriptType.DeleteDirectory => DELETE_FOLDER_SCRIPT,
+ ScriptType.ExecuteSql => RUN_SQL_QUERY,
+ ScriptType.UninstallDatabaseTool => UNINSTALL_DBTOOL_SCRIPT,
+ ScriptType.InstallDatabaseTool => INSTALL_DBTOOL_SCRIPT,
ScriptType.None => string.Empty,
_ => string.Empty,
};
return this;
}
-
-
- private bool ValidateScript()
- {
- var propertyNames = typeof(InstallOptions).GetProperties().Select(p => p.Name);
-
- return !string.IsNullOrEmpty(currentScript) && !propertyNames.Any(currentScript.Contains);
- }
}
@@ -264,9 +277,34 @@ public enum ScriptType
///
ResignMacros,
+
///
/// The script which generates code files for Xperience objects.
///
GenerateCode,
+
+
+ ///
+ /// The script which deletes a local folder and its contents.
+ ///
+ DeleteDirectory,
+
+
+ ///
+ /// The script which executes a SQL query against a database.
+ ///
+ ExecuteSql,
+
+
+ ///
+ /// The script which uninstalls the Kentico.Xperience.DbManager global tool.
+ ///
+ UninstallDatabaseTool,
+
+
+ ///
+ /// The script which installs the Kentico.Xperience.DbManager global tool.
+ ///
+ InstallDatabaseTool,
}
}
diff --git a/src/Wizards/Base/AbstractWizard.cs b/src/Wizards/Base/AbstractWizard.cs
index 6911791..06cab1f 100644
--- a/src/Wizards/Base/AbstractWizard.cs
+++ b/src/Wizards/Base/AbstractWizard.cs
@@ -15,12 +15,12 @@ namespace Xperience.Manager.Wizards
public TOptions Options { get; set; } = new();
- public abstract Task InitSteps();
+ public abstract Task InitSteps(params string[] args);
- public async Task Run()
+ public async Task Run(params string[] args)
{
- await InitSteps();
+ await InitSteps(args);
do
{
await Steps.Current.Execute();
diff --git a/src/Wizards/Base/IWizard.cs b/src/Wizards/Base/IWizard.cs
index 7718e7a..6e79918 100644
--- a/src/Wizards/Base/IWizard.cs
+++ b/src/Wizards/Base/IWizard.cs
@@ -17,17 +17,18 @@ public interface IWizard where TOptions : IWizardOptions
public TOptions Options { get; set; }
-
///
/// Initializes the with the s required to
/// populate the .
///
- public Task InitSteps();
+ /// Optional arguments to pass to the step initialization.
+ public Task InitSteps(params string[] args);
///
/// Requests user input to generate the .
///
- public Task Run();
+ /// Optional arguments to pass to the wizard.
+ public Task Run(params string[] args);
}
}
diff --git a/src/Services/CodeGenerateWizard.cs b/src/Wizards/CodeGenerateWizard.cs
similarity index 97%
rename from src/Services/CodeGenerateWizard.cs
rename to src/Wizards/CodeGenerateWizard.cs
index 1389913..89a6f6c 100644
--- a/src/Services/CodeGenerateWizard.cs
+++ b/src/Wizards/CodeGenerateWizard.cs
@@ -19,7 +19,7 @@ public class CodeGenerateWizard : AbstractWizard
];
- public override Task InitSteps()
+ public override Task InitSteps(params string[] args)
{
Steps.Add(new Step(new()
{
diff --git a/src/Wizards/InstallDatabaseWizard.cs b/src/Wizards/InstallDatabaseWizard.cs
new file mode 100644
index 0000000..7c7cd37
--- /dev/null
+++ b/src/Wizards/InstallDatabaseWizard.cs
@@ -0,0 +1,59 @@
+using Spectre.Console;
+
+using Xperience.Manager.Options;
+using Xperience.Manager.Steps;
+
+namespace Xperience.Manager.Wizards
+{
+ ///
+ /// A wizard which generates an for installing Xperience by Kentico databases.
+ ///
+ public class InstallDatabaseWizard : AbstractWizard
+ {
+ public const string SKIP_EXISTINGDB_STEP = "skipexistingdbstep";
+
+
+ public override Task InitSteps(params string[] args)
+ {
+ var serverPrompt = new TextPrompt($"Enter the [{Constants.PROMPT_COLOR}]SQL server[/] name:");
+ if (!string.IsNullOrEmpty(Options.ServerName))
+ {
+ serverPrompt.DefaultValue(Options.ServerName);
+ }
+ Steps.Add(new Step(new()
+ {
+ Prompt = serverPrompt,
+ ValueReceiver = (v) => Options.ServerName = v
+ }));
+
+ Steps.Add(new Step(new()
+ {
+ Prompt = new TextPrompt($"Enter the [{Constants.PROMPT_COLOR}]database[/] name:")
+ .AllowEmpty()
+ .DefaultValue(Options.DatabaseName),
+ ValueReceiver = (v) => Options.DatabaseName = v
+ }));
+
+ var useExistingPrompt = new ConfirmationPrompt($"Use [{Constants.PROMPT_COLOR}]existing[/] database?")
+ {
+ DefaultValue = Options.UseExistingDatabase
+ };
+ Steps.Add(new Step(new()
+ {
+ Prompt = useExistingPrompt,
+ ValueReceiver = (v) => Options.UseExistingDatabase = v,
+ SkipChecker = () => args.Contains(SKIP_EXISTINGDB_STEP)
+ }));
+
+ Steps.Add(new Step(new()
+ {
+ Prompt = new TextPrompt($"Enter the admin [{Constants.PROMPT_COLOR}]password[/]:")
+ .AllowEmpty()
+ .DefaultValue(Options.AdminPassword),
+ ValueReceiver = (v) => Options.AdminPassword = v
+ }));
+
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/src/Wizards/InstallWizard.cs b/src/Wizards/InstallProjectWizard.cs
similarity index 64%
rename from src/Wizards/InstallWizard.cs
rename to src/Wizards/InstallProjectWizard.cs
index 61c73d7..eebcb8c 100644
--- a/src/Wizards/InstallWizard.cs
+++ b/src/Wizards/InstallProjectWizard.cs
@@ -7,9 +7,9 @@
namespace Xperience.Manager.Wizards
{
///
- /// A wizard which generates an for installing Xperience by Kentico.
+ /// A wizard which generates an for installing Xperience by Kentico project files.
///
- public class InstallWizard : AbstractWizard
+ public class InstallProjectWizard : AbstractWizard
{
private readonly IEnumerable templates = [
Constants.TEMPLATE_SAMPLE,
@@ -18,7 +18,7 @@ public class InstallWizard : AbstractWizard
];
- public override async Task InitSteps()
+ public override async Task InitSteps(params string[] args)
{
var versions = await NuGetVersionHelper.GetPackageVersions(Constants.TEMPLATES_PACKAGE);
var filtered = versions.Where(v => !v.IsPrerelease && !v.IsLegacyVersion && v.Major >= 25)
@@ -68,37 +68,6 @@ public override async Task InitSteps()
ValueReceiver = (v) => Options.UseCloud = v,
SkipChecker = IsAdminTemplate
}));
-
-
- var serverPrompt = new TextPrompt($"Enter the [{Constants.PROMPT_COLOR}]SQL server[/] name:");
- if (!string.IsNullOrEmpty(Options.ServerName))
- {
- serverPrompt.DefaultValue(Options.ServerName);
- }
- Steps.Add(new Step(new()
- {
- Prompt = serverPrompt,
- ValueReceiver = (v) => Options.ServerName = v,
- SkipChecker = IsAdminTemplate
- }));
-
- Steps.Add(new Step(new()
- {
- Prompt = new TextPrompt($"Enter the [{Constants.PROMPT_COLOR}]database[/] name:")
- .AllowEmpty()
- .DefaultValue(Options.DatabaseName),
- ValueReceiver = (v) => Options.DatabaseName = v,
- SkipChecker = IsAdminTemplate
- }));
-
- Steps.Add(new Step(new()
- {
- Prompt = new TextPrompt($"Enter the admin [{Constants.PROMPT_COLOR}]password[/]:")
- .AllowEmpty()
- .DefaultValue(Options.AdminPassword),
- ValueReceiver = (v) => Options.AdminPassword = v,
- SkipChecker = IsAdminTemplate
- }));
}
diff --git a/src/Wizards/MacroWizard.cs b/src/Wizards/MacroWizard.cs
index 1b8a57f..2b0644c 100644
--- a/src/Wizards/MacroWizard.cs
+++ b/src/Wizards/MacroWizard.cs
@@ -10,7 +10,7 @@ namespace Xperience.Manager.Wizards
///
public class MacroWizard : AbstractWizard
{
- public override Task InitSteps()
+ public override Task InitSteps(params string[] args)
{
Steps.Add(new Step(new()
{
diff --git a/src/Wizards/NewProfileWizard.cs b/src/Wizards/NewProfileWizard.cs
index b72f518..7fbd1fe 100644
--- a/src/Wizards/NewProfileWizard.cs
+++ b/src/Wizards/NewProfileWizard.cs
@@ -10,7 +10,7 @@ namespace Xperience.Manager.Wizards
///
public class NewProfileWizard : AbstractWizard
{
- public override Task InitSteps()
+ public override Task InitSteps(params string[] args)
{
Steps.Add(new Step(new()
{
diff --git a/src/Wizards/RepositoryConfigurationWizard.cs b/src/Wizards/RepositoryConfigurationWizard.cs
index 6a269c6..a297eda 100644
--- a/src/Wizards/RepositoryConfigurationWizard.cs
+++ b/src/Wizards/RepositoryConfigurationWizard.cs
@@ -20,7 +20,7 @@ public class RepositoryConfigurationWizard : AbstractWizard(new()
{
diff --git a/src/Wizards/SettingsWizard.cs b/src/Wizards/SettingsWizard.cs
index 1b18088..99c9257 100644
--- a/src/Wizards/SettingsWizard.cs
+++ b/src/Wizards/SettingsWizard.cs
@@ -10,7 +10,7 @@ namespace Xperience.Manager.Wizards
///
public class SettingsWizard : AbstractWizard
{
- public override Task InitSteps()
+ public override Task InitSteps(params string[] args)
{
Steps.Add(new Step(new()
{
diff --git a/src/Wizards/UpdateWizard.cs b/src/Wizards/UpdateWizard.cs
index 1ac4bc3..efbbd3a 100644
--- a/src/Wizards/UpdateWizard.cs
+++ b/src/Wizards/UpdateWizard.cs
@@ -11,7 +11,7 @@ namespace Xperience.Manager.Wizards
///
public class UpdateWizard : AbstractWizard
{
- public override async Task InitSteps()
+ public override async Task InitSteps(params string[] args)
{
var versions = await NuGetVersionHelper.GetPackageVersions(Constants.TEMPLATES_PACKAGE);
var filtered = versions.Where(v => !v.IsPrerelease && !v.IsLegacyVersion && v.Major >= 25)
diff --git a/test/Data/config_with_installoptions.json b/test/Data/config_with_installoptions.json
index 2a47ecd..ee60aab 100644
--- a/test/Data/config_with_installoptions.json
+++ b/test/Data/config_with_installoptions.json
@@ -1,14 +1,17 @@
{
- "Version": "3.4.1.0",
+ "Version": "4.1.0.0",
"CurrentProfile": null,
- "DefaultInstallOptions": {
- "DatabaseName": "mydb",
+ "DefaultInstallProjectOptions": {
"ProjectName": "myproject",
- "ServerName": "myserver",
"Template": "kentico-xperience-sample-mvc",
"UseCloud": true,
"Version": null
},
+ "DefaultInstallDatabaseOptions": {
+ "DatabaseName": "mydb",
+ "UseExistingDatabase": false,
+ "ServerName": "myserver"
+ },
"Profiles": [],
"CDRootPath": "c:\\ContinuousDeployment"
}
\ No newline at end of file
diff --git a/test/Data/config_with_multiple_profiles.json b/test/Data/config_with_multiple_profiles.json
index c0749e8..ebc75cd 100644
--- a/test/Data/config_with_multiple_profiles.json
+++ b/test/Data/config_with_multiple_profiles.json
@@ -1,14 +1,8 @@
{
- "Version": "3.4.1.0",
+ "Version": "4.1.0.0",
"CurrentProfile": null,
- "DefaultInstallOptions": {
- "DatabaseName": null,
- "ProjectName": null,
- "ServerName": null,
- "Template": null,
- "UseCloud": false,
- "Version": null
- },
+ "DefaultInstallProjectOptions": null,
+ "DefaultInstallDatabaseOptions": null,
"Profiles": [
{
"ProjectName": "1",
diff --git a/test/Data/config_with_one_profile.json b/test/Data/config_with_one_profile.json
index 1d858d6..03aad3b 100644
--- a/test/Data/config_with_one_profile.json
+++ b/test/Data/config_with_one_profile.json
@@ -1,14 +1,8 @@
{
- "Version": "3.4.1.0",
+ "Version": "4.1.0.0",
"CurrentProfile": null,
- "DefaultInstallOptions": {
- "DatabaseName": null,
- "ProjectName": null,
- "ServerName": null,
- "Template": null,
- "UseCloud": false,
- "Version": null
- },
+ "DefaultInstallProjectOptions": null,
+ "DefaultInstallDatabaseOptions": null,
"Profiles": [
{
"ProjectName": "1",
diff --git a/test/Kentico.Xperience.Manager.Tests.csproj b/test/Kentico.Xperience.Manager.Tests.csproj
index 73f8124..a6a3012 100644
--- a/test/Kentico.Xperience.Manager.Tests.csproj
+++ b/test/Kentico.Xperience.Manager.Tests.csproj
@@ -1,4 +1,4 @@
-
+
net7.0;net8.0
@@ -9,10 +9,10 @@
-
+
-
+
diff --git a/test/Tests/Commands/BuildCommandTests.cs b/test/Tests/Commands/BuildCommandTests.cs
new file mode 100644
index 0000000..14f87ea
--- /dev/null
+++ b/test/Tests/Commands/BuildCommandTests.cs
@@ -0,0 +1,32 @@
+using NSubstitute;
+
+using NUnit.Framework;
+
+using Xperience.Manager.Commands;
+using Xperience.Manager.Services;
+
+namespace Xperience.Manager.Tests.Commands
+{
+ ///
+ /// Tests for .
+ ///
+ public class BuildCommandTests : TestBase
+ {
+ private readonly IShellRunner shellRunner = Substitute.For();
+
+
+ [SetUp]
+ public void BuildCommandTestsSetUp() => shellRunner.Execute(Arg.Any()).Returns((x) => GetDummyProcess());
+
+
+ [Test]
+ public async Task Execute_CallsBuildScript()
+ {
+ var command = new BuildCommand(shellRunner, new ScriptBuilder());
+ await command.PreExecute(new(), string.Empty);
+ await command.Execute(new(), string.Empty);
+
+ shellRunner.Received().Execute(Arg.Is(x => x.Script.Equals($"dotnet build")));
+ }
+ }
+}
diff --git a/test/Tests/Commands/CodeGenerateCommandTests.cs b/test/Tests/Commands/CodeGenerateCommandTests.cs
new file mode 100644
index 0000000..fdb9c3f
--- /dev/null
+++ b/test/Tests/Commands/CodeGenerateCommandTests.cs
@@ -0,0 +1,56 @@
+using NSubstitute;
+
+using NUnit.Framework;
+
+using Xperience.Manager.Commands;
+using Xperience.Manager.Options;
+using Xperience.Manager.Services;
+using Xperience.Manager.Wizards;
+
+namespace Xperience.Manager.Tests.Commands
+{
+ ///
+ /// Tests for .
+ ///
+ public class CodeGenerateCommandTests : TestBase
+ {
+ private const string EXCLUDE = "ex";
+ private const string INCLUDE = "*";
+ private const string LOCATION = "/dir";
+ private const string NAMESPACE = "ns";
+ private const string TYPE = CodeGenerateOptions.TYPE_REUSABLE_CONTENT_TYPES;
+ private const bool WITH_PROVIDER = false;
+ private readonly IShellRunner shellRunner = Substitute.For();
+ private readonly IWizard generateWizard = Substitute.For>();
+
+
+ [SetUp]
+ public void CodeGenerateCommandTestsSetUp()
+ {
+ generateWizard.Run().Returns(new CodeGenerateOptions
+ {
+ Exclude = EXCLUDE,
+ Include = INCLUDE,
+ Location = LOCATION,
+ Namespace = NAMESPACE,
+ Type = TYPE,
+ WithProviderClass = WITH_PROVIDER
+ });
+
+ shellRunner.Execute(Arg.Any()).Returns((x) => GetDummyProcess());
+ }
+
+
+ [Test]
+ public async Task Execute_CallsCodeGenScript()
+ {
+ var command = new CodeGenerateCommand(shellRunner, new ScriptBuilder(), generateWizard);
+ await command.PreExecute(new(), string.Empty);
+ await command.Execute(new(), string.Empty);
+
+ string expectedCodeGenScript = $"dotnet run -- --kxp-codegen --skip-confirmation --type \"{TYPE}\" --location \"{LOCATION}\" --include \"{INCLUDE}\" --exclude \"{EXCLUDE}\" --with-provider-class {WITH_PROVIDER} --namespace \"{NAMESPACE}\"";
+
+ shellRunner.Received().Execute(Arg.Is(x => x.Script.Equals(expectedCodeGenScript)));
+ }
+ }
+}
diff --git a/test/Tests/Commands/InstallCommandTests.cs b/test/Tests/Commands/InstallCommandTests.cs
index 641eca7..3608bff 100644
--- a/test/Tests/Commands/InstallCommandTests.cs
+++ b/test/Tests/Commands/InstallCommandTests.cs
@@ -19,22 +19,28 @@ public class InstallCommandTests : TestBase
private const string SERVER_NAME = "TESTSERVER";
private const string TEMPLATE = "TEMPLATE";
private const string PROJECT_NAME = "PROJECT";
+ private const bool USE_EXISTING = false;
private readonly Version version = new(1, 0, 0);
private readonly IShellRunner shellRunner = Substitute.For();
- private readonly IWizard installWizard = Substitute.For>();
+ private readonly IWizard projectWizard = Substitute.For>();
+ private readonly IWizard dbWizard = Substitute.For>();
[SetUp]
public void InstallCommandTestsSetUp()
{
- installWizard.Run().Returns(new InstallOptions
+ projectWizard.Run().Returns(new InstallProjectOptions
{
ProjectName = PROJECT_NAME,
+ Version = version,
+ Template = TEMPLATE
+ });
+ dbWizard.Run().Returns(new InstallDatabaseOptions
+ {
AdminPassword = PASSWORD,
DatabaseName = DB_NAME,
ServerName = SERVER_NAME,
- Version = version,
- Template = TEMPLATE
+ UseExistingDatabase = USE_EXISTING
});
shellRunner.Execute(Arg.Any()).Returns((x) => GetDummyProcess());
@@ -44,14 +50,14 @@ public void InstallCommandTestsSetUp()
[Test]
public async Task Execute_CallsInstallationScripts()
{
- var command = new InstallCommand(shellRunner, new ScriptBuilder(), installWizard, Substitute.For());
+ var command = new InstallCommand(shellRunner, new ScriptBuilder(), projectWizard, dbWizard, Substitute.For());
await command.PreExecute(new(), string.Empty);
await command.Execute(new(), string.Empty);
string expectedProjectFileScript = $"dotnet new {TEMPLATE} -n {PROJECT_NAME}";
string expectedUninstallScript = "dotnet new uninstall kentico.xperience.templates";
string expectedTemplateScript = $"dotnet new install kentico.xperience.templates::{version}";
- string expectedDatabaseScript = $"dotnet kentico-xperience-dbmanager -- -s \"{SERVER_NAME}\" -d \"{DB_NAME}\" -a \"{PASSWORD}\"";
+ string expectedDatabaseScript = $"dotnet kentico-xperience-dbmanager -- -s \"{SERVER_NAME}\" -d \"{DB_NAME}\" -a \"{PASSWORD}\" --use-existing-database {USE_EXISTING}";
Assert.Multiple(() =>
{
diff --git a/test/Tests/Commands/MacroCommandTests.cs b/test/Tests/Commands/MacroCommandTests.cs
new file mode 100644
index 0000000..6af1811
--- /dev/null
+++ b/test/Tests/Commands/MacroCommandTests.cs
@@ -0,0 +1,64 @@
+using NSubstitute;
+
+using NUnit.Framework;
+
+using Xperience.Manager.Commands;
+using Xperience.Manager.Options;
+using Xperience.Manager.Services;
+using Xperience.Manager.Wizards;
+
+namespace Xperience.Manager.Tests.Commands
+{
+ ///
+ /// Tests for .
+ ///
+ public class MacroCommandTests : TestBase
+ {
+ private const string USER = "admin";
+ private const string OLD_SALT = "old";
+ private const string NEW_SALT = "new";
+ private readonly IShellRunner shellRunner = Substitute.For();
+ private readonly IWizard macroWizard = Substitute.For>();
+
+
+ [SetUp]
+ public void MacroCommandTestsSetUp() => shellRunner.Execute(Arg.Any()).Returns((x) => GetDummyProcess());
+
+
+ [Test]
+ public async Task Execute_SignAll_CallsSignAllScript()
+ {
+ macroWizard.Run().Returns(new MacroOptions
+ {
+ SignAll = true,
+ UserName = USER,
+ NewSalt = NEW_SALT
+ });
+ var command = new MacroCommand(macroWizard, shellRunner, new ScriptBuilder());
+ await command.PreExecute(new(), string.Empty);
+ await command.Execute(new(), string.Empty);
+
+ string expectedMacroScript = $"dotnet run --no-build -- --kxp-resign-macros --sign-all --username \"{USER}\" --new-salt \"{NEW_SALT}\"";
+
+ shellRunner.Received().Execute(Arg.Is(x => x.Script.Equals(expectedMacroScript)));
+ }
+
+
+ [Test]
+ public async Task Execute_OldAndNewSalt_ScriptContainsSalts()
+ {
+ macroWizard.Run().Returns(new MacroOptions
+ {
+ OldSalt = OLD_SALT,
+ NewSalt = NEW_SALT
+ });
+ var command = new MacroCommand(macroWizard, shellRunner, new ScriptBuilder());
+ await command.PreExecute(new(), string.Empty);
+ await command.Execute(new(), string.Empty);
+
+ string expectedMacroScript = $"dotnet run --no-build -- --kxp-resign-macros --old-salt \"{OLD_SALT}\" --new-salt \"{NEW_SALT}\"";
+
+ shellRunner.Received().Execute(Arg.Is(x => x.Script.Equals(expectedMacroScript)));
+ }
+ }
+}
diff --git a/test/Tests/Services/IConfigManagerTests.cs b/test/Tests/Services/IConfigManagerTests.cs
index 7bfab20..35dc429 100644
--- a/test/Tests/Services/IConfigManagerTests.cs
+++ b/test/Tests/Services/IConfigManagerTests.cs
@@ -97,15 +97,16 @@ public async Task GetDefaultInstallOptions_GetsCustomOptions()
{
File.Copy("Data/config_with_installoptions.json", Constants.CONFIG_FILENAME);
- var options = await configManager.GetDefaultInstallOptions();
+ var dbOptions = await configManager.GetDefaultInstallDatabaseOptions();
+ var projectOptions = await configManager.GetDefaultInstallProjectOptions();
Assert.Multiple(() =>
{
- Assert.That(options.UseCloud, Is.True);
- Assert.That(options.DatabaseName, Is.EqualTo("mydb"));
- Assert.That(options.ServerName, Is.EqualTo("myserver"));
- Assert.That(options.ProjectName, Is.EqualTo("myproject"));
- Assert.That(options.Template, Is.EqualTo("kentico-xperience-sample-mvc"));
+ Assert.That(dbOptions.DatabaseName, Is.EqualTo("mydb"));
+ Assert.That(dbOptions.ServerName, Is.EqualTo("myserver"));
+ Assert.That(projectOptions.UseCloud, Is.True);
+ Assert.That(projectOptions.ProjectName, Is.EqualTo("myproject"));
+ Assert.That(projectOptions.Template, Is.EqualTo("kentico-xperience-sample-mvc"));
});
}
}
diff --git a/test/Tests/Services/IScriptBuilderTests.cs b/test/Tests/Services/IScriptBuilderTests.cs
index 00dc0dc..d07fd24 100644
--- a/test/Tests/Services/IScriptBuilderTests.cs
+++ b/test/Tests/Services/IScriptBuilderTests.cs
@@ -11,13 +11,16 @@ namespace Xperience.Manager.Tests.Services
public class IScriptBuilderTests
{
private readonly IScriptBuilder scriptBuilder = new ScriptBuilder();
- private readonly InstallOptions validInstallOptions = new()
+ private readonly InstallProjectOptions validProjectOptions = new()
{
ProjectName = "TEST",
+ Template = "kentico-xperience-sample-mvc"
+ };
+ private readonly InstallDatabaseOptions validDatabaseOptions = new()
+ {
AdminPassword = "PW",
DatabaseName = "DB",
- ServerName = "TESTSERVER",
- Template = "kentico-xperience-sample-mvc"
+ ServerName = "SERVER"
};
private readonly UpdateOptions validUpdateOptions = new() { PackageName = "kentico.xperience.webapp" };
@@ -25,29 +28,19 @@ public class IScriptBuilderTests
[Test]
public void ProjectInstallScript_WithValidOptions_ReturnsValidScript()
{
- string script = scriptBuilder.SetScript(ScriptType.ProjectInstall).WithPlaceholders(validInstallOptions).Build();
- string expected = $"dotnet new {validInstallOptions.Template} -n {validInstallOptions.ProjectName}";
+ string script = scriptBuilder.SetScript(ScriptType.ProjectInstall).WithPlaceholders(validProjectOptions).Build();
+ string expected = $"dotnet new {validProjectOptions.Template} -n {validProjectOptions.ProjectName}";
Assert.That(script, Is.EqualTo(expected));
}
- [Test]
- public void ProjectInstallScript_WithInvalidOptions_ThrowsException()
- {
- var options = new InstallOptions { Template = string.Empty };
- var builder = scriptBuilder.SetScript(ScriptType.ProjectInstall).WithPlaceholders(options);
-
- Assert.That(() => builder.Build(), Throws.InvalidOperationException);
- }
-
-
[Test]
public void TemplateInstall_AppendVersion_AddsParameter()
{
var version = new Version(1, 0, 0);
string script = scriptBuilder.SetScript(ScriptType.TemplateInstall)
- .WithPlaceholders(validInstallOptions)
+ .WithPlaceholders(validProjectOptions)
.AppendVersion(version)
.Build();
string expected = $"dotnet new install kentico.xperience.templates::{version}";
@@ -73,20 +66,10 @@ public void PackageUpdate_AppendVersion_AddsParameter()
[Test]
public void DatabaseInstallScript_WithValidOptions_ReturnsValidScript()
{
- string script = scriptBuilder.SetScript(ScriptType.DatabaseInstall).WithPlaceholders(validInstallOptions).Build();
- string expected = $"dotnet kentico-xperience-dbmanager -- -s \"{validInstallOptions.ServerName}\" -d \"{validInstallOptions.DatabaseName}\" -a \"{validInstallOptions.AdminPassword}\"";
+ string script = scriptBuilder.SetScript(ScriptType.DatabaseInstall).WithPlaceholders(validDatabaseOptions).Build();
+ string expected = $"dotnet kentico-xperience-dbmanager -- -s \"{validDatabaseOptions.ServerName}\" -d \"{validDatabaseOptions.DatabaseName}\" -a \"{validDatabaseOptions.AdminPassword}\" --use-existing-database {validDatabaseOptions.UseExistingDatabase}";
Assert.That(script, Is.EqualTo(expected));
}
-
-
- [Test]
- public void DatabaseInstallScript_WithInvalidOptions_ThrowsException()
- {
- var options = new InstallOptions();
- var builder = scriptBuilder.SetScript(ScriptType.DatabaseInstall).WithPlaceholders(options);
-
- Assert.That(() => builder.Build(), Throws.InvalidOperationException);
- }
}
}