diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 92058e8a..00000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,87 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: "CodeQL" - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - schedule: - - cron: '17 20 * * 5' - -jobs: - analyze: - name: Analyze - # Runner size impacts CodeQL analysis time. To learn more, please see: - # - https://gh.io/recommended-hardware-resources-for-running-codeql - # - https://gh.io/supported-runners-and-hardware-resources - # - https://gh.io/using-larger-runners - # Consider using larger runners for possible analysis time improvements. - runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} - timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} - permissions: - # required for all workflows - security-events: write - - # only required for workflows in private repositories - actions: read - contents: read - - strategy: - fail-fast: false - matrix: - language: [ 'csharp' ] - # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] - # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both - # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v3 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - - # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - # uses: github/codeql-action/autobuild@v3 - run: | - dotnet build ./clio/clio.csproj /p:ErrorOnDuplicatePublishOutputFiles=false - - - # ℹ️ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 - with: - category: "/language:${{matrix.language}}" diff --git a/README.md b/README.md index 15b21f10..dedd1a89 100644 --- a/README.md +++ b/README.md @@ -177,7 +177,8 @@ docker run -it --rm clio reg-web-app -help - [Using for CI/CD systems](#using-for-cicd-systems) - [GitOps](#gitops) - [Installation of Creatio](#installation-of-creatio-using-clio) - --[Manage requirment Windows features](#manage-requirment-windows-features) + - [Manage required Windows features](#manage-required-windows-features) + - [Uninstall Creatio](#uninstall-creatio) # Arguments @@ -1121,7 +1122,7 @@ To create an empty cluster, we recommend using [Rancher Desktop](https://rancher > If you already have running MSSQL/PostgresSQL/Redis servers on you local machine you have to configure kubernetes services ports to avoid collisions. Reffer to services.yaml in related directories -## Manage requirment Windows features +## Manage required Windows features To manage required windows features execute command @@ -1295,3 +1296,11 @@ You can also specify `DbName` and `BackupFilePath` properties to simplify comman ```bash clio resrore-db -e ``` + +## Uninstall Creatio + +Uninstall Creatio from your local machine by executing the following command: + +```bash +clio uninstall-creatio -e +``` diff --git a/build.ps1 b/build.ps1 index f3fa735f..8d05a32c 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,4 +1,4 @@ -$cliogate_Version="2.0.0.29" +$cliogate_Version="2.0.0.30" $clioPath=".\clio\bin\Release\net6.0\clio.dll" dotnet build .\clio\clio.csproj -c Release --no-incremental diff --git a/clio.tests/ApplicationInstallerTests.cs b/clio.tests/ApplicationInstallerTests.cs index 9db1f1ca..82fc0d16 100644 --- a/clio.tests/ApplicationInstallerTests.cs +++ b/clio.tests/ApplicationInstallerTests.cs @@ -20,7 +20,7 @@ internal class ApplicationInstallerTests : BaseClioModuleTests [Test] public void RestartApplicationAfterInstallPackageInNet6() { string packagePath = "T:\\TestClioPackage.gz"; - _fileSystem.AddFile(packagePath, new System.IO.Abstractions.TestingHelpers.MockFileData(new byte[0])); + FileSystem.AddFile(packagePath, new System.IO.Abstractions.TestingHelpers.MockFileData(new byte[0])); EnvironmentSettings environmentSettings = new EnvironmentSettings(); environmentSettings.IsNetCore = true; var applicationClientFactory = Substitute.For(); @@ -29,7 +29,7 @@ public void RestartApplicationAfterInstallPackageInNet6() { var scriptExecutor = Substitute.For(); var serviceUrlBuilder = Substitute.For(); var logger = Substitute.For(); - var clioFileSystem = new FileSystem(_fileSystem); + var clioFileSystem = new FileSystem(FileSystem); ApplicationInstaller applicationInstaller = new ApplicationInstaller(environmentSettings, applicationClientFactory, application, @@ -47,7 +47,7 @@ public void RestartApplicationAfterInstallPackageInNet6() { [Test] public void RestartApplicationAfterInstallFolderInNet6() { string packageFolderPath = "T:\\TestClioPackageFolder"; - _fileSystem.AddDirectory(packageFolderPath); + FileSystem.AddDirectory(packageFolderPath); EnvironmentSettings environmentSettings = new EnvironmentSettings(); environmentSettings.IsNetCore = true; var applicationClientFactory = Substitute.For(); @@ -55,12 +55,12 @@ public void RestartApplicationAfterInstallFolderInNet6() { var packageArchiver = Substitute.For(); packageArchiver.When(p => p.Pack(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any())).Do( callInfo => { - _fileSystem.AddEmptyFile(callInfo[1].ToString()); + FileSystem.AddEmptyFile(callInfo[1].ToString()); }); var scriptExecutor = Substitute.For(); var serviceUrlBuilder = Substitute.For(); var logger = Substitute.For(); - var clioFileSystem = new FileSystem(_fileSystem); + var clioFileSystem = new FileSystem(FileSystem); ApplicationInstaller applicationInstaller = new ApplicationInstaller(environmentSettings, applicationClientFactory, application, @@ -77,7 +77,7 @@ public void RestartApplicationAfterInstallFolderInNet6() { [Test] public void CatchRestartApplicationErrorAfterInstallFolderInNet6() { string packageFolderPath = "T:\\TestClioPackageFolder"; - _fileSystem.AddDirectory(packageFolderPath); + FileSystem.AddDirectory(packageFolderPath); EnvironmentSettings environmentSettings = new EnvironmentSettings(); environmentSettings.IsNetCore = true; var applicationClientFactory = Substitute.For(); @@ -86,12 +86,12 @@ public void CatchRestartApplicationErrorAfterInstallFolderInNet6() { var packageArchiver = Substitute.For(); packageArchiver.When(p => p.Pack(Arg.Any(), Arg.Any(), Arg.Any(), Arg.Any())).Do(callInfo => { - _fileSystem.AddEmptyFile(callInfo[1].ToString()); + FileSystem.AddEmptyFile(callInfo[1].ToString()); }); var scriptExecutor = Substitute.For(); var serviceUrlBuilder = Substitute.For(); var logger = Substitute.For(); - var clioFileSystem = new FileSystem(_fileSystem); + var clioFileSystem = new FileSystem(FileSystem); ApplicationInstaller applicationInstaller = new ApplicationInstaller(environmentSettings, applicationClientFactory, application, diff --git a/clio.tests/Command/AssemblyCommandTests.cs b/clio.tests/Command/AssemblyCommandTests.cs index 641704a3..39a8d1c9 100644 --- a/clio.tests/Command/AssemblyCommandTests.cs +++ b/clio.tests/Command/AssemblyCommandTests.cs @@ -46,7 +46,7 @@ protected override void AdditionalRegistrations(ContainerBuilder containerBuilde [Category("Unit")] public void Execute_ShouldWriteResponse_WhenItIsSuccessful(){ // Arrange - AssemblyCommand command = _container.Resolve(); + AssemblyCommand command = Container.Resolve(); command.Logger = _loggerMock; string executorType = typeof(AssemblyCommand).FullName; diff --git a/clio.tests/Command/BaseClioModuleTests.cs b/clio.tests/Command/BaseClioModuleTests.cs index 07d36628..6732eca3 100644 --- a/clio.tests/Command/BaseClioModuleTests.cs +++ b/clio.tests/Command/BaseClioModuleTests.cs @@ -6,7 +6,7 @@ namespace Clio.Tests.Command; -[TestFixture] +[TestFixture(Category = "UnitTests")] public abstract class BaseClioModuleTests { @@ -14,18 +14,18 @@ public abstract class BaseClioModuleTests [SetUp] public virtual void Setup(){ - _fileSystem = CreateFs(); - BindingsModule bindingModule = new(_fileSystem); - _container = bindingModule.Register(_environmentSettings, true, AdditionalRegistrations); + FileSystem = CreateFs(); + BindingsModule bindingModule = new(FileSystem); + Container = bindingModule.Register(EnvironmentSettings, true, AdditionalRegistrations); } #endregion #region Fields: Protected - protected MockFileSystem _fileSystem; - protected IContainer _container; - protected EnvironmentSettings _environmentSettings = new() { + protected MockFileSystem FileSystem; + protected IContainer Container; + protected EnvironmentSettings EnvironmentSettings = new() { Uri = "http://localhost", Login = "", Password = "" diff --git a/clio.tests/Command/DownloadSettingsToManifestCommand.cs b/clio.tests/Command/DownloadSettingsToManifestCommand.cs index ab4ec947..b2046581 100644 --- a/clio.tests/Command/DownloadSettingsToManifestCommand.cs +++ b/clio.tests/Command/DownloadSettingsToManifestCommand.cs @@ -62,17 +62,17 @@ public void SaveWebServiceToFile() { ILogger loggerMock = Substitute.For(); SaveSettingsToManifestCommand command = new(providerMock, loggerMock, - _container.Resolve(), _container.Resolve(), webServiceManagerMock, _container.Resolve()); + Container.Resolve(), Container.Resolve(), webServiceManagerMock, Container.Resolve()); //Act command.Execute(saveSettingsToManifestOptions); //Assert - _fileSystem.File.Exists(saveSettingsToManifestOptions.ManifestFileName).Should().BeTrue(); + FileSystem.File.Exists(saveSettingsToManifestOptions.ManifestFileName).Should().BeTrue(); string expectedContent = TestFileSystem.ReadExamplesFile("deployments-manifest", "expected-saved-manifest.yaml"); - _fileSystem.File.ReadAllText(saveSettingsToManifestOptions.ManifestFileName).Trim().Should() + FileSystem.File.ReadAllText(saveSettingsToManifestOptions.ManifestFileName).Trim().Should() .Be(expectedContent.Trim()); loggerMock.Received(1).WriteInfo("Done"); @@ -172,7 +172,7 @@ string expectedContent } private IContainer GetContainer() { - return MockDataContainer.GetContainer(_fileSystem); + return MockDataContainer.GetContainer(FileSystem); } [TestCase(true)] @@ -204,16 +204,16 @@ public void SaveEnvironmentSettingsEmptyPackagesToFile(bool accending) { ILogger loggerMock = Substitute.For(); SaveSettingsToManifestCommand command = new(providerMock, loggerMock, - _container.Resolve(), _container.Resolve(), webServiceManagerMock, _container.Resolve()); + Container.Resolve(), Container.Resolve(), webServiceManagerMock, Container.Resolve()); //Act command.Execute(saveSettingsToManifestOptions); //Assert - _fileSystem.File.Exists(saveSettingsToManifestOptions.ManifestFileName).Should().BeTrue(); + FileSystem.File.Exists(saveSettingsToManifestOptions.ManifestFileName).Should().BeTrue(); string expectedContent = TestFileSystem.ReadExamplesFile("deployments-manifest", "expected-saved-full-manifest-WithoutSchemas.yaml"); - _fileSystem.File.ReadAllText(saveSettingsToManifestOptions.ManifestFileName).Trim().Should() + FileSystem.File.ReadAllText(saveSettingsToManifestOptions.ManifestFileName).Trim().Should() .Be(expectedContent.Trim()); loggerMock.Received(1).WriteInfo("Done"); @@ -251,16 +251,16 @@ public void SaveEnvironmentSettingsPackagesWithSchemasToFile(bool packageAccendi ILogger loggerMock = Substitute.For(); SaveSettingsToManifestCommand command = new(providerMock, loggerMock, - _container.Resolve(), _container.Resolve(), webServiceManagerMock, _container.Resolve()); + Container.Resolve(), Container.Resolve(), webServiceManagerMock, Container.Resolve()); //Act command.Execute(saveSettingsToManifestOptions); //Assert - _fileSystem.File.Exists(saveSettingsToManifestOptions.ManifestFileName).Should().BeTrue(); + FileSystem.File.Exists(saveSettingsToManifestOptions.ManifestFileName).Should().BeTrue(); string expectedContent = TestFileSystem.ReadExamplesFile("deployments-manifest", "expected-saved-full-manifest.yaml"); - _fileSystem.File.ReadAllText(saveSettingsToManifestOptions.ManifestFileName).Trim().Should() + FileSystem.File.ReadAllText(saveSettingsToManifestOptions.ManifestFileName).Trim().Should() .Be(expectedContent.Trim()); loggerMock.Received(1).WriteInfo("Done"); diff --git a/clio.tests/Command/ListInstalledAppsCommand.cs b/clio.tests/Command/ListInstalledAppsCommand.cs index e10a9c7f..88e58c2a 100644 --- a/clio.tests/Command/ListInstalledAppsCommand.cs +++ b/clio.tests/Command/ListInstalledAppsCommand.cs @@ -26,7 +26,7 @@ public void Repository_ShouldBeCalled() DataProviderMock dataProviderMock = new (); ILogger loggerMock = Substitute.For(); IApplicationClient applicationClientMock = Substitute.For(); - ListInstalledAppsCommand command = new(dataProviderMock, loggerMock, applicationClientMock, _environmentSettings); + ListInstalledAppsCommand command = new(dataProviderMock, loggerMock, applicationClientMock, EnvironmentSettings); ListInstalledAppsOptions options = new(); var mock = dataProviderMock diff --git a/clio.tests/Command/MockDataCommandTests.cs b/clio.tests/Command/MockDataCommandTests.cs index d41c7577..a90bc216 100644 --- a/clio.tests/Command/MockDataCommandTests.cs +++ b/clio.tests/Command/MockDataCommandTests.cs @@ -27,7 +27,7 @@ protected override MockFileSystem CreateFs(){ [Test] public void CreateDataFiles(){ - FileSystem clioFileSystem = new FileSystem(_fileSystem); + FileSystem clioFileSystem = new FileSystem(FileSystem); // Arrange MockDataCommand command = new MockDataCommand(null, null, clioFileSystem); MockDataCommandOptions options = new MockDataCommandOptions { @@ -35,14 +35,14 @@ public void CreateDataFiles(){ Data = @"T:\MockDataProjects\Tests\MockData" }; command.Execute(options); - _fileSystem.Directory.Exists(options.Models).Should().BeTrue(); - _fileSystem.Directory.Exists(options.Data).Should().BeTrue(); + FileSystem.Directory.Exists(options.Models).Should().BeTrue(); + FileSystem.Directory.Exists(options.Data).Should().BeTrue(); } [Test] public void FindModels(){ // Arrange - FileSystem clioFileSystem = new FileSystem(_fileSystem); + FileSystem clioFileSystem = new FileSystem(FileSystem); MockDataCommand command = new MockDataCommand(null, null, clioFileSystem); MockDataCommandOptions options = new MockDataCommandOptions { Models = @"T:/MockDataProjects", @@ -57,7 +57,7 @@ public void FindModels(){ [Test] public void GetODataData(){ // Arrange - FileSystem clioFileSystem = new FileSystem(_fileSystem); + FileSystem clioFileSystem = new FileSystem(FileSystem); IApplicationClient mockCreatioClient = Substitute.For(); string contactExpectedContent = clioFileSystem.ReadAllText(Path.Combine("T:/MockDataProjects", "Expected", "Contact.json")); @@ -84,7 +84,7 @@ string accountExpectedContent //Assert List models = command.FindModels(options.Models); - string[] dataFiles = _fileSystem.Directory.GetFiles(options.Data, "*.json", SearchOption.AllDirectories); + string[] dataFiles = FileSystem.Directory.GetFiles(options.Data, "*.json", SearchOption.AllDirectories); dataFiles.Count().Should().Be(models.Count); foreach (string dataFile in dataFiles) { string model = Path.GetFileNameWithoutExtension(dataFile); diff --git a/clio.tests/Command/PingAppCommandTests.cs b/clio.tests/Command/PingAppCommandTests.cs index ec88f598..c91979ad 100644 --- a/clio.tests/Command/PingAppCommandTests.cs +++ b/clio.tests/Command/PingAppCommandTests.cs @@ -24,12 +24,12 @@ protected override void AdditionalRegistrations(ContainerBuilder containerBuilde [TestCase(false)] public void PingAppCommandShouldBeUsesAllRetryOptions(bool isNetCore) { //Arrange - _environmentSettings.IsNetCore = isNetCore; - _fileSystem = CreateFs(); - BindingsModule bindingModule = new(_fileSystem); - _container = bindingModule.Register(_environmentSettings, true, AdditionalRegistrations); + EnvironmentSettings.IsNetCore = isNetCore; + FileSystem = CreateFs(); + BindingsModule bindingModule = new(FileSystem); + Container = bindingModule.Register(EnvironmentSettings, true, AdditionalRegistrations); - PingAppCommand command = _container.Resolve(); + PingAppCommand command = Container.Resolve(); PingAppOptions options = new PingAppOptions() { TimeOut = 1, RetryCount = 2, RetryDelay = 3 }; // Act @@ -50,10 +50,10 @@ public void PingAppCommandShouldBeUsesAllRetryOptions(bool isNetCore) { [TestCase(false)] public void PingAppCommandShouldBeUsesAllRetryOptionsOnNet6Environment(bool isNetCore) { //Arrange - _fileSystem = CreateFs(); - BindingsModule bindingModule = new(_fileSystem); - _container = bindingModule.Register(_environmentSettings, true, AdditionalRegistrations); - PingAppCommand command = _container.Resolve(); + FileSystem = CreateFs(); + BindingsModule bindingModule = new(FileSystem); + Container = bindingModule.Register(EnvironmentSettings, true, AdditionalRegistrations); + PingAppCommand command = Container.Resolve(); PingAppOptions options = new PingAppOptions() { TimeOut = 1, RetryCount = 2, diff --git a/clio.tests/Command/Program.Tests.cs b/clio.tests/Command/Program.Tests.cs index a78e323e..6ef36f08 100644 --- a/clio.tests/Command/Program.Tests.cs +++ b/clio.tests/Command/Program.Tests.cs @@ -1,13 +1,10 @@ using System; -using System.Collections.Generic; using System.IO; using System.IO.Abstractions.TestingHelpers; -using System.Linq; using ATF.Repository.Mock; using ATF.Repository.Providers; using Autofac; using Clio.Command; -using DocumentFormat.OpenXml.Drawing.Charts; using FluentAssertions; using NSubstitute; using NUnit.Framework; @@ -26,6 +23,13 @@ public void TearDown() { Program.AppUpdater = null; } + public override void Setup(){ + base.Setup(); + appUpdaterMock.ClearReceivedCalls(); + Program.Container = null; + Program.AppUpdater = null; + } + protected override void AdditionalRegistrations(ContainerBuilder containerBuilder) { var dataProviderMock = new DataProviderMock(); containerBuilder.RegisterInstance(dataProviderMock).As(); @@ -36,11 +40,11 @@ protected override void AdditionalRegistrations(ContainerBuilder containerBuilde public void Resolve_DoesNotThrowException_WhenCommandDoesNotNeedEnvironment() { CreateWorkspaceCommandOptions options = new CreateWorkspaceCommandOptions(); bool logAndSettings = false; - Program.Container = _container; + Program.Container = Container; var filePath = Path.Combine(Environment.CurrentDirectory, SettingsRepository.AppSettingsFile); - _fileSystem.AddFile(filePath, new MockFileData(File + FileSystem.AddFile(filePath, new MockFileData(File .ReadAllText(Path.Combine("Examples", "AppConfigs", "appsettings-with-wrong-active-key.json")))); - SettingsRepository.FileSystem = _fileSystem; + SettingsRepository.FileSystem = FileSystem; Program.Resolve(options, logAndSettings); } @@ -48,12 +52,12 @@ public void Resolve_DoesNotThrowException_WhenCommandDoesNotNeedEnvironment() { [Test] public void TryToRunAutoupdate() { - Program.Container = _container; + Program.Container = Container; Program.AppUpdater = Substitute.For(); var filePath = Path.Combine(Environment.CurrentDirectory, SettingsRepository.AppSettingsFile); - _fileSystem.AddFile(filePath, new MockFileData(File + FileSystem.AddFile(filePath, new MockFileData(File .ReadAllText(Path.Combine("Examples", "AppConfigs", "appsettings-with-wrong-active-key.json")))); - SettingsRepository.FileSystem = _fileSystem; + SettingsRepository.FileSystem = FileSystem; Program.AutoUpdate = true; Program.ExecuteCommands(new string[] { "ver", "--clio" }); Program.AppUpdater.Received(1).CheckUpdate(); @@ -62,12 +66,12 @@ public void TryToRunAutoupdate() { [Test] public void SkipAutoupdateIfUpdateDisable() { - Program.Container = _container; + Program.Container = Container; Program.AppUpdater = Substitute.For(); var filePath = Path.Combine(Environment.CurrentDirectory, SettingsRepository.AppSettingsFile); - _fileSystem.AddFile(filePath, new MockFileData(File + FileSystem.AddFile(filePath, new MockFileData(File .ReadAllText(Path.Combine("Examples", "AppConfigs", "appsettings-with-wrong-active-key.json")))); - SettingsRepository.FileSystem = _fileSystem; + SettingsRepository.FileSystem = FileSystem; Program.AutoUpdate = false; Program.ExecuteCommands(new string[] { "ver", "--clio" }); Program.AppUpdater.Received(0).CheckUpdate(); @@ -76,11 +80,11 @@ public void SkipAutoupdateIfUpdateDisable() { [Test] public void GetAutoUpdaterFromDIByDefault() { - Program.Container = _container; + Program.Container = Container; var filePath = Path.Combine(Environment.CurrentDirectory, SettingsRepository.AppSettingsFile); - _fileSystem.AddFile(filePath, new MockFileData(File + FileSystem.AddFile(filePath, new MockFileData(File .ReadAllText(Path.Combine("Examples", "AppConfigs", "appsettings-with-wrong-active-key.json")))); - SettingsRepository.FileSystem = _fileSystem; + SettingsRepository.FileSystem = FileSystem; Program.AutoUpdate = true; Program.ExecuteCommands(new string[] { "ver", "--clio" }); appUpdaterMock.Received(1).CheckUpdate(); @@ -89,9 +93,9 @@ public void GetAutoUpdaterFromDIByDefault() { [Test] public void GetAutoUpdaterFromDIWithoutInitContainer() { var filePath = Path.Combine(Environment.CurrentDirectory, SettingsRepository.AppSettingsFile); - _fileSystem.AddFile(filePath, new MockFileData(File + FileSystem.AddFile(filePath, new MockFileData(File .ReadAllText(Path.Combine("Examples", "AppConfigs", "appsettings-with-wrong-active-key.json")))); - SettingsRepository.FileSystem = _fileSystem; + SettingsRepository.FileSystem = FileSystem; Program.AutoUpdate = true; Program.ExecuteCommands(new string[] { "ver", "--clio" }).Should().Be(0); Program.AppUpdater.Checked.Should().BeTrue(); diff --git a/clio.tests/Command/ReadmeChecker.cs b/clio.tests/Command/ReadmeChecker.cs index 7cf0bf02..f8def4b1 100644 --- a/clio.tests/Command/ReadmeChecker.cs +++ b/clio.tests/Command/ReadmeChecker.cs @@ -66,7 +66,7 @@ public bool IsInReadme(Type commandOptionType){ private void PopulateListToCheck(Type T){ string commandVerbName = T.GetAttribute().Name; - List aliases = T.GetAttribute().Aliases.ToList(); + List aliases = T.GetAttribute().Aliases?.ToList() ?? []; aliases.Add(commandVerbName); //Add Verb foreach(var alias in aliases) { diff --git a/clio.tests/Command/ShowDiffEnvironmentsCommandTests.cs b/clio.tests/Command/ShowDiffEnvironmentsCommandTests.cs index ae40713f..86e0c4d1 100644 --- a/clio.tests/Command/ShowDiffEnvironmentsCommandTests.cs +++ b/clio.tests/Command/ShowDiffEnvironmentsCommandTests.cs @@ -44,7 +44,7 @@ public void MergeManifest_FindPackages(string sourceManifestFileName, string tar private IContainer GetContainer() { - return MockDataContainer.GetContainer(_fileSystem); + return MockDataContainer.GetContainer(FileSystem); } } diff --git a/clio.tests/Command/UninstallCommand.cs b/clio.tests/Command/UninstallCommand.cs new file mode 100644 index 00000000..35ffbaf3 --- /dev/null +++ b/clio.tests/Command/UninstallCommand.cs @@ -0,0 +1,69 @@ +using System.Threading.Tasks; +using Autofac; +using Clio.Command; +using Clio.Common; +using FluentAssertions; +using NSubstitute; +using NUnit.Framework; + +namespace Clio.Tests.Command; + +[Author("Kirill Krylov", "k.krylov@creatio.com")] +internal class UninstallCreatioCommandTests : BaseCommandTests +{ + + ICreatioUninstaller _creatioUninstaller = Substitute.For(); + protected override void AdditionalRegistrations(ContainerBuilder containerBuilder){ + base.AdditionalRegistrations(containerBuilder); + containerBuilder.RegisterInstance(_creatioUninstaller); + } + + private UninstallCreatioCommand _sut; + + public override void Setup(){ + base.Setup(); + _sut = Container.Resolve(); + } + + [Test] + public void Execute_ShouldEarlyReturn_WhenValidationFails(){ + + //Arrange + var options = new UninstallCreatioCommandOptions(); + + //Act + int exitCode = _sut.Execute(options); + + //Assert + exitCode.Should().Be(1); + } + + [Test] + public void Execute_ShouldReturn_When_EnvironmentNameValidationPasses(){ + + //Arrange + var options = new UninstallCreatioCommandOptions{EnvironmentName = "some"}; + + //Act + int exitCode = _sut.Execute(options); + + //Assert + exitCode.Should().Be(0); + _creatioUninstaller.Received(1).UninstallByEnvironmentName(options.EnvironmentName); + } + + [Test] + public void Execute_ShouldReturn_When_PhysicalPathValidationPasses(){ + + //Arrange + const string directoryPath = @"C:\some_creatio_folder"; + var options = new UninstallCreatioCommandOptions{PhysicalPath = directoryPath}; + FileSystem.AddDirectory(directoryPath); + //Act + int exitCode = _sut.Execute(options); + + //Assert + exitCode.Should().Be(0); + _creatioUninstaller.Received(1).UninstallByPath(options.PhysicalPath); + } +} \ No newline at end of file diff --git a/clio.tests/Common/ClioGateway.Tests.cs b/clio.tests/Common/ClioGateway.Tests.cs index 1a116b64..3af64f8d 100644 --- a/clio.tests/Common/ClioGateway.Tests.cs +++ b/clio.tests/Common/ClioGateway.Tests.cs @@ -60,7 +60,7 @@ public void GetInstalledVersion_Should_LowestVersion_When_BothInstalled(){ _createPackageInfo(new Version(2, 0, 0), Net6ClioPkgName), _createPackageInfo(new Version(2, 2, 2), "not_cliogate") ]); - IClioGateway clioGateway = _container.Resolve(); + IClioGateway clioGateway = Container.Resolve(); // Act PackageVersion actualPackageInfo = clioGateway.GetInstalledVersion(); @@ -77,7 +77,7 @@ public void GetInstalledVersion_Should_ReturnPackageInfo(){ _createPackageInfo(new Version(2, 2, 2), "not_cliogate"), _createPackageInfo(new Version(1, 1, 1), Net6ClioPkgName) ]); - IClioGateway clioGateway = _container.Resolve(); + IClioGateway clioGateway = Container.Resolve(); // Act PackageVersion actualPackageInfo = clioGateway.GetInstalledVersion(); @@ -94,7 +94,7 @@ public void GetInstalledVersion_Should_ReturnPackageInfoNetCore(){ _createPackageInfo(new Version(2, 2, 2), "not_cliogate"), _createPackageInfo(new Version(1, 1, 1), NetFrameworkClioPkgName) ]); - IClioGateway clioGateway = _container.Resolve(); + IClioGateway clioGateway = Container.Resolve(); // Act PackageVersion actualPackageInfo = clioGateway.GetInstalledVersion(); @@ -111,7 +111,7 @@ public void IsCompatibleWith_Should_BeTey_When_CheckedLowerThanExisting(){ _createPackageInfo(new Version(2, 2, 2), "not_cliogate"), _createPackageInfo(new Version(1, 1, 1), NetFrameworkClioPkgName) ]); - IClioGateway clioGateway = _container.Resolve(); + IClioGateway clioGateway = Container.Resolve(); // Act bool actualPackageInfo = clioGateway.IsCompatibleWith("1.0.0"); @@ -126,7 +126,7 @@ public void IsCompatibleWith_ShouldBeFalse_When_ClioGate_NotInstalled(){ _applicationPackageListProviderMock.GetPackages().Returns([ _createPackageInfo(new Version(2, 2, 2), "not_cliogate") ]); - IClioGateway clioGateway = _container.Resolve(); + IClioGateway clioGateway = Container.Resolve(); // Act bool actualPackageInfo = clioGateway.IsCompatibleWith("1.0.0"); @@ -142,7 +142,7 @@ public void IsCompatibleWith_ShouldReturnFalse_When_CheckedHigherThanExisting(){ _createPackageInfo(new Version(2, 2, 2), NetFrameworkClioPkgName), _createPackageInfo(new Version(2, 2, 2), "not_cliogate") ]); - IClioGateway clioGateway = _container.Resolve(); + IClioGateway clioGateway = Container.Resolve(); // Act bool actualPackageInfo = clioGateway.IsCompatibleWith("3.0.0"); @@ -159,7 +159,7 @@ public void IsCompatibleWith_ShouldReturnFalse_When_CheckedHigherThanExisting_Ne _createPackageInfo(new Version(2, 2, 2), Net6ClioPkgName), _createPackageInfo(new Version(2, 2, 2), "not_cliogate") ]); - IClioGateway clioGateway = _container.Resolve(); + IClioGateway clioGateway = Container.Resolve(); // Act bool actualPackageInfo = clioGateway.IsCompatibleWith("3.0.0"); @@ -186,7 +186,7 @@ public void IsCompatibleWith_ShouldReturnFalse_When_CheckedHigherThanExisting_4D _applicationPackageListProviderMock.GetPackages().Returns([ _createPackageInfo(new Version(installedVersion), Net6ClioPkgName), ]); - IClioGateway clioGateway = _container.Resolve(); + IClioGateway clioGateway = Container.Resolve(); // Act bool actualPackageInfo = clioGateway.IsCompatibleWith(requiredVersion); diff --git a/clio.tests/Common/CreatioUninstallerTestFixture.cs b/clio.tests/Common/CreatioUninstallerTestFixture.cs new file mode 100644 index 00000000..c65f48dd --- /dev/null +++ b/clio.tests/Common/CreatioUninstallerTestFixture.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Abstractions.TestingHelpers; +using Autofac; +using Clio.Common; +using Clio.Common.db; +using Clio.Common.K8; +using Clio.Requests; +using Clio.Tests.Command; +using Clio.UserEnvironment; +using MediatR; +using NSubstitute; +using NUnit.Framework; +using ILogger = Clio.Common.ILogger; + +namespace Clio.Tests.Common; + +public class CreatioUninstallerTestFixture : BaseClioModuleTests +{ + + #region Constants: Private + + private const string ConnectionStringsFileName = "ConnectionStrings.config"; + private const string EnvironmentName = "work"; + private const string InstalledCreatioPath = @"C:\inetpub\wwwroot\work"; + + #endregion + + #region Fields: Private + + private readonly ISettingsRepository _settingsRepositoryMock = Substitute.For(); + private ICreatioUninstaller _sut; + private readonly IMediator _mediatorMock = Substitute.For(); + private readonly ILogger _loggerMock = Substitute.For(); + private readonly Ik8Commands _k8CommandsMock = Substitute.For(); + private readonly IMssql _mssqlMock = Substitute.For(); + private readonly IPostgres _postgresMock = Substitute.For(); + + #endregion + + #region Properties: Private + + private Action> MockMediator => + allSitesMock => { + _mediatorMock.When(i => + i.Send(Arg.Any())) + .Do(i => { + AllSitesRequest allSitesRequest = i[0] as AllSitesRequest; + allSitesRequest?.Callback.Invoke(allSitesMock); + } + ); + }; + + #endregion + + #region Methods: Private + + private void MockNoSitesFound(){ + IEnumerable allSitesMock = []; + MockMediator(allSitesMock); + } + + private void MockStartedSite(string url = "", string siteName = EnvironmentName){ + IEnumerable allSitesMock = [ + new IISScannerHandler.UnregisteredSite( + new IISScannerHandler.SiteBinding(siteName, "Started", "", InstalledCreatioPath), + [ + string.IsNullOrWhiteSpace(url) ? + new Uri(EnvironmentSettings.Uri) : new Uri(url) + ], + IISScannerHandler.SiteType.NetFramework) + ]; + MockMediator(allSitesMock); + } + + #endregion + + #region Methods: Protected + + private readonly k8Commands.ConnectionStringParams _cnpMs = new (0, 0, 0, 0, "", ""); + private readonly k8Commands.ConnectionStringParams _cnpPg = new (0, 0, 0, 0, "", ""); + + protected override void AdditionalRegistrations(ContainerBuilder containerBuilder){ + base.AdditionalRegistrations(containerBuilder); + containerBuilder.RegisterInstance(_settingsRepositoryMock).As(); + containerBuilder.RegisterInstance(_mediatorMock).As(); + containerBuilder.RegisterInstance(_loggerMock).As(); + containerBuilder.RegisterInstance(_k8CommandsMock).As(); + containerBuilder.RegisterInstance(_mssqlMock).As(); + containerBuilder.RegisterInstance(_postgresMock).As(); + } + + #endregion + + #region Methods: Public + + public override void Setup(){ + EnvironmentSettings = new EnvironmentSettings { + Uri = "http://kkrylovn.tscrm.com:40090", + Login = "", + Password = "" + }; + base.Setup(); + _settingsRepositoryMock.GetEnvironment(EnvironmentName).Returns(EnvironmentSettings); + + _k8CommandsMock.GetMssqlConnectionString().Returns(_cnpMs); + _k8CommandsMock.GetPostgresConnectionString().Returns(_cnpPg); + + _sut = Container.Resolve(); + FileSystem.AddDirectory(InstalledCreatioPath); + } + + #endregion + + [Test] + public void UninstallByEnvironmentName_Exits_WhenNoSiteFoundByUrl(){ + //Arrange + MockStartedSite("https://google.ca","fake"); + + //Act + _sut.UninstallByEnvironmentName(EnvironmentName); + + //Assert + _loggerMock.Received(1).WriteWarning($"Could not find IIS by environment name: {EnvironmentName}"); + } + + [Test] + public void UninstallByEnvironmentName_Exits_WhenNoSiteFoundInIIS(){ + //Arrange + MockNoSitesFound(); + + //Act + _sut.UninstallByEnvironmentName(EnvironmentName); + + //Assert + _loggerMock.Received(1).WriteWarning("IIS does not have any sites. Nothing to uninstall."); + } + + [Test] + public void UninstallByEnvironmentName_FindsDirPath(){ + //Arrange + MockStartedSite(); + + //Act + _sut.UninstallByEnvironmentName(EnvironmentName); + + //Assert + _loggerMock.Received(1).WriteInfo($"Uninstalling Creatio from directory: {InstalledCreatioPath}"); + } + + [TestCase("ConnectionStrings_PG")] + [TestCase("ConnectionStrings_MS")] + public void UninstallByPath_DropsDb(string fileName){ + //Arrange + MockStartedSite(); + _loggerMock.ClearReceivedCalls(); + string csPath = Path.Join(InstalledCreatioPath, ConnectionStringsFileName); + string csContent = File.ReadAllText($"Examples/CreatioInstalledDir/{fileName}.config"); + FileSystem.AddFile(csPath, new MockFileData(csContent)); + string dbType = fileName == "ConnectionStrings_PG" ? "PostgreSql" : "MsSql"; + const string dbNameInFile = "dbname"; + + //Act + _sut.UninstallByPath(InstalledCreatioPath); + + //Assert + _loggerMock.Received(1).WriteInfo($"Found db: dbname, Server: {dbType}"); + + if (fileName == "ConnectionStrings_PG") { + _k8CommandsMock.Received(1).GetPostgresConnectionString(); + _postgresMock.Received(1).Init("127.0.0.1", _cnpPg.DbPort, _cnpPg.DbUsername, _cnpPg.DbPassword); + _postgresMock.Received(1).DropDb(dbNameInFile); + } + if (fileName == "ConnectionStrings_MS") { + _k8CommandsMock.Received(1).GetMssqlConnectionString(); + _mssqlMock.Received(1).Init("127.0.0.1", _cnpMs.DbPort, _cnpMs.DbUsername, _cnpMs.DbPassword); + _mssqlMock.Received(1).DropDb(dbNameInFile); + } + } + + [TestCase("ConnectionStrings_PG")] + [TestCase("ConnectionStrings_MS")] + public void UninstallByPath_Returns_When_ConnectionString_Invalid(string fileName){ + //Arrange + _loggerMock.ClearReceivedCalls(); + string csPath = Path.Join(InstalledCreatioPath, ConnectionStringsFileName); + string csContent = File.ReadAllText($"Examples/CreatioInstalledDir/{fileName}.config"); + FileSystem.AddFile(csPath, new MockFileData(csContent)); + string dbType = fileName == "ConnectionStrings_PG" ? "PostgreSql" : "MsSql"; + + //Act + _sut.UninstallByPath(InstalledCreatioPath); + + //Assert + _loggerMock.Received(1).WriteInfo($"Found db: dbname, Server: {dbType}"); + } + + [Test] + public void UninstallByPath_Returns_When_ConnectionString_NotExist(){ + //Arrange + _loggerMock.ClearReceivedCalls(); + //MockStartedSite(); + + //Act + _sut.UninstallByPath(InstalledCreatioPath); + + //Assert + _loggerMock.Received(1).WriteWarning($"ConnectionStrings file not found in: {InstalledCreatioPath}"); + } + + [Test] + public void UninstallByPath_Returns_When_DirectoryDoesNotExist(){ + //Arrange + _loggerMock.ClearReceivedCalls(); + const string creatioDirectoryPath = @"C:\random_dir"; + + //Act + _sut.UninstallByPath(creatioDirectoryPath); + + //Assert + _loggerMock.Received(1).WriteWarning($"Directory {creatioDirectoryPath} does not exist."); + } + +} \ No newline at end of file diff --git a/clio.tests/Common/CsProjManager/CsprojFileTests.cs b/clio.tests/Common/CsProjManager/CsprojFileTests.cs index d3b3c9d3..53ee19cb 100644 --- a/clio.tests/Common/CsProjManager/CsprojFileTests.cs +++ b/clio.tests/Common/CsProjManager/CsprojFileTests.cs @@ -33,8 +33,8 @@ public class CsprojFileTests : BaseClioModuleTests public override void Setup(){ base.Setup(); - _csprojFile = _container.Resolve(); - _workspacePathBuilder = _container.Resolve(); + _csprojFile = Container.Resolve(); + _workspacePathBuilder = Container.Resolve(); } #endregion @@ -48,8 +48,8 @@ public async Task Initialize_WithFileInfo_ShouldReturnInitializedCsprojFile(){ string csProjPath = _workspacePathBuilder.BuildPackageProjectPath(packageName); byte[] content = await _getFileContentAsync($"Examples/CsProjFiles/{packageName}.csproj"); - _fileSystem.AddFile(csProjPath, new MockFileData(content)); - IFileInfo fileInfo = new MockFileInfo(_fileSystem, csProjPath); + FileSystem.AddFile(csProjPath, new MockFileData(content)); + IFileInfo fileInfo = new MockFileInfo(FileSystem, csProjPath); // Act IInitializedCsprojFile result = _csprojFile.Initialize(fileInfo); @@ -66,7 +66,7 @@ public async Task Initialize_WithPackageName_ShouldReturnInitializedCsprojFile() string csProjPath = _workspacePathBuilder.BuildPackageProjectPath(packageName); byte[] content = await _getFileContentAsync($"Examples/CsProjFiles/{packageName}.csproj"); - _fileSystem.AddFile(csProjPath, new MockFileData(content)); + FileSystem.AddFile(csProjPath, new MockFileData(content)); // Act IInitializedCsprojFile result = _csprojFile.Initialize(packageName); @@ -100,7 +100,7 @@ public async Task GetPackageReferences_ShouldReturnPackageReferences(string pack // Arrange string csProjPath = _workspacePathBuilder.BuildPackageProjectPath(packageName); byte[] content = await _getFileContentAsync($"Examples/CsProjFiles/{packageName}.csproj"); - _fileSystem.AddFile(csProjPath, new MockFileData(content)); + FileSystem.AddFile(csProjPath, new MockFileData(content)); IInitializedCsprojFile initializedCsProj = _csprojFile.Initialize(packageName); // Act @@ -120,7 +120,7 @@ public async Task GetPackageReferences_ShouldNotInclude_TerrasoftConfiguration(s // Arrange string csProjPath = _workspacePathBuilder.BuildPackageProjectPath(packageName); byte[] content = await _getFileContentAsync($"Examples/CsProjFiles/{packageName}.csproj"); - _fileSystem.AddFile(csProjPath, new MockFileData(content)); + FileSystem.AddFile(csProjPath, new MockFileData(content)); IInitializedCsprojFile initializedCsProj = _csprojFile.Initialize(packageName); // Act diff --git a/clio.tests/CommonProgramTest.cs b/clio.tests/CommonProgramTest.cs index 00b72630..385d1f49 100644 --- a/clio.tests/CommonProgramTest.cs +++ b/clio.tests/CommonProgramTest.cs @@ -113,12 +113,12 @@ public void ApplyEnvManifestOptionsWhenOptionInFileNullAndCommandLineIsEmpty() { [Test] public void ReadEnvironmentOptionsFromManifestFile() { - _fileSystem.MockExamplesFolder("deployments-manifest"); + FileSystem.MockExamplesFolder("deployments-manifest"); var manifestFileName = "full-creatio-config.yaml"; - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; EnvironmentSettings envSettingsFromFile = environmentManager.GetEnvironmentFromManifest(manifestFilePath); - var commonFileSystem = new Clio.Common.FileSystem(_fileSystem); + var commonFileSystem = new Clio.Common.FileSystem(FileSystem); var environmentOptionsFromFile = Program.ReadEnvironmentOptionsFromManifestFile(manifestFilePath, commonFileSystem); envSettingsFromFile.Uri.Should().Be(environmentOptionsFromFile.Uri); envSettingsFromFile.Login.Should().Be(environmentOptionsFromFile.Login); @@ -127,12 +127,12 @@ public void ReadEnvironmentOptionsFromManifestFile() { [Test] public void ReadEnvironmentOptionsFromOnlySettingsManifestFile() { - _fileSystem.MockExamplesFolder("deployments-manifest"); + FileSystem.MockExamplesFolder("deployments-manifest"); var manifestFileName = "only-settings.yaml"; - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; EnvironmentSettings envSettingsFromFile = environmentManager.GetEnvironmentFromManifest(manifestFilePath); - var commonFileSystem = new Clio.Common.FileSystem(_fileSystem); + var commonFileSystem = new Clio.Common.FileSystem(FileSystem); var environmnetOptionsFromFile = Program.ReadEnvironmentOptionsFromManifestFile(manifestFilePath, commonFileSystem); environmnetOptionsFromFile.Should().BeNull(); } diff --git a/clio.tests/EnvironmentManagerTests.cs b/clio.tests/EnvironmentManagerTests.cs index 75aaaf42..a59cdeeb 100644 --- a/clio.tests/EnvironmentManagerTests.cs +++ b/clio.tests/EnvironmentManagerTests.cs @@ -29,7 +29,7 @@ protected override MockFileSystem CreateFs() { [TestCase("easy-creatio-config.yaml", 3)] [TestCase("full-creatio-config.yaml", 2)] public void GetApplicationsFrommanifest_if_applicationExists(string fileName, int appCount) { - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{fileName}"; var applications = environmentManager.GetApplicationsFromManifest(manifestFilePath); appCount.Should().Be(applications.Count); @@ -40,7 +40,7 @@ public void GetApplicationsFrommanifest_if_applicationExists(string fileName, in [TestCase(0, "CrtCustomer360", "1.5.2", "full-creatio-config.yaml")] [TestCase(1, "CrtCaseManagment", "1.0.2", "full-creatio-config.yaml")] public void GetApplicationsFrommanifest_if_applicationExists(int appIndex, string appName, string appVersion, string manifestFileName) { - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; var applications = environmentManager.GetApplicationsFromManifest(manifestFilePath); appName.Should().Be(applications[appIndex].Name); @@ -49,7 +49,7 @@ public void GetApplicationsFrommanifest_if_applicationExists(int appIndex, strin [TestCase("easy-creatio-config.yaml")] public void FindApplicationsFromManifest_In_AppHub(string manifestFileName) { - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; var applicationsFromAppHub = environmentManager.FindApplicationsInAppHub(manifestFilePath); applicationsFromAppHub.Should().HaveCount(2); @@ -69,7 +69,7 @@ public static IEnumerable FindApplicationsFromManifestTestCases { [TestCaseSource(nameof(FindApplicationsFromManifestTestCases))] public void FindApplicationsFromManifest_InAppHub_WithCorrectBranch(string manifestFileName, string[] zipFilePathes) { - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; var applicationsFromAppHub = environmentManager.FindApplicationsInAppHub(manifestFilePath); applicationsFromAppHub.Should().HaveCount(3); @@ -82,7 +82,7 @@ public void FindApplicationsFromManifest_InAppHub_WithCorrectBranch(string manif [TestCase("easy-creatio-config.yaml", "CrtCaseManagment", "//tscrm.com/dfs-ts/MyAppHub/CrtCaseManagment/1.0.2/CrtCaseManagment_1.0.2.zip")] public void FindAppHubPath_In_FromManifest(string manifestFileName, string appName, string path) { string resultPath = path.Replace('/', Path.DirectorySeparatorChar); - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; var app = environmentManager.FindApplicationsInAppHub(manifestFilePath).Where(s => s.Name == appName).FirstOrDefault(); resultPath.Should().Be(app.ZipFileName); @@ -91,8 +91,8 @@ public void FindAppHubPath_In_FromManifest(string manifestFileName, string appNa [TestCase("easy-creatio-config.yaml", "CrtCustomer360", "//tscrm.com/dfs-ts/MyAppHub/Customer360/1.5.2/Customer360_1.5.2.zip")] public void FindAppHubPath_In_FromManifest_ByAliases(string manifestFileName, string appName, string path) { string resultPath = path.Replace('/', Path.DirectorySeparatorChar); - _fileSystem.MockFile(resultPath); - var environmentManager = _container.Resolve(); + FileSystem.MockFile(resultPath); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; var app = environmentManager.FindApplicationsInAppHub(manifestFilePath).Where(s => s.Name == appName).FirstOrDefault(); resultPath.Should().Be(app.ZipFileName); @@ -101,7 +101,7 @@ public void FindAppHubPath_In_FromManifest_ByAliases(string manifestFileName, st [TestCase("easy-creatio-config.yaml", "https://preprod.creatio.com", "https://preprod.creatio.com/0/ServiceModel/AuthService.svc/Login")] [TestCase("full-creatio-config.yaml", "https://production.creatio.com", "https://production.creatio.com/0/ServiceModel/AuthService.svc/Login")] public void GetEnvironmentUrl_FromManifest(string manifestFileName, string url, string authAppUrl) { - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; EnvironmentSettings env = environmentManager.GetEnvironmentFromManifest(manifestFilePath); url.Should().Be(env.Uri); @@ -110,7 +110,7 @@ public void GetEnvironmentUrl_FromManifest(string manifestFileName, string url, [TestCase("feature-creatio-config.yaml", 3)] public void ParsesYamlAndReturnsStructure(string manifestFileName, int count) { - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; IEnumerable features = environmentManager.GetFeaturesFromManifest(manifestFilePath); features.Count().Should().Be(count); @@ -144,7 +144,7 @@ public void ParsesYamlAndReturnsStructure(string manifestFileName, int count) { [TestCase("setting-creatio-config.yaml", 7)] public void GetSettingsFromManifest(string manifestFileName, int count) { //Arrange - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; //Act @@ -200,7 +200,7 @@ public void GetSettingsFromManifest(string manifestFileName, int count) { [TestCase("setting-creatio-config-broken.yaml", 7)] public void GetSettingsFromManifest_Throws_When_YAML_ValueNull(string manifestFileName, int count) { //Arrange - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; //Act + Assert @@ -212,7 +212,7 @@ public void GetSettingsFromManifest_Throws_When_YAML_ValueNull(string manifestFi [TestCase("setting-creatio-config-broken.yaml", 7)] public void GetSettingsFromManifest_Throws_When_YAML_CodeNull(string manifestFileName, int count) { //Arrange - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; //Act + Assert @@ -224,7 +224,7 @@ public void GetSettingsFromManifest_Throws_When_YAML_CodeNull(string manifestFil [TestCase("web-services-creatio.yaml", 2)] public void GetWebServicesFromManifest(string manifestFileName, int count) { //Arrange - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; //Act @@ -247,7 +247,7 @@ public void GetWebServicesFromManifest(string manifestFileName, int count) { [TestCase("sections-without-items-creatio.yaml")] public void GetWebServicesFromManifest_WhenExistsSectionButNotExistsItems(string manifestFileName) { //Arrange - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFilePath = $"C:\\{manifestFileName}"; //Act @@ -262,7 +262,7 @@ public void GetWebServicesFromManifest_WhenExistsSectionButNotExistsItems(string [Test] public void GetEnvironmentPackagesManifest() { - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFileName = $"C:\\creatio-config-package.yaml"; var expectedPackagesCount = 2; List packages = environmentManager.GetPackagesGromManifest(manifestFileName); @@ -282,7 +282,7 @@ public void GetEnvironmentPackagesManifest() { [Test] public void GetEnvironmentEmptyPackagesManifest() { - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var manifestFileName = $"C:\\creatio-config-empty-package.yaml"; var expectedPackagesCount = 0; List packages = environmentManager.GetPackagesGromManifest(manifestFileName); @@ -292,7 +292,7 @@ public void GetEnvironmentEmptyPackagesManifest() { [Test] public void SaveEnvironmentPackagesManifest() { string environmentUrl = "https://preprod.atf.com"; - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var expectedManifestFileName = $"C:\\creatio-config-package.yaml"; var actualManifestFileName = $"C:\\actual-creatio-config-package.yaml"; var expectedPackagesCount = 2; @@ -314,15 +314,15 @@ public void SaveEnvironmentPackagesManifest() { Packages = environmnetPackages }; environmentManager.SaveManifestToFile(actualManifestFileName, environmentManifest); - var expectedFile = _fileSystem.File.ReadAllText(expectedManifestFileName); - var actualFile = _fileSystem.File.ReadAllText(actualManifestFileName); + var expectedFile = FileSystem.File.ReadAllText(expectedManifestFileName); + var actualFile = FileSystem.File.ReadAllText(actualManifestFileName); expectedFile.Should().Be(actualFile); } [Test] public void SaveEnvironmentPackagesInReversOrderManifest() { string environmentUrl = "https://preprod.atf.com"; - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var expectedManifestFileName = $"C:\\creatio-config-package.yaml"; var actualManifestFileName = $"C:\\actual-creatio-config-package.yaml"; var expectedPackagesCount = 2; @@ -344,15 +344,15 @@ public void SaveEnvironmentPackagesInReversOrderManifest() { Packages = environmnetPackages }; environmentManager.SaveManifestToFile(actualManifestFileName, environmentManifest); - var expectedFile = _fileSystem.File.ReadAllText(expectedManifestFileName); - var actualFile = _fileSystem.File.ReadAllText(actualManifestFileName); + var expectedFile = FileSystem.File.ReadAllText(expectedManifestFileName); + var actualFile = FileSystem.File.ReadAllText(actualManifestFileName); expectedFile.Should().Be(actualFile); } [Test] public void ThrowException_If_SaveEnvironmentManifest_in_ExistingFile() { var existingManifestFilePath = $"C:\\creatio-config-package.yaml"; - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var environmentManifest = new EnvironmentManifest(); Action act = () => environmentManager.SaveManifestToFile(existingManifestFilePath, environmentManifest); act.Should().Throw().WithMessage($"Manifest file already exists: {existingManifestFilePath}"); @@ -361,7 +361,7 @@ public void ThrowException_If_SaveEnvironmentManifest_in_ExistingFile() { [Test] public void RewriteExistingManifest_If_SaveEnvironmentManifest_in_ExistingFile() { var existingManifestFilePath = $"C:\\creatio-config-package.yaml"; - var environmentManager = _container.Resolve(); + var environmentManager = Container.Resolve(); var environmentManifest = new EnvironmentManifest(); Action act = () => environmentManager.SaveManifestToFile(existingManifestFilePath, environmentManifest, true); act.Should().NotThrow(); diff --git a/clio.tests/Examples/CreatioInstalledDir/ConnectionStrings_MS.config b/clio.tests/Examples/CreatioInstalledDir/ConnectionStrings_MS.config new file mode 100644 index 00000000..c39affb7 --- /dev/null +++ b/clio.tests/Examples/CreatioInstalledDir/ConnectionStrings_MS.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/clio.tests/Examples/CreatioInstalledDir/ConnectionStrings_PG.config b/clio.tests/Examples/CreatioInstalledDir/ConnectionStrings_PG.config new file mode 100644 index 00000000..876a038d --- /dev/null +++ b/clio.tests/Examples/CreatioInstalledDir/ConnectionStrings_PG.config @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/clio.tests/InstallerCommandTests.cs b/clio.tests/InstallerCommandTests.cs index adc78d74..dddfb337 100644 --- a/clio.tests/InstallerCommandTests.cs +++ b/clio.tests/InstallerCommandTests.cs @@ -28,7 +28,7 @@ internal class InstallerCommandTests :BaseClioModuleTests public override void Setup() { base.Setup(); - _installerCommand = _container.Resolve(); + _installerCommand = Container.Resolve(); } diff --git a/clio.tests/Package/PackageCreator.Test.cs b/clio.tests/Package/PackageCreator.Test.cs index 48ea0a47..3b1026ef 100644 --- a/clio.tests/Package/PackageCreator.Test.cs +++ b/clio.tests/Package/PackageCreator.Test.cs @@ -32,11 +32,11 @@ internal class PackageCreatorTest : BaseClioModuleTests #region Methods: Private private PackageCreator InitCreator(){ - return new PackageCreator(_container.Resolve(), _container.Resolve(), - _container.Resolve(), - _container.Resolve(), _container.Resolve(), - _container.Resolve(), _container.Resolve(), - _container.Resolve(), _container.Resolve()); + return new PackageCreator(Container.Resolve(), Container.Resolve(), + Container.Resolve(), + Container.Resolve(), Container.Resolve(), + Container.Resolve(), Container.Resolve(), + Container.Resolve(), Container.Resolve()); } #endregion @@ -68,9 +68,9 @@ public void Create_AddPackageToWorkspaceWithTwoApplication(){ string appDescriptorPathTwo = Path.Combine(PackagesPath, PackageNameTwo, "Files", "app-descriptor.json"); string appDescriptorPathThree = Path.Combine(PackagesPath, PackageNameThree, "Files", "app-descriptor.json"); - _fileSystem.File.Exists(appDescriptorPathOne).Should().BeTrue(); - _fileSystem.File.Exists(appDescriptorPathTwo).Should().BeTrue(); - _fileSystem.File.Exists(appDescriptorPathThree).Should().BeFalse(); + FileSystem.File.Exists(appDescriptorPathOne).Should().BeTrue(); + FileSystem.File.Exists(appDescriptorPathTwo).Should().BeTrue(); + FileSystem.File.Exists(appDescriptorPathThree).Should().BeFalse(); } [Test] @@ -86,8 +86,8 @@ public void Create_AddTwoApplicationsToWorkplace(){ string appDescriptorPathOne = Path.Combine(PackagesPath, PackageNameOne, "Files", "app-descriptor.json"); string appDescriptorPathTwo = Path.Combine(PackagesPath, PackageNameTwo, "Files", "app-descriptor.json"); - _fileSystem.File.Exists(appDescriptorPathOne).Should().BeTrue(); - _fileSystem.File.Exists(appDescriptorPathTwo).Should().BeTrue(); + FileSystem.File.Exists(appDescriptorPathOne).Should().BeTrue(); + FileSystem.File.Exists(appDescriptorPathTwo).Should().BeTrue(); } [Test] @@ -103,8 +103,8 @@ public void Create_AddTwoPackagesInEmptyWorkspaceByDefault(){ string appDescriptorPathOne = Path.Combine(PackagesPath, PackageNameOne, "Files", "app-descriptor.json"); string appDescriptorPathTwo = Path.Combine(PackagesPath, PackageNameTwo, "Files", "app-descriptor.json"); - _fileSystem.File.Exists(appDescriptorPathOne).Should().BeFalse(); - _fileSystem.File.Exists(appDescriptorPathTwo).Should().BeFalse(); + FileSystem.File.Exists(appDescriptorPathOne).Should().BeFalse(); + FileSystem.File.Exists(appDescriptorPathTwo).Should().BeFalse(); } [Test] @@ -120,8 +120,8 @@ public void Create_AddTwoPackagesWithoutApplication(){ string appDescriptorPathOne = Path.Combine(PackagesPath, PackageNameOne, "Files", "app-descriptor.json"); string appDescriptorPathTwo = Path.Combine(PackagesPath, PackageNameTwo, "Files", "app-descriptor.json"); - _fileSystem.File.Exists(appDescriptorPathOne).Should().BeFalse(); - _fileSystem.File.Exists(appDescriptorPathTwo).Should().BeFalse(); + FileSystem.File.Exists(appDescriptorPathOne).Should().BeFalse(); + FileSystem.File.Exists(appDescriptorPathTwo).Should().BeFalse(); } [Test] @@ -132,14 +132,14 @@ public void Create_RewritePackageIfPackagesWithSameNamesExistsOnDescriptor(){ //Act creator.Create(PackagesPath, PackageNameOne, true); creator.Create(PackagesPath, PackageNameTwo); - _fileSystem.Directory.Delete(Path.Combine(PackagesPath, PackageNameTwo), true); + FileSystem.Directory.Delete(Path.Combine(PackagesPath, PackageNameTwo), true); string appDescriptorPathOne = Path.Combine(PackagesPath, PackageNameOne, "Files", "app-descriptor.json"); - string appDescriptorContent = _fileSystem.File.ReadAllText(appDescriptorPathOne); + string appDescriptorContent = FileSystem.File.ReadAllText(appDescriptorPathOne); AppDescriptorJson appDescriptor = JsonSerializer.Deserialize(appDescriptorContent); appDescriptor.Packages.Add(new Clio.Package.Package {Name = PackageNameTwo, UId = Guid.NewGuid().ToString()}); creator.SaveAppDescriptorToFile(appDescriptor, appDescriptorPathOne); creator.Create(PackagesPath, PackageNameTwo); - appDescriptorContent = _fileSystem.File.ReadAllText(appDescriptorPathOne); + appDescriptorContent = FileSystem.File.ReadAllText(appDescriptorPathOne); appDescriptor = JsonSerializer.Deserialize(appDescriptorContent); appDescriptor.Packages.Count().Should().Be(2); } @@ -152,10 +152,10 @@ public void Create_RewritePackageIfPackageWithSameNameExistsOnDescriptor(){ //Act creator.Create(PackagesPath, PackageNameOne, true); creator.Create(PackagesPath, PackageNameTwo); - _fileSystem.Directory.Delete(Path.Combine(PackagesPath, PackageNameTwo), true); + FileSystem.Directory.Delete(Path.Combine(PackagesPath, PackageNameTwo), true); creator.Create(PackagesPath, PackageNameTwo); string appDescriptorPathOne = Path.Combine(PackagesPath, PackageNameOne, "Files", "app-descriptor.json"); - string appDescriptorContent = _fileSystem.File.ReadAllText(appDescriptorPathOne); + string appDescriptorContent = FileSystem.File.ReadAllText(appDescriptorPathOne); AppDescriptorJson appDescriptor = JsonSerializer.Deserialize(appDescriptorContent); appDescriptor.Packages.Count().Should().Be(2); } @@ -183,10 +183,10 @@ public void Create_TwoPackages(){ string appDescriptorPathOne = Path.Combine(PackagesPath, PackageNameOne, "Files", "app-descriptor.json"); string appDescriptorPathTwo = Path.Combine(PackagesPath, PackageNameTwo, "Files", "app-descriptor.json"); - _fileSystem.File.Exists(appDescriptorPathOne).Should().BeTrue(); - _fileSystem.File.Exists(appDescriptorPathTwo).Should().BeFalse(); + FileSystem.File.Exists(appDescriptorPathOne).Should().BeTrue(); + FileSystem.File.Exists(appDescriptorPathTwo).Should().BeFalse(); - string appDescriptorContent = _fileSystem.File.ReadAllText(appDescriptorPathOne); + string appDescriptorContent = FileSystem.File.ReadAllText(appDescriptorPathOne); AppDescriptorJson appDescriptor = JsonSerializer.Deserialize(appDescriptorContent); appDescriptor.Packages.Count().Should().Be(2); } @@ -202,7 +202,7 @@ public void Create_With(){ //Assert string appDescriptorContent - = _fileSystem.File.ReadAllText(Path.Combine(PackagesPath, PackageNameOne, "Files", "app-descriptor.json")); + = FileSystem.File.ReadAllText(Path.Combine(PackagesPath, PackageNameOne, "Files", "app-descriptor.json")); AppDescriptorJson appDescriptor = JsonSerializer.Deserialize(appDescriptorContent); appDescriptor.Name.Should().Be(PackageNameOne); diff --git a/clio.tests/Validators/UninstallCreatioCommandOptionsValidatorTestFixture.cs b/clio.tests/Validators/UninstallCreatioCommandOptionsValidatorTestFixture.cs new file mode 100644 index 00000000..da7b2abc --- /dev/null +++ b/clio.tests/Validators/UninstallCreatioCommandOptionsValidatorTestFixture.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Autofac; +using Clio.Command; +using Clio.Tests.Command; +using FluentAssertions; +using FluentValidation; +using FluentValidation.Results; +using NUnit.Framework; + +namespace Clio.Tests.Validators; + +[TestFixture(Category = "UnitTests")] +public class UninstallCreatioCommandOptionsValidatorTestFixture : BaseClioModuleTests +{ + + private UninstallCreatioCommandOptionsValidator _sut; + + public override void Setup(){ + base.Setup(); + _sut = Container.Resolve(); + } + + public static IEnumerable EnvironmentNameIsEmptyTestCases { + get + { + yield return new TestCaseData(new Tuple( + new UninstallCreatioCommandOptions(), + new ValidationFailure { + ErrorCode = "ArgumentParse.Error", + ErrorMessage = "Either path to creatio directory or environment name must be provided", + Severity = Severity.Error, + })) + .SetName("Returns Error when PhysicalPath and EnvironmentName IsEmpty") + .SetDescription("Tests that the validator returns an error when both the EnvironmentName and PhysicalPath is empty."); + + yield return new TestCaseData(new Tuple( + new UninstallCreatioCommandOptions { + PhysicalPath = string.Empty + }, + new ValidationFailure { + ErrorCode = "ArgumentParse.Error", + ErrorMessage = "Either path to creatio directory or environment name must be provided", + Severity = Severity.Error, + })) + .SetName("Returns Error when PhysicalPath IsEmpty") + .SetDescription("Tests that the validator returns an error when the PhysicalPath is empty."); + + yield return new TestCaseData(new Tuple( + new UninstallCreatioCommandOptions { + EnvironmentName = string.Empty + }, + new ValidationFailure { + ErrorCode = "ArgumentParse.Error", + ErrorMessage = "Either path to creatio directory or environment name must be provided", + Severity = Severity.Error, + })) + .SetName("Returns Error when EnvironmentName IsEmpty") + .SetDescription("Tests that the validator returns an error when the EnvironmentName is empty."); + + yield return new TestCaseData(new Tuple( + new UninstallCreatioCommandOptions { + EnvironmentName = "env1", + PhysicalPath = @"some value" + }, + new ValidationFailure { + ErrorCode = "ArgumentParse.Error", + ErrorMessage = "Either environment name or path to creatio directory must be provided, not both", + Severity = Severity.Error, + })) + .SetName("Returns Error when EnvironmentName and PhysicalPath AreNotEmpty") + .SetDescription(""" + Tests that the validator returns an error when + the EnvironmentName is not empty and PhysicalPath is empty. + """); + + yield return new TestCaseData(new Tuple( + new UninstallCreatioCommandOptions { + PhysicalPath = @"some value" + }, new ValidationFailure { + ErrorCode = "ArgumentParse.Error", + ErrorMessage = "PhysicalPath must be a valid directory path", + Severity = Severity.Error, + AttemptedValue = "some value" + })) + .SetName("Returns Error when PhysicalPath IsNot a valid file URI") + .SetDescription(""" + Tests that the validator returns an error when + the PhysicalPath is not a valid file URI. + """); + + yield return new TestCaseData(new Tuple( + new UninstallCreatioCommandOptions { + PhysicalPath = @"https://google.ca" + }, + new ValidationFailure { + ErrorCode = "ArgumentParse.Error", + ErrorMessage = "PhysicalPath must be a valid directory path", + Severity = Severity.Error, + AttemptedValue = @"https://google.ca" + })) + .SetName("Returns Error when PhysicalPath IsNot a valid file URI") + .SetDescription(""" + Tests that the validator returns an error when + the PhysicalPath is not a valid file URI. + """); + + yield return new TestCaseData(new Tuple( + new UninstallCreatioCommandOptions { + PhysicalPath = @"C:\inetpub\" + }, new ValidationFailure { + ErrorCode = "ArgumentParse.Error", + ErrorMessage = "PhysicalPath must be a valid directory path to an Existing directory", + Severity = Severity.Error, + AttemptedValue = @"C:\inetpub\" + })) + .SetName("Returns Error when PhysicalPath does not exist in FS") + .SetDescription(""" + Tests that the validator returns an error when + the PhysicalPath points to non existent directory. + """); + } + } + [Test, TestCaseSource(nameof(EnvironmentNameIsEmptyTestCases))] + public void Validate_ShouldReturnError(Tuple testCase) + { + //Act + ValidationResult validationResult = _sut.Validate(testCase.Item1); + + //Assert + validationResult.Errors.First().Should().BeEquivalentTo(testCase.Item2); + } + + [Test] + public void Validate_ShouldNotReturnError_When_EnvNotEmpty() + { + //Arrange + UninstallCreatioCommandOptions opts = new () { + EnvironmentName = "some_env" + }; + + //Act + ValidationResult validationResult = _sut.Validate(opts); + + //Assert + validationResult.Errors.Should().HaveCount(0); + } + [Test] + public void Validate_ShouldNotReturnError_When_PhysicalPathValid() + { + //Arrange + const string dirName = @"C:\inetpup\wwwroot\"; + FileSystem.AddDirectory(dirName); + + UninstallCreatioCommandOptions opts = new () { + PhysicalPath = dirName + }; + + //Act + ValidationResult validationResult = _sut.Validate(opts); + + //Assert + validationResult.Errors.Should().HaveCount(0); + } + + +} \ No newline at end of file diff --git a/clio.tests/WebApplication/DownloaderTests.cs b/clio.tests/WebApplication/DownloaderTests.cs index 248a490f..76b87993 100644 --- a/clio.tests/WebApplication/DownloaderTests.cs +++ b/clio.tests/WebApplication/DownloaderTests.cs @@ -39,7 +39,7 @@ protected override void AdditionalRegistrations(ContainerBuilder containerBuilde [Test] public void DownloadPackageDll_CopiesDownloadedFile(){ // Arrange - IServiceUrlBuilder urlBuilder = _container.Resolve(); + IServiceUrlBuilder urlBuilder = Container.Resolve(); string url = urlBuilder.Build(ServiceUrlBuilder.KnownRoute.DownloadPackageDllFile); const string archiveName = "MyPackage.dll"; const string mockFileContent = "file content"; @@ -53,27 +53,27 @@ public void DownloadPackageDll_CopiesDownloadedFile(){ .When(c => c.DownloadFile(url, archiveFilePath, requestData)) .Do(c => { - _fileSystem.AddFile(Path.Combine(tempDir, archiveName), new MockFileData(mockFileContent)); + FileSystem.AddFile(Path.Combine(tempDir, archiveName), new MockFileData(mockFileContent)); }); _applicationClientFactoryMock.CreateClient(Arg.Any()) .Returns(_applicationClientMock); // Act - (_container.Resolve() as Downloader)?.DownloadPackageDll(downloadInfo, tempDir); + (Container.Resolve() as Downloader)?.DownloadPackageDll(downloadInfo, tempDir); // Assert string expectedLog = $"Run download - OK: {archiveName}"; _loggerMock.Received(1).WriteInfo(expectedLog); _loggerMock.ClearReceivedCalls(); - _fileSystem.File.Exists(destinationPath).Should().BeTrue(); - _fileSystem.File.ReadAllText(destinationPath).Should().Be(mockFileContent); + FileSystem.File.Exists(destinationPath).Should().BeTrue(); + FileSystem.File.ReadAllText(destinationPath).Should().Be(mockFileContent); } [Test] public void DownloadPackageDll_WritesWarning_When_DownloadedFileIsSizeOfZero(){ // Arrange - IServiceUrlBuilder urlBuilder = _container.Resolve(); + IServiceUrlBuilder urlBuilder = Container.Resolve(); string url = urlBuilder.Build(ServiceUrlBuilder.KnownRoute.DownloadPackageDllFile); const string archiveName = "MyPackage.dll"; DownloadInfo downloadInfo = new DownloadInfo(url, archiveName, "", ""); @@ -83,15 +83,15 @@ public void DownloadPackageDll_WritesWarning_When_DownloadedFileIsSizeOfZero(){ .When(c => c.DownloadFile(url, Arg.Any(), Arg.Any())) .Do(c => { - _fileSystem.RemoveFile(destinationPath); - _fileSystem.AddEmptyFile(destinationPath); + FileSystem.RemoveFile(destinationPath); + FileSystem.AddEmptyFile(destinationPath); }); _applicationClientFactoryMock.CreateClient(Arg.Any()) .Returns(_applicationClientMock); // Act - (_container.Resolve() as Downloader)?.DownloadPackageDll(downloadInfo, tempDir); + (Container.Resolve() as Downloader)?.DownloadPackageDll(downloadInfo, tempDir); // Assert @@ -106,7 +106,7 @@ public void DownloadPackageDll_WritesWarning_When_UrlInvalid(){ // Arrange const string url = "my_url"; const string archiveName = "MyPackage.dll"; - IDownloader downloader = _container.Resolve(); + IDownloader downloader = Container.Resolve(); string warningMessage = $@"Invalid URI: The format of the URI could not be determined.{Environment.NewLine}"; _applicationClientMock @@ -132,7 +132,7 @@ public void DownloadPackageDll_WritesWarning_When_UrlInvalid(){ [Test] public void DownloadPackageDll_WritesWarning_WhenDownloadedFileNotFound(){ // Arrange - IServiceUrlBuilder urlBuilder = _container.Resolve(); + IServiceUrlBuilder urlBuilder = Container.Resolve(); string url = urlBuilder.Build(ServiceUrlBuilder.KnownRoute.DownloadPackageDllFile); const string archiveName = "MyPackage.dll"; DownloadInfo downloadInfo = new DownloadInfo(url, archiveName, "", ""); @@ -146,7 +146,7 @@ public void DownloadPackageDll_WritesWarning_WhenDownloadedFileNotFound(){ c.DownloadFile(url, Arg.Any(), Arg.Any())); // Act - (_container.Resolve() as Downloader)?.DownloadPackageDll(downloadInfo, tempDir); + (Container.Resolve() as Downloader)?.DownloadPackageDll(downloadInfo, tempDir); // Assert @@ -160,7 +160,7 @@ public void DownloadPackageDll_WritesWarning_WhenDownloadedFileNotFound(){ public void DownloadPackageDll_WriteWarning_When_packageNameEmpty(){ // Arrange const string url = "my_url"; - IDownloader downloader = _container.Resolve(); + IDownloader downloader = Container.Resolve(); IEnumerable downloadInfos = [ new DownloadInfo(url, "", "", "") ]; diff --git a/clio.tests/Workspace/ApplicationDownloaderTests.cs b/clio.tests/Workspace/ApplicationDownloaderTests.cs index d3326c90..6b56c265 100644 --- a/clio.tests/Workspace/ApplicationDownloaderTests.cs +++ b/clio.tests/Workspace/ApplicationDownloaderTests.cs @@ -44,13 +44,13 @@ protected override void AdditionalRegistrations(ContainerBuilder containerBuilde public void Download_Should_DownloadFiles(){ // Arrange _csprojFileMock.Initialize(Arg.Any()).Returns(_initializedCsprojFile); - IApplicationDownloader downloader = _container.Resolve(); + IApplicationDownloader downloader = Container.Resolve(); List refs = [ new Reference("CalendarBase", null, _mockHintPath("CalendarBase"), false, false) ]; _initializedCsprojFile.GetPackageReferences() .Returns(refs); - IServiceUrlBuilder serviceUrlBuilder = _container.Resolve(); + IServiceUrlBuilder serviceUrlBuilder = Container.Resolve(); _clioGatewayMock.IsCompatibleWith("2.0.0.29").Returns(true); _clioGatewayMock.IsCompatibleWith("2.0.0.0").Returns(true); @@ -69,13 +69,13 @@ public void Download_Should_DownloadFiles(){ public void DownloadPackageDll_Skips_When_ClioGateIsOld(){ // Arrange _csprojFileMock.Initialize(Arg.Any()).Returns(_initializedCsprojFile); - IApplicationDownloader downloader = _container.Resolve(); + IApplicationDownloader downloader = Container.Resolve(); List refs = [ new Reference("CalendarBase", null, _mockHintPath("CalendarBase"), false, false) ]; _initializedCsprojFile.GetPackageReferences() .Returns(refs); - IServiceUrlBuilder serviceUrlBuilder = _container.Resolve(); + IServiceUrlBuilder serviceUrlBuilder = Container.Resolve(); _clioGatewayMock.IsCompatibleWith("2.0.0.29").Returns(false); // Act @@ -94,7 +94,7 @@ public void DownloadPackageDll_Skips_When_ClioGateIsOld(){ public void Download_Skips_WhenPackagesEmpty(){ // Arrange _csprojFileMock.Initialize(Arg.Any()).Returns(_initializedCsprojFile); - IApplicationDownloader downloader = _container.Resolve(); + IApplicationDownloader downloader = Container.Resolve(); _initializedCsprojFile.GetPackageReferences().Returns([]); _clioGatewayMock.IsCompatibleWith("2.0.0.29").Returns(true); diff --git a/clio/BindingsModule.cs b/clio/BindingsModule.cs index c3e229f3..85da4063 100644 --- a/clio/BindingsModule.cs +++ b/clio/BindingsModule.cs @@ -186,8 +186,13 @@ public IContainer Register(EnvironmentSettings settings = null, bool registerNul containerBuilder.RegisterMediatR(configuration); containerBuilder.RegisterGeneric(typeof(ValidationBehaviour<,>)).As(typeof(IPipelineBehavior<,>)); + + //Validators containerBuilder.RegisterType(); containerBuilder.RegisterType(); + containerBuilder.RegisterType(); + + containerBuilder.RegisterType().As(); containerBuilder.RegisterType(); containerBuilder.RegisterType(); containerBuilder.RegisterType(); @@ -200,8 +205,14 @@ public IContainer Register(EnvironmentSettings settings = null, bool registerNul containerBuilder.RegisterType(); containerBuilder.RegisterType(); containerBuilder.RegisterType(); + containerBuilder.RegisterType(); containerBuilder.RegisterType(); + + containerBuilder.RegisterType().As(); + containerBuilder.RegisterType().As(); + + additionalRegistrations?.Invoke(containerBuilder); return containerBuilder.Build(); } diff --git a/clio/Command/ExternalLink.cs b/clio/Command/ExternalLink.cs index a369d0e3..a3f9a9ec 100644 --- a/clio/Command/ExternalLink.cs +++ b/clio/Command/ExternalLink.cs @@ -81,7 +81,7 @@ public override int Execute(ExternalLinkOptions options) { Type runtimeType = GetType().Assembly.GetTypes() .FirstOrDefault(t => t.FullName.ToLower(CultureInfo.InvariantCulture) == $"clio.requests.{_uri.Host}"); - IExtenalLink xRequest = Activator.CreateInstance(runtimeType, true) as IExtenalLink; + IExternalLink xRequest = Activator.CreateInstance(runtimeType, true) as IExternalLink; xRequest.Content = options.Content; Task.Run(async () => diff --git a/clio/Command/InstallerCommand.cs b/clio/Command/InstallerCommand.cs index f73ec964..9f5ae033 100644 --- a/clio/Command/InstallerCommand.cs +++ b/clio/Command/InstallerCommand.cs @@ -18,7 +18,7 @@ namespace Clio.Command; -[Verb("deploy-creatio", Aliases = new string[]{""}, HelpText = "Deploy Creatio from zip file")] +[Verb("deploy-creatio", Aliases = new []{"dc", "ic", "install-creation"}, HelpText = "Deploy Creatio from zip file")] public class PfInstallerOptions : EnvironmentNameOptions { diff --git a/clio/Command/SetFsmConfigCommand.cs b/clio/Command/SetFsmConfigCommand.cs index 6cd7df46..0538803e 100644 --- a/clio/Command/SetFsmConfigCommand.cs +++ b/clio/Command/SetFsmConfigCommand.cs @@ -138,7 +138,7 @@ private string GetWebConfigPathFromEnvName(string envName) { if(string.IsNullOrWhiteSpace(env.Uri)) { throw new Exception($"Could not find path to environment: '{envName}'"); } - IEnumerable sites = IISScannerHandler._findAllCreatioSites(); + IEnumerable sites = IISScannerHandler.FindAllCreatioSites(); foreach (IISScannerHandler.UnregisteredSite site in sites) { foreach (Uri unregisteredSiteUri in site.Uris) { if(unregisteredSiteUri.ToString() == new Uri(env.Uri).ToString()) { diff --git a/clio/Command/UninstallCreatio.cs b/clio/Command/UninstallCreatio.cs new file mode 100644 index 00000000..fda60130 --- /dev/null +++ b/clio/Command/UninstallCreatio.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using Clio.Common; +using CommandLine; +using FluentValidation; +using FluentValidation.Results; + +namespace Clio.Command; + +public class UninstallCreatioCommandOptionsValidator : AbstractValidator +{ + + #region Constructors: Public + + public UninstallCreatioCommandOptionsValidator(IFileSystem fileSystem){ + RuleFor(o => string.IsNullOrWhiteSpace(o.PhysicalPath) && string.IsNullOrWhiteSpace(o.EnvironmentName)) + .Cascade(CascadeMode.Stop) + .Custom((value, context) => { + if (value) { + context.AddFailure(new ValidationFailure { + ErrorCode = "ArgumentParse.Error", + ErrorMessage = "Either path to creatio directory or environment name must be provided", + Severity = Severity.Error + }); + } + }); + + RuleFor(o => !string.IsNullOrWhiteSpace(o.PhysicalPath) && !string.IsNullOrWhiteSpace(o.EnvironmentName)) + .Cascade(CascadeMode.Stop) + .Custom((value, context) => { + if (value) { + context.AddFailure(new ValidationFailure { + ErrorCode = "ArgumentParse.Error", + ErrorMessage + = "Either environment name or path to creatio directory must be provided, not both", + Severity = Severity.Error + }); + } + }); + + RuleFor(o => o.PhysicalPath) + .Cascade(CascadeMode.Stop) + .Custom((value, context) => { + if (string.IsNullOrWhiteSpace(value)) { + return; + } + bool isUri = Uri.TryCreate(value, UriKind.Absolute, out Uri dirPath); + if (!isUri || dirPath.Scheme != Uri.UriSchemeFile) { + context.AddFailure(new ValidationFailure { + ErrorCode = "ArgumentParse.Error", + ErrorMessage = "PhysicalPath must be a valid directory path", + Severity = Severity.Error, + AttemptedValue = value + }); + } + }) + .Custom((value, context) => { + if (string.IsNullOrWhiteSpace(value)) { + return; + } + bool isUri = Uri.TryCreate(value, UriKind.Absolute, out Uri dirPath); + if (!isUri || dirPath.Scheme != Uri.UriSchemeFile) { + return; + } + + bool dirExists = fileSystem.ExistsDirectory(value); + if (!dirExists) { + context.AddFailure(new ValidationFailure { + ErrorCode = "ArgumentParse.Error", + ErrorMessage = "PhysicalPath must be a valid directory path to an Existing directory", + Severity = Severity.Error, + AttemptedValue = value + }); + } + }); + } + + #endregion + +} + +[Verb("uninstall-creatio", Aliases = new[] {"uc"}, HelpText = "Uninstall local instance of creatio")] +public class UninstallCreatioCommandOptions : EnvironmentNameOptions +{ + + #region Properties: Public + + [Option('d', "physicalPath", Required = false, HelpText = "Path to applications")] + public string PhysicalPath { get; set; } + + #endregion + +} + +public class UninstallCreatioCommand : Command +{ + + #region Fields: Private + + private readonly IValidator _validator; + private readonly ILogger _logger; + private readonly ICreatioUninstaller _creatioUninstaller; + + #endregion + + #region Constructors: Public + + public UninstallCreatioCommand(IValidator validator, + ILogger logger, ICreatioUninstaller creatioUninstaller){ + _validator = validator; + _logger = logger; + _creatioUninstaller = creatioUninstaller; + } + + #endregion + + #region Methods: Private + + private int PrintDoneAndExit(UninstallCreatioCommandOptions options){ + if (options.EnvironmentName is not null) { + _logger.WriteInfo($"Done removing Creatio instance by name: {options.EnvironmentName}"); + } + if (options.PhysicalPath is not null) { + _logger.WriteInfo($"Done removing Creatio instance by PhysicalPath: {options.PhysicalPath}"); + } + return 0; + } + + private int PrintErrorsAndExit(List errors){ + _logger.PrintValidationFailureErrors(errors); + return 1; + } + + #endregion + + #region Methods: Public + + public override int Execute(UninstallCreatioCommandOptions options){ + ValidationResult validationResult = _validator.Validate(options); + if (!validationResult.IsValid) { + return PrintErrorsAndExit(validationResult.Errors); + } + + Action act = options switch { + var _ when options.PhysicalPath is not null => () => _creatioUninstaller.UninstallByPath(options.PhysicalPath), + var _ when options.EnvironmentName is not null => () => _creatioUninstaller.UninstallByEnvironmentName(options.EnvironmentName), + var _ => throw new ArgumentOutOfRangeException(nameof(options), "Either PhysicalPath or EnvironmentName must be provided") + }; + act.Invoke(); + + return PrintDoneAndExit(options); + } + + #endregion + +} \ No newline at end of file diff --git a/clio/Common/ConsoleLogger.cs b/clio/Common/ConsoleLogger.cs index e3c9d375..02d6f1a0 100644 --- a/clio/Common/ConsoleLogger.cs +++ b/clio/Common/ConsoleLogger.cs @@ -1,7 +1,11 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; using System.Threading; using ConsoleTables; +using FluentValidation.Results; namespace Clio.Common; @@ -23,6 +27,7 @@ public class ConsoleLogger : ILogger private ConsoleLogger(){ CancellationToken = CancellationTokenSource.Token; + Console.OutputEncoding = System.Text.Encoding.UTF8; } #endregion @@ -102,7 +107,17 @@ private void WriteWarningInternal(string value){ #endregion #region Methods: Public - + + public void PrintValidationFailureErrors(IEnumerable errors) { + errors.Select(e => new { e.ErrorMessage, e.ErrorCode, e.Severity }) + .ToList().ForEach(e => + { + string msg = $"{e.Severity.ToString().ToUpper(CultureInfo.InvariantCulture)} ({e.ErrorCode}) - {e.ErrorMessage}"; + WriteError(msg); + }); + } + + public void PrintTable(ConsoleTable table){ _logQueue.Enqueue(new TableMessage(table)); } diff --git a/clio/Common/CreatioUninstaller.cs b/clio/Common/CreatioUninstaller.cs new file mode 100644 index 00000000..b6b5300d --- /dev/null +++ b/clio/Common/CreatioUninstaller.cs @@ -0,0 +1,282 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Xml; +using Clio.Common.db; +using Clio.Common.K8; +using Clio.Requests; +using Clio.UserEnvironment; +using DocumentFormat.OpenXml.Wordprocessing; +using MediatR; +using OneOf; +using OneOf.Types; + +namespace Clio.Common; + +public interface ICreatioUninstaller +{ + + #region Methods: Public + + /// + /// Uninstalls Creatio by the specified environment name. + /// + /// The name of the environment to uninstall. + /// + /// + /// This method performs the following operations: + /// Sends a request to gather all registered sites in IIS. + /// Retrieves the environment settings based on the provided environment name. + /// Checks if any site matches the environment's URL. + /// + /// If a matching site is found, proceeds to uninstall Creatio from the + /// directory associated with the site. + /// + /// Logs warnings if no sites are found or if the specified environment cannot be matched to any site. + /// + /// + /// + public void UninstallByEnvironmentName(string environmentName); + + /// + /// Uninstalls Creatio by the specified directory path + /// + /// + /// Path to a directory where creatio is installed. + /// Example: C:\inetpub\wwwroot\site_one + /// + /// + /// + /// This method performs the following operations: + /// IIS - Stop Application and AppPool. + /// IIS - Delete Application and AppPool. + /// Find DB from ConnectionString. + /// If in Rancher, drop DB. + /// Delete content in /wwwroot/{EnvironmentName}. + /// Delete content for AppPool User (C:\Users\{AppPoolUser}). + /// + /// + /// + public void UninstallByPath(string creatioDirectoryPath); + + #endregion + +} + +public class CreatioUninstaller : ICreatioUninstaller +{ + + #region Fields: Private + + private readonly IFileSystem _fileSystem; + private readonly ISettingsRepository _settingsRepository; + private readonly IMediator _mediator; + private readonly ILogger _logger; + private readonly Ik8Commands _k8Commands; + private readonly IMssql _mssql; + private readonly IPostgres _postgres; + + private readonly Action _dropPgDbByName + = (dbName, cn, logger, db) => { + db.Init("127.0.0.1", cn.DbPort, cn.DbUsername, cn.DbPassword); + db.DropDb(dbName); + logger.WriteInfo($"Postgres DB: {dbName} dropped"); + }; + + private readonly Action _dropMsDbByName + = (dbName, cn, logger, db) => { + db.Init("127.0.0.1", cn.DbPort, cn.DbUsername, cn.DbPassword); + db.DropDb(dbName); + logger.WriteInfo($"MsSQL DB: {dbName} dropped"); + }; + + #endregion + + #region Constructors: Public + + public CreatioUninstaller(IFileSystem fileSystem, ISettingsRepository settingsRepository, + IMediator mediator, ILogger logger, Ik8Commands k8Commands, IMssql mssql, IPostgres postgres){ + _fileSystem = fileSystem; + _settingsRepository = settingsRepository; + _mediator = mediator; + _logger = logger; + _k8Commands = k8Commands; + _mssql = mssql; + _postgres = postgres; + } + + #endregion + + #region Properties: Private + + private IEnumerable AllSites { get; set; } + + private Action> OnAllSitesRequestCompleted => + sites => { AllSites = sites; }; + + #endregion + + #region Methods: Private + + private static OneOf GetDbInfoFromXmlContent(string csContent){ + XmlDocument doc = new(); + doc.LoadXml(csContent); + + const string mssqlMarker = "Data Source="; + const string psqlMarker = "Server="; + + XmlNodeList nodes = doc.ChildNodes; + foreach (object node in nodes) { + if (node is XmlElement element && element.Name == "connectionStrings") { + foreach (object childNode in element.ChildNodes) { + if (childNode is XmlElement childElement && childElement.Name == "add") { + string name = childElement.GetAttribute("name"); + if (name == "db") { + string connectionString = childElement.GetAttribute("connectionString"); + { + if (connectionString.Contains(psqlMarker)) { + const string pattern = @"Database=([^;]+)"; + Match match = Regex.Match(connectionString, pattern); + if (match.Success) { + string dbName = match.Groups[1].Value; + return new DbInfo(dbName, "PostgreSql"); + } + return new Error(); + } + if (connectionString.Contains(mssqlMarker)) { + const string pattern = @"Catalog=([^;]+)"; + Match match = Regex.Match(connectionString, pattern); + if (match.Success) { + string dbName = match.Groups[1].Value; + return new DbInfo(dbName, "MsSql"); + } + return new Error(); + } + } + } + } + } + } + } + return new Error(); + } + + private OneOf GetDbInfoFromConnectionStringsFile(string creatioDirectoryPath){ + const string connectionStringsFileName = "ConnectionStrings.config"; + string connectionStringsPath = Path.Join(creatioDirectoryPath, connectionStringsFileName); + bool csExists = _fileSystem.ExistsFile(connectionStringsPath); + if (!csExists) { + _logger.WriteWarning($"ConnectionStrings file not found in: {creatioDirectoryPath}"); + return new Error(); + } + string csPath = Path.Join(creatioDirectoryPath, connectionStringsFileName); + string csContent = _fileSystem.ReadAllText(csPath); + if (string.IsNullOrWhiteSpace(csContent)) { + _logger.WriteWarning($"Could not read ConnectionStrings file from : {creatioDirectoryPath}"); + return new Error(); + } + return GetDbInfoFromXmlContent(csContent); + } + + #endregion + + #region Methods: Public + + public void UninstallByEnvironmentName(string environmentName){ + AllSitesRequest request = new() { + Callback = OnAllSitesRequestCompleted + }; + _mediator.Send(request); + + EnvironmentSettings settings = _settingsRepository.GetEnvironment(environmentName); + Uri envUri = new(settings.Uri); + + if (!AllSites.Any()) { + _logger.WriteWarning("IIS does not have any sites. Nothing to uninstall."); + return; + } + string directoryPath = AllSites.FirstOrDefault(all => all.Uris.Contains(envUri))?.siteBinding.path; + string directoryPath2 = AllSites.FirstOrDefault(all => all.siteBinding.name == environmentName)?.siteBinding.path; + if (string.IsNullOrEmpty(directoryPath) && string.IsNullOrEmpty(directoryPath2)) { + _logger.WriteWarning($"Could not find IIS by environment name: {environmentName}"); + return; + } + _logger.WriteInfo($"Uninstalling Creatio from directory: {directoryPath}"); + UninstallByPath(directoryPath); + + _settingsRepository.RemoveEnvironment(environmentName); + _logger.WriteInfo($"Unregisted {environmentName} from clio"); + } + + private void StopIISSite(string creatioDirectoryPath){ + if(AllSites is null) { + AllSitesRequest request = new() { + Callback = OnAllSitesRequestCompleted + }; + _mediator.Send(request); + } + var site = AllSites.FirstOrDefault(all => all.siteBinding.path == creatioDirectoryPath); + if(site is not null) { + var removeRequest = new StopInstanceByNameRequest{SiteName = site.siteBinding.name}; + _mediator.Send(removeRequest); + _logger.WriteInfo($"IIS Stopped: {removeRequest.SiteName}"); + }else { + _logger.WriteWarning($"IIS NOT Stopped Name: {site.siteBinding.name} DIR: {creatioDirectoryPath}"); + } + } + + private void DeleteIISSite(string creatioDirectoryPath){ + if(AllSites is null) { + AllSitesRequest request = new() { + Callback = OnAllSitesRequestCompleted + }; + _mediator.Send(request); + } + var site = AllSites.FirstOrDefault(all => all.siteBinding.path == creatioDirectoryPath); + if(site is not null) { + var removeRequest = new DeleteInstanceByNameRequest{SiteName = site.siteBinding.name}; + _mediator.Send(removeRequest); + _logger.WriteInfo($"IIS Removed: {removeRequest.SiteName}"); + }else { + _logger.WriteWarning($"IIS NOT Removed: {creatioDirectoryPath}"); + } + } + + + public void UninstallByPath(string creatioDirectoryPath){ + if (!_fileSystem.ExistsDirectory(creatioDirectoryPath)) { + _logger.WriteWarning($"Directory {creatioDirectoryPath} does not exist."); + return; + } + StopIISSite(creatioDirectoryPath); + OneOf dbInfo = GetDbInfoFromConnectionStringsFile(creatioDirectoryPath); + DeleteIISSite(creatioDirectoryPath); + + if (dbInfo.Value is Error or null or not DbInfo) { + return; + } + DbInfo info = dbInfo.Value as DbInfo; + _logger.WriteInfo($"Found db: {info!.DbName}, Server: {info!.DbType}"); + + k8Commands.ConnectionStringParams cn = info.DbType switch { + "MsSql" => _k8Commands.GetMssqlConnectionString(), + "PostgreSql" => _k8Commands.GetPostgresConnectionString(), + var _ => throw new Exception("Unknown db type") + }; + + if(info.DbType == "MsSql") { + _dropMsDbByName(info.DbName, cn, _logger, _mssql); + } else { + _dropPgDbByName(info.DbName, cn, _logger, _postgres); + } + _fileSystem.DeleteDirectory(creatioDirectoryPath, true); + _logger.WriteInfo($"Directory: {creatioDirectoryPath} deleted"); + } + + #endregion + + private record DbInfo(string DbName, string DbType); + +} \ No newline at end of file diff --git a/clio/Common/ILogger.cs b/clio/Common/ILogger.cs index 6e0e7806..361d57b9 100644 --- a/clio/Common/ILogger.cs +++ b/clio/Common/ILogger.cs @@ -1,4 +1,6 @@ -using ConsoleTables; +using System.Collections.Generic; +using ConsoleTables; +using FluentValidation.Results; namespace Clio.Common { @@ -66,6 +68,17 @@ public interface ILogger /// /// Table to be written to the log. void PrintTable(ConsoleTable table); + + + /// + /// Prints a collection of validation errors to the log. + /// + /// + /// Each error in the collection is printed as a separate entry in the log. + /// This method is useful for displaying the details of validation failures to the user. + /// + /// The collection of objects to be printed. + void PrintValidationFailureErrors(IEnumerable errors); } } \ No newline at end of file diff --git a/clio/Common/K8/k8Commands.cs b/clio/Common/K8/k8Commands.cs index dc6e37dc..a126c551 100644 --- a/clio/Common/K8/k8Commands.cs +++ b/clio/Common/K8/k8Commands.cs @@ -11,7 +11,22 @@ namespace Clio.Common.K8; -public class k8Commands +public interface Ik8Commands +{ + + void CopyBackupFileToPod(k8Commands.PodType podType, string src, string destFileName); + + string DeleteBackupImage(k8Commands.PodType podType, string fileName); + + string RestorePgDatabase(string backupFileName, string dbName); + + k8Commands.ConnectionStringParams GetPostgresConnectionString(); + + k8Commands.ConnectionStringParams GetMssqlConnectionString(); + +} + +public class k8Commands : Ik8Commands { #region Enum: Public @@ -118,8 +133,6 @@ public ActivePod(PodType podType) { public k8Commands(IKubernetes client) { _client = client; - - } #endregion diff --git a/clio/Common/db/Mssql.cs b/clio/Common/db/Mssql.cs index 56d86a87..433b630b 100644 --- a/clio/Common/db/Mssql.cs +++ b/clio/Common/db/Mssql.cs @@ -7,7 +7,8 @@ namespace Clio.Common.db; public interface IMssql { - + void Init(string host, int port, string username, string password); + bool CreateDb (string dbName, string backupFileName); bool CheckDbExists(string dbName); @@ -21,8 +22,19 @@ public interface IMssql public class Mssql : IMssql { - private readonly SqlConnectionStringBuilder _builder; + private SqlConnectionStringBuilder _builder; + public Mssql(){} + public void Init(string host, int port, string username, string password){ + _builder = new SqlConnectionStringBuilder { + DataSource = $"{host},{port}", + UserID = username, + Password = password, + InitialCatalog = "master", + Encrypt = false + }; + } + public Mssql(string host, int port, string username, string password) { _builder = new SqlConnectionStringBuilder { DataSource = $"{host},{port}", diff --git a/clio/Common/db/Postgres.cs b/clio/Common/db/Postgres.cs index f044e72a..465d72b4 100644 --- a/clio/Common/db/Postgres.cs +++ b/clio/Common/db/Postgres.cs @@ -3,11 +3,28 @@ namespace Clio.Common.db; -public class Postgres + +public interface IPostgres +{ + void Init(string host, int port, string username, string password); + bool CreateDbFromTemplate(string templateName, string dbName); + bool CreateDb(string dbName); + bool SetDatabaseAsTemplate(string dbName); + bool CheckTemplateExists(string templateName); + bool DropDb(string dbName); +} + +public class Postgres : IPostgres { - private readonly string _connectionString; + private string _connectionString; + public Postgres(){ } + + public void Init(string host, int port, string username, string password){ + _connectionString = $"Host={host};Port={port};Username={username};Password={password};Database=postgres"; + } + public Postgres(int port, string username, string password) { _connectionString = $"Host=127.0.0.1;Port={port};Username={username};Password={password};Database=postgres"; } @@ -103,8 +120,36 @@ FROM pg_catalog.pg_database d var result = cmd.ExecuteScalar(); cnn.Close(); - return result is long r && r == 1; + return result is long and 1; + } catch (Exception e) when (e is PostgresException pe){ + Console.WriteLine($"[{pe.Severity}] - {pe.MessageText}"); + return false; + } + catch(Exception e) when (e is NpgsqlException ne) { + Console.WriteLine(ne.Message); + return false; + } + catch(Exception e) { + Console.WriteLine(e.Message); + return false; + } + } + + public bool DropDb(string dbName){ + try { + using NpgsqlDataSource dataSource = NpgsqlDataSource.Create(_connectionString); + using NpgsqlConnection cnn = dataSource.OpenConnection(); + string killSqlConnections = @$" + SELECT pg_terminate_backend(pg_stat_activity.pid) + FROM pg_stat_activity + WHERE pg_stat_activity.datname = '{dbName}' + "; + using NpgsqlCommand killConnectionCmd = dataSource.CreateCommand(killSqlConnections); + killConnectionCmd.ExecuteNonQuery(); + using NpgsqlCommand cmd = dataSource.CreateCommand($"DROP DATABASE IF EXISTS \"{dbName}\";"); + cmd.ExecuteNonQuery(); + cnn.Close(); return true; } catch (Exception e) when (e is PostgresException pe){ Console.WriteLine($"[{pe.Severity}] - {pe.MessageText}"); diff --git a/clio/Program.cs b/clio/Program.cs index 629a0094..dc824acf 100644 --- a/clio/Program.cs +++ b/clio/Program.cs @@ -622,6 +622,7 @@ private static int ConvertPackage(ConvertOptions opts) { typeof(CloneEnvironmentOptions), typeof(ShowDiffEnvironmentsOptions), typeof(MockDataCommandOptions), + typeof(UninstallCreatioCommandOptions), }; public static Func ExecuteCommandWithOption = (instance) => { @@ -723,6 +724,7 @@ private static int ConvertPackage(ConvertOptions opts) { CloneEnvironmentOptions opts => Resolve(opts).Execute(opts), ShowDiffEnvironmentsOptions opts => Resolve(opts).Execute(opts), MockDataCommandOptions opts => Resolve(opts).Execute(opts), + UninstallCreatioCommandOptions opts => Resolve(opts).Execute(opts), _ => 1, }; }; diff --git a/clio/Requests/GetAppSettingsFilePath.cs b/clio/Requests/GetAppSettingsFilePath.cs index ce0646c0..3b966e4c 100644 --- a/clio/Requests/GetAppSettingsFilePath.cs +++ b/clio/Requests/GetAppSettingsFilePath.cs @@ -6,7 +6,7 @@ namespace Clio.Requests { - public class GetAppSettingsFilePath : IExtenalLink + public class GetAppSettingsFilePath : IExternalLink { public string Content { get; set; diff --git a/clio/Requests/IExtenalLink.cs b/clio/Requests/IExternalLink.cs similarity index 68% rename from clio/Requests/IExtenalLink.cs rename to clio/Requests/IExternalLink.cs index 2c818ddb..4036641a 100644 --- a/clio/Requests/IExtenalLink.cs +++ b/clio/Requests/IExternalLink.cs @@ -2,7 +2,7 @@ namespace Clio.Requests { - internal interface IExtenalLink : IRequest + internal interface IExternalLink : IRequest { public string Content { diff --git a/clio/Requests/IISScannerRequest.cs b/clio/Requests/IISScannerRequest.cs index 2decd4dc..324870b1 100644 --- a/clio/Requests/IISScannerRequest.cs +++ b/clio/Requests/IISScannerRequest.cs @@ -16,15 +16,31 @@ using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; +using Terrasoft.Messaging.Common; namespace Clio.Requests { - public class IISScannerRequest : IExtenalLink + public class IISScannerRequest : IExternalLink { public string Content { get; set; } } + + internal class AllSitesRequest: IRequest + { + public Action> Callback; + } + + internal class StopInstanceByNameRequest: IRequest + { + public string SiteName { get; set; } + } + internal class DeleteInstanceByNameRequest: IRequest + { + public string SiteName { get; set; } + } + /// /// Finds path to appSetting.json @@ -35,7 +51,8 @@ public string Content { /// /// /// - internal class IISScannerHandler : BaseExternalLinkHandler, IRequestHandler + internal class IISScannerHandler : BaseExternalLinkHandler, IRequestHandler, + IRequestHandler, IRequestHandler,IRequestHandler { private readonly ISettingsRepository _settingsRepository; private readonly RegAppCommand _regCommand; @@ -49,6 +66,21 @@ public IISScannerHandler(ISettingsRepository settingsRepository, RegAppCommand r _powerShellFactory = powerShellFactory; } + public async Task Handle(AllSitesRequest request, CancellationToken cancellationToken){ + IEnumerable sites = FindAllCreatioSites(); + request.Callback(sites); + } + public async Task Handle(StopInstanceByNameRequest request, CancellationToken cancellationToken){ + var name = request.SiteName; + StopSiteByName(name); + StopAppPoolByName(name); + } + + public async Task Handle(DeleteInstanceByNameRequest request, CancellationToken cancellationToken){ + var name = request.SiteName; + RemoveSiteByName(name); + RemoveAppPoolByName(name); + } public async Task Handle(IISScannerRequest request, CancellationToken cancellationToken) { Uri.TryCreate(request.Content, UriKind.Absolute, out _clioUri); @@ -133,19 +165,16 @@ public async Task Handle(IISScannerRequest request, CancellationToken cancellati /// /// Finds Creatio Sites in IIS that are not registered with clio /// - internal static readonly Func> _findAllCreatioSites = () => + internal static readonly Func> FindAllCreatioSites = () => { return _getBindings() .Where(site => _detectSiteType(site.path) != SiteType.NotCreatioSite) - .Select(site => - { - return new UnregisteredSite( - siteBinding: site, - Uris: _convertBindingToUri(site.binding), - siteType: _detectSiteType(site.path)); - }); + .Select(site => new UnregisteredSite( + siteBinding: site, + Uris: _convertBindingToUri(site.binding), + siteType: _detectSiteType(site.path))); }; - + /// /// Executes appcmd.exe with arguments and captures output /// @@ -194,8 +223,28 @@ public async Task Handle(IISScannerRequest request, CancellationToken cancellati return result; }; + private static readonly Action StopAppPoolByName = (name) => + { + var r = _appcmd($"stop apppool /apppool.name:{name}"); + }; + + private static readonly Action StopSiteByName = (name) => + { + var r = _appcmd($"stop site /site.name:{name}"); + }; + + private static readonly Action RemoveSiteByName = (name) => + { + var r = _appcmd($"delete site /site.name:{name}"); + }; + private static readonly Action RemoveAppPoolByName = (name) => + { + var r = _appcmd($"delete apppool /apppool.name:{name}"); + }; + + private static Func, Exception>, Collection> getValue = (oneOf) => { if (oneOf.Value is Exception ex) @@ -296,6 +345,18 @@ public async Task Handle(IISScannerRequest request, CancellationToken cancellati return SiteType.NotCreatioSite; }; + /// + /// + /// + /// Site name in IIS + /// State of IIS site + /// + /// Started: when IIS site Started + /// Stopped: when IIS site Started + /// + /// + /// + /// Site directory path internal sealed record SiteBinding(string name, string state, string binding, string path) { } diff --git a/clio/Requests/OpenUrl.cs b/clio/Requests/OpenUrl.cs index 170dac31..71487c32 100644 --- a/clio/Requests/OpenUrl.cs +++ b/clio/Requests/OpenUrl.cs @@ -6,7 +6,7 @@ namespace Clio.Requests { - public class OpenUrl : IExtenalLink + public class OpenUrl : IExternalLink { public string Content { get; set; diff --git a/clio/Requests/RegisterOAuthCredentials.cs b/clio/Requests/RegisterOAuthCredentials.cs index 5a043fe2..6c21a558 100644 --- a/clio/Requests/RegisterOAuthCredentials.cs +++ b/clio/Requests/RegisterOAuthCredentials.cs @@ -6,7 +6,7 @@ namespace Clio.Requests { - public class RegisterOAuthCredentials : IExtenalLink + public class RegisterOAuthCredentials : IExternalLink { public string Content { get; set; diff --git a/clio/Requests/Restart .cs b/clio/Requests/Restart .cs index 926677f7..c8f034b4 100644 --- a/clio/Requests/Restart .cs +++ b/clio/Requests/Restart .cs @@ -6,7 +6,7 @@ namespace Clio.Requests { - public class Restart : IExtenalLink + public class Restart : IExternalLink { public string Content { get; set; diff --git a/clio/clio.csproj b/clio/clio.csproj index 9d5557c8..72fc4e44 100644 --- a/clio/clio.csproj +++ b/clio/clio.csproj @@ -26,6 +26,7 @@ clio.ruleset README.md 11 + NU5119;NU1701;CS8632;CS0659;CS0661;CS0108;NU5100;CA1416;CS0169 diff --git a/clio/cliogate/cliogate.gz b/clio/cliogate/cliogate.gz index 76ad8df1..6aa2ce59 100644 Binary files a/clio/cliogate/cliogate.gz and b/clio/cliogate/cliogate.gz differ diff --git a/clio/cliogate/cliogate_netcore.gz b/clio/cliogate/cliogate_netcore.gz index 7a6ceb27..f25573e6 100644 Binary files a/clio/cliogate/cliogate_netcore.gz and b/clio/cliogate/cliogate_netcore.gz differ diff --git a/cliogate/Files/cs/BpmcliApiGateway.cs b/cliogate/Files/cs/BpmcliApiGateway.cs index 37b01aed..af4b4ba9 100644 --- a/cliogate/Files/cs/BpmcliApiGateway.cs +++ b/cliogate/Files/cs/BpmcliApiGateway.cs @@ -177,7 +177,16 @@ private T GetPackageAttributeValue(string key, string packageName){ public string GetSysSettingValueByCode(string code){ CheckCanManageSysSettings(); bool isValue = SysSettings.TryGetValue(_userConnection, code, out object value); - if(!isValue) { + + if(value == null || !isValue) { + var v = SysSettings.GetDefValue(_userConnection, code); + if(v != null) { + value = v; + isValue = true; + } + } + + if(value == null || !isValue) { return string.Empty; } diff --git a/cliogate/descriptor.json b/cliogate/descriptor.json index b1239196..19bc1f62 100644 --- a/cliogate/descriptor.json +++ b/cliogate/descriptor.json @@ -1,11 +1,11 @@ { "Descriptor": { "UId": "e24226f9-c177-458f-af34-9338e2699983", - "PackageVersion": "2.0.0.29", + "PackageVersion": "2.0.0.30", "Name": "cliogate", "Type": 0, "ProjectPath": "", - "ModifiedOnUtc": "/Date(1720127457000)/", + "ModifiedOnUtc": "/Date(1720985138000)/", "Maintainer": "Advanced Technologies Foundation", "DependsOn": [] }