diff --git a/clio.tests/Command/DeactivatePackageCommand.Tests.cs b/clio.tests/Command/DeactivatePackageCommand.Tests.cs new file mode 100644 index 00000000..7554dced --- /dev/null +++ b/clio.tests/Command/DeactivatePackageCommand.Tests.cs @@ -0,0 +1,47 @@ +using System; +using Clio.Command.PackageCommand; +using Clio.Common; +using Clio.Package; +using NSubstitute; +using NUnit.Framework; + +namespace Clio.Tests.Command; + +[TestFixture] +public class DeactivatePackageCommandTestCase { + + #region Methods: Public + + [Test, Category("Unit")] + public void Execute_DeactivatesPackage() + { + var packageDeactivator = Substitute.For(); + var applicationClient = Substitute.For(); + var logger = Substitute.For(); + var packageName = "TestPackageName"; + packageDeactivator.Deactivate(packageName); + var command = new DeactivatePackageCommand(packageDeactivator, applicationClient, new EnvironmentSettings(), logger); + Assert.AreEqual(0, command.Execute(new DeactivatePkgOptions { PackageName = packageName })); + logger.Received().WriteLine($"Start deactivation package: \"{packageName}\""); + logger.Received().WriteLine($"Package \"{packageName}\" successfully deactivated."); + } + + [Test, Category("Unit")] + public void Execute_ShowsErrorMessage_WhenPackageWasNotDeactivated() + { + var packageDeactivator = Substitute.For(); + var applicationClient = Substitute.For(); + var logger = Substitute.For(); + var packageName = "TestPackageName"; + var errorMessage = "SomeErrorMessage"; + packageDeactivator.When(deactivator => deactivator.Deactivate(packageName)).Throw(new Exception(errorMessage)); + var command = new DeactivatePackageCommand(packageDeactivator, applicationClient, new EnvironmentSettings(), logger); + Assert.AreEqual(1, command.Execute(new DeactivatePkgOptions { PackageName = packageName})); + logger.Received().WriteLine($"Start deactivation package: \"{packageName}\""); + logger.Received().WriteLine(errorMessage); + logger.DidNotReceive().WriteLine($"Package \"{packageName}\" successfully deactivated."); + } + + #endregion + +} \ No newline at end of file diff --git a/clio.tests/Package/PackageDeactivator.Tests.cs b/clio.tests/Package/PackageDeactivator.Tests.cs new file mode 100644 index 00000000..8aa45283 --- /dev/null +++ b/clio.tests/Package/PackageDeactivator.Tests.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Clio.Common; +using Clio.Common.Responses; +using Clio.Package; +using NSubstitute; +using NUnit.Framework; + +namespace Clio.Tests.Package; + +[TestFixture] +public class PackageDeactivatorTestCase +{ + #region Properties: Private + + private IApplicationPackageListProvider _applicationPackageListProvider; + private IApplicationClient _applicationClient; + private IServiceUrlBuilder _serviceUrlBuilder; + private PackageDeactivator _packageDeactivator; + + #endregion + + #region Methods: Private + + private static PackageInfo CreatePackageInfo(string packageName, Guid? packageUId = null) + { + return new PackageInfo(new PackageDescriptor { Name = packageName, UId = packageUId ?? Guid.NewGuid() }, + string.Empty, + Enumerable.Empty()); + } + + #endregion + + #region Methods: Public + + [SetUp] + public void Init() + { + _applicationPackageListProvider = Substitute.For(); + _applicationClient = Substitute.For(); + _serviceUrlBuilder = Substitute.For(); + _packageDeactivator = new PackageDeactivator(_applicationPackageListProvider, _applicationClient, + _serviceUrlBuilder); + } + + [Test, Category("Unit")] + public void Deactivate_DeactivatesPackageByName() + { + const string packageName = "TestPackageName"; + Guid packageUId = Guid.NewGuid(); + _applicationPackageListProvider.GetPackages("{}").Returns(new List + { + CreatePackageInfo("SomePackage"), + CreatePackageInfo(packageName, packageUId), + CreatePackageInfo("SomePackage1") + }); + const string fullUrl = "TestUrl"; + _serviceUrlBuilder.Build("/ServiceModel/PackageService.svc/DeactivatePackage").Returns(fullUrl); + _applicationClient.ExecutePostRequest(fullUrl, + Arg.Is(data => data.Contains(packageUId.ToString()))) + .Returns(new BaseResponse { Success = true }); + Assert.DoesNotThrow(() => _packageDeactivator.Deactivate(packageName)); + } + + [Test, Category("Unit")] + [TestCase("")] + [TestCase(null)] + public void Deactivate_ThrowsException_WhenPackageByNameIsNotValid(string packageName) + { + Assert.Throws(() => _packageDeactivator.Deactivate(packageName)); + } + + [Test, Category("Unit")] + public void Deactivate_ThrowsException_WhenPackageNotFoundByName() + { + const string packageName = "TestPackageName"; + _applicationPackageListProvider.GetPackages("{}").Returns(new List + { + CreatePackageInfo("SomePackage") + }); + Assert.Throws(() => _packageDeactivator.Deactivate(packageName), + $"Package with name {packageName} not found"); + } + + [Test, Category("Unit")] + public void Deactivate_ThrowsException_WhenPackageWasNotBeenDeactivated() + { + const string packageName = "TestPackageName"; + Guid packageUId = Guid.NewGuid(); + const string errorMessage = "Some error"; + _applicationPackageListProvider.GetPackages("{}").Returns(new List + { + CreatePackageInfo(packageName, packageUId), + }); + _applicationClient.ExecutePostRequest(Arg.Any(), + Arg.Is(data => data.Contains(packageUId.ToString()))) + .Returns(new BaseResponse { Success = false, ErrorInfo = new ErrorInfo {Message = errorMessage}}); + + Assert.Throws(() => _packageDeactivator.Deactivate(packageName), errorMessage); + } + + #endregion + +} \ No newline at end of file diff --git a/clio/BindingsModule.cs b/clio/BindingsModule.cs index c232e144..1f2af9c8 100644 --- a/clio/BindingsModule.cs +++ b/clio/BindingsModule.cs @@ -111,6 +111,7 @@ public IContainer Register(EnvironmentSettings settings = null) { containerBuilder.RegisterType(); containerBuilder.RegisterType(); containerBuilder.RegisterType(); + containerBuilder.RegisterType(); return containerBuilder.Build(); } diff --git a/clio/Command/PackageCommand/DeactivatePackageCommand.cs b/clio/Command/PackageCommand/DeactivatePackageCommand.cs new file mode 100644 index 00000000..1be532b8 --- /dev/null +++ b/clio/Command/PackageCommand/DeactivatePackageCommand.cs @@ -0,0 +1,56 @@ +using System; +using Clio.Common; +using Clio.Package; + +namespace Clio.Command.PackageCommand +{ + using CommandLine; + + [Verb("deactivate-pkg", HelpText = "Deactivate package from a web application. Will be available in 8.1.2")] + internal class DeactivatePkgOptions : EnvironmentOptions { + [Value(0, MetaName = "Name", Required = true, HelpText = "Package name")] + public string PackageName { + get; set; + } + } + + #region Class: GetPackageVersionCommand + + internal class DeactivatePackageCommand : RemoteCommand { + private readonly IPackageDeactivator _packageDeactivator; + private readonly ILogger _logger; + + #region Constructors: Public + + public DeactivatePackageCommand(IPackageDeactivator packageDeactivator, IApplicationClient applicationClient, + EnvironmentSettings environmentSettings, ILogger logger) + : base(applicationClient, environmentSettings) { + _packageDeactivator = packageDeactivator; + _logger = logger; + } + + #endregion + + #region Methods: Public + + public override int Execute(DeactivatePkgOptions options) { + try { + string packageName = options.PackageName; + _logger.WriteLine($"Start deactivation package: \"{packageName}\""); + _packageDeactivator.Deactivate(packageName); + _logger.WriteLine($"Package \"{packageName}\" successfully deactivated."); + return 0; + } + catch (Exception e) { + _logger.WriteLine(e.Message); + return 1; + } + } + + #endregion + + } + + #endregion + +} diff --git a/clio/Common/IApplicationClient.cs b/clio/Common/IApplicationClient.cs index ce57d08f..eea32b10 100644 --- a/clio/Common/IApplicationClient.cs +++ b/clio/Common/IApplicationClient.cs @@ -1,6 +1,7 @@ using Creatio.Client; using System; using System.Threading; +using Clio.Common.Responses; namespace Clio.Common { @@ -10,9 +11,12 @@ public interface IApplicationClient void DownloadFile(string url, string filePath, string requestData); string ExecuteGetRequest(string url, int requestTimeout = Timeout.Infinite); string ExecutePostRequest(string url, string requestData, int requestTimeout = Timeout.Infinite); + void Login(); string UploadFile(string url, string filePath); string UploadAlmFile(string url, string filePath); + + T ExecutePostRequest(string url, string requestData, int requestTimeout = Timeout.Infinite) where T: BaseResponse, new(); } public class CreatioClientAdapter : IApplicationClient @@ -63,5 +67,22 @@ internal T As() { throw new NotImplementedException(); } - } + + /// + /// Performs post request and returns deserialized response. + /// + /// Request url. + /// Request data. + /// Request timeout. Default: infinity period. + /// Return value type. + /// Response. + public T ExecutePostRequest(string url, string requestData, int requestTimeout = Timeout.Infinite) + where T: BaseResponse, new() { + var converter = new JsonConverter(); + string response = _creatioClient.ExecutePostRequest(url, requestData, requestTimeout); + return converter.DeserializeObject(response); + } + + + } } diff --git a/clio/Package/IPackageDeactivator.cs b/clio/Package/IPackageDeactivator.cs new file mode 100644 index 00000000..542e9c58 --- /dev/null +++ b/clio/Package/IPackageDeactivator.cs @@ -0,0 +1,21 @@ +namespace Clio.Package; + +#region Interface: IPackageDeactivator + +/// +/// Provides interface for package deactivation. +/// +public interface IPackageDeactivator { + + #region Methods: Internal + + /// + /// Deactivate package by UId. + /// + /// + void Deactivate(string packageName); + + #endregion +} + +#endregion \ No newline at end of file diff --git a/clio/Package/PackageDeactivator.cs b/clio/Package/PackageDeactivator.cs new file mode 100644 index 00000000..829e8f41 --- /dev/null +++ b/clio/Package/PackageDeactivator.cs @@ -0,0 +1,73 @@ +using System; +using System.Linq; +using Clio.Common; +using Clio.Common.Responses; + +namespace Clio.Package; + +/// +internal class PackageDeactivator: IPackageDeactivator +{ + #region Fields: Private + + private readonly IApplicationPackageListProvider _applicationPackageListProvider; + private readonly IApplicationClient _applicationClient; + private readonly IServiceUrlBuilder _serviceUrlBuilder; + + #endregion + + #region Constructors: Public + + public PackageDeactivator(IApplicationPackageListProvider applicationPackageListProvider, + IApplicationClient applicationClient, IServiceUrlBuilder serviceUrlBuilder) { + _applicationPackageListProvider = applicationPackageListProvider; + _applicationClient = applicationClient; + _serviceUrlBuilder = serviceUrlBuilder; + } + + #endregion + + #region Methods: Private + + private static string CreateRequestData(Guid packageUId) { + return "\"" + packageUId + "\""; + } + private static void ThrowsErrorIfUnsuccessfulResponseReceived(BaseResponse deactivateResponse) { + if (deactivateResponse.Success) { + return; + } + throw new Exception(deactivateResponse.ErrorInfo.Message); + } + + private BaseResponse SendDeactivateRequest(Guid packageUId) { + return _applicationClient.ExecutePostRequest( + _serviceUrlBuilder.Build("/ServiceModel/PackageService.svc/DeactivatePackage"), + CreateRequestData(packageUId)); + } + + private Guid GetPackageUId(string packageName) { + PackageInfo packageInfo = + _applicationPackageListProvider.GetPackages("{}") + .FirstOrDefault(package => package.Descriptor.Name == packageName); + if (packageInfo is null) { + throw new Exception($"Package with name {packageName} not found"); + } + + return packageInfo.Descriptor.UId; + } + + #endregion + + #region Methods: Public + + /// + public void Deactivate(string packageName) { + packageName.CheckArgumentNullOrWhiteSpace(nameof(packageName)); + Guid packageUId = GetPackageUId(packageName); + BaseResponse deactivateResponse = SendDeactivateRequest(packageUId); + ThrowsErrorIfUnsuccessfulResponseReceived(deactivateResponse); + } + + #endregion + +} \ No newline at end of file diff --git a/clio/Program.cs b/clio/Program.cs index 3a3b2f9e..b8d38a7a 100644 --- a/clio/Program.cs +++ b/clio/Program.cs @@ -558,7 +558,8 @@ private static int ConvertPackage(ConvertOptions opts) typeof(CreateInfrastructureOptions), typeof(OpenInfrastructureOptions), typeof(CheckWindowsFeaturesOptions), - typeof(CreateTestProjectOptions) + typeof(CreateTestProjectOptions), + typeof(DeactivatePkgOptions) }; public static Func ExecuteCommandWithOption = (instance) => { return instance switch { @@ -634,6 +635,7 @@ private static int ConvertPackage(ConvertOptions opts) (OpenInfrastructureOptions opts) => Resolve().Execute(opts), (CheckWindowsFeaturesOptions opts) => Resolve().Execute(opts), (CreateTestProjectOptions opts) => Resolve(opts).Execute(opts), + (DeactivatePkgOptions opts) => Resolve(opts).Execute(opts), _ => 1, }; };