diff --git a/clio.tests/Command/UploadLicensesCommand.Tests.cs b/clio.tests/Command/UploadLicensesCommand.Tests.cs new file mode 100644 index 0000000..ea1fc7f --- /dev/null +++ b/clio.tests/Command/UploadLicensesCommand.Tests.cs @@ -0,0 +1,82 @@ +namespace Clio.Tests.Command +{ + using Clio.Command; + using Clio.Command.PackageCommand; + using Clio.Common; + using NSubstitute; + using NUnit.Framework; + + [TestFixture] + public class UploadLicensesCommandTestCase + { + private UploadLicensesCommandTestable _command; + + [SetUp] + public void SetUp() { + _command = new UploadLicensesCommandTestable(Substitute.For(), + Substitute.For()); + } + + [Test] + public void TestProceedResponse_SuccessResponse_DoesNotThrow() { + var response = "{\"success\": true}"; + var options = new UploadLicensesOptions(); + Assert.DoesNotThrow(() => _command.TestProceedResponse(response, options)); + } + + [Test] + public void TestProceedResponse_ErrorResponseWithErrorInfo_ThrowsException() { + var response = @" + { + ""success"": false, + ""errorInfo"": { + ""message"": ""Invalid license key"", + ""errorCode"": ""INVALID_KEY"" + } + }"; + var options = new UploadLicensesOptions(); + var ex = Assert.Throws(() => + _command.TestProceedResponse(response, options)); + Assert.That(ex.Message, + Is.EqualTo("License not installed. ErrorCode: INVALID_KEY, Message: Invalid license key")); + } + + [Test] + public void TestProceedResponse_ErrorResponseWithoutErrorInfo_ThrowsException() { + var response = "{\"success\": false}"; + var options = new UploadLicensesOptions(); + var ex = Assert.Throws(() => + _command.TestProceedResponse(response, options)); + Assert.That(ex.Message, Is.EqualTo("License not installed: Unknown error details")); + } + + [Test] + public void TestProceedResponse_ResponseWithoutSuccessProperty_DoesNotThrow() { + var response = "{\"errorInfo\": { \"message\": \"Error occurred\" }}"; + var options = new UploadLicensesOptions(); + Assert.DoesNotThrow(() => _command.TestProceedResponse(response, options)); + } + + [Test] + public void TestProceedResponse_AuthenticationFailed_ThrowsLicenseInstallationException() { + var response = "{\"Message\":\"Authentication failed.\",\"StackTrace\":null,\"ExceptionType\":\"System.InvalidOperationException\"}"; + var options = new UploadLicensesOptions(); + var ex = Assert.Throws(() => + _command.TestProceedResponse(response, options)); + Assert.That(ex.Message, Is.EqualTo("License not installed: Authentication failed.")); + } + } + + public class UploadLicensesCommandTestable : UploadLicensesCommand + { + public UploadLicensesCommandTestable(IApplicationClient applicationClient, EnvironmentSettings settings) + : base(applicationClient, settings) { + } + + public void TestProceedResponse(string response, UploadLicensesOptions options) { + ProceedResponse(response, options); + } + } + + +} diff --git a/clio/Command/CommandLineOptions.cs b/clio/Command/CommandLineOptions.cs index b09a92b..996f41a 100644 --- a/clio/Command/CommandLineOptions.cs +++ b/clio/Command/CommandLineOptions.cs @@ -123,6 +123,11 @@ public virtual bool ShowDefaultEnvironment() { [Option("force", Required = false, HelpText = "Force restore")] public bool Force { get; set; } + [Option("callback-process", Required = false, HelpText = "Callback process name")] + public string CallbackProcess { + get; set; + } + internal virtual bool RequiredEnvironment => true; public void CopyFromEnvironmentSettings(EnvironmentOptions source) @@ -156,6 +161,7 @@ public void CopyFromEnvironmentSettings(EnvironmentOptions source) this.DbWorknigFolder = source.DbWorknigFolder; this.DbName = source.DbName; this.Force = source.Force; + this.CallbackProcess = source.CallbackProcess; } internal bool IsEmpty() { diff --git a/clio/Command/PushWorkspaceCommand.cs b/clio/Command/PushWorkspaceCommand.cs index 50bf384..0b88e92 100644 --- a/clio/Command/PushWorkspaceCommand.cs +++ b/clio/Command/PushWorkspaceCommand.cs @@ -1,6 +1,8 @@ namespace Clio.Command { using System; + using System.Text.Json; + using Clio.Command.StartProcess; using Clio.Common; using Clio.Workspaces; using CommandLine; @@ -10,6 +12,8 @@ namespace Clio.Command [Verb("push-workspace", Aliases = new string[] { "pushw" }, HelpText = "Push workspace to selected environment")] public class PushWorkspaceCommandOptions : EnvironmentOptions { + [Option("unlock", Required = false, HelpText = "Unlock workspace package after install workspace to the environment")] + public bool NeedUnlockPackage { get; set; } } #endregion @@ -22,32 +26,78 @@ public class PushWorkspaceCommand : Command #region Fields: Private private readonly IWorkspace _workspace; + private UnlockPackageCommand _unlockPackageCommand; + public IApplicationClientFactory _applicationClientFactory; + private readonly EnvironmentSettings _environmentSettings; + private readonly IServiceUrlBuilder _serviceUrlBuilder; #endregion #region Constructors: Public - public PushWorkspaceCommand(IWorkspace workspace) { + public PushWorkspaceCommand(IWorkspace workspace, UnlockPackageCommand unlockPackageCommand, + IApplicationClientFactory applicationClientFactory, EnvironmentSettings environmentSettings, + IServiceUrlBuilder serviceUrlBuilder) { workspace.CheckArgumentNull(nameof(workspace)); _workspace = workspace; + _unlockPackageCommand = unlockPackageCommand; + _applicationClientFactory = applicationClientFactory; + _environmentSettings = environmentSettings; + _serviceUrlBuilder = serviceUrlBuilder; } + #endregion #region Methods: Public public override int Execute(PushWorkspaceCommandOptions options) { - try - { + try { + Console.WriteLine("Push workspace..."); + CallbackInfo(options.CallbackProcess, "Push workspace..."); _workspace.Install(); + if (options.NeedUnlockPackage) { + var unlockPackageCommandOptions = new UnlockPackageOptions(); + unlockPackageCommandOptions.CopyFromEnvironmentSettings(options); + unlockPackageCommandOptions.Name = string.Join(',', _workspace.WorkspaceSettings.Packages); + Console.WriteLine("Unlock packages..."); + CallbackInfo(options.CallbackProcess, "Unlock packages..."); + _unlockPackageCommand.Execute(unlockPackageCommandOptions); + } Console.WriteLine("Done"); + CallbackInfo(options.CallbackProcess, "Workspace was successfully restored"); return 0; } catch (Exception e) { Console.WriteLine(e.Message); + CallbackInfo(options.CallbackProcess, e.Message); return 1; } } + private void CallbackInfo(string callbackProcess, string message) { + if (!string.IsNullOrEmpty(callbackProcess)) { + var applicationClient = _applicationClientFactory.CreateClient(_environmentSettings); + var runProcessUri = _serviceUrlBuilder.Build(ServiceUrlBuilder.KnownRoute.RunProcess); + ProcessStartArgs runProcessArgs = new() { + SchemaName = callbackProcess, + Values = [ + new ProcessStartArgs.ParameterValues { + Name = "Message", + Value = message + }, + new ProcessStartArgs.ParameterValues { + Name = "Title", + Value = "CLIO" + } +] + }; + Console.WriteLine($"Run callback process {callbackProcess}"); + var processRunResponseJson = applicationClient.ExecutePostRequest(runProcessUri, JsonSerializer.Serialize(runProcessArgs)); + var response = JsonSerializer.Deserialize(processRunResponseJson); + Console.WriteLine($"Run process id {response.ProcessId}"); + } + } + #endregion } diff --git a/clio/Command/StartProcess/ProcessArgs.cs b/clio/Command/StartProcess/ProcessArgs.cs new file mode 100644 index 0000000..78611e3 --- /dev/null +++ b/clio/Command/StartProcess/ProcessArgs.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Newtonsoft.Json; + +namespace Clio.Command.StartProcess; + +[JsonObject] +public record ProcessStartArgs +{ + + [JsonObject] + public class ParameterValues + { + [JsonPropertyName("name")] + [JsonProperty("name")] + public string Name { get; init; } + + [JsonPropertyName("value")] + [JsonProperty("value")] + public string Value { get; init; } + } + + [JsonProperty("schemaName")] + [JsonPropertyName("schemaName")] + public string SchemaName { get; init; } + + [JsonProperty("parameterValues")] + [JsonPropertyName("parameterValues")] + public ParameterValues[] Values { get; init; } + + [JsonProperty("resultParameterNames")] + [JsonPropertyName("resultParameterNames")] + public string[] Result { get; init; } + +} + + +[JsonObject] +public record ProcessStartResponse +{ + [JsonPropertyName("processId")] + [JsonProperty("processId")] + public Guid ProcessId { get; init; } + + [JsonPropertyName("processStatus")] + [JsonProperty("processStatus")] + public int ProcessStatus { get; init; } + + [JsonPropertyName("resultParameterValues")] + [JsonProperty("resultParameterValues")] + public Dictionary ResultParameterValues { get; init; } + + [JsonPropertyName("executionData")] + [JsonProperty("executionData")] + public object ExecutionData { get; init; } + + [JsonPropertyName("success")] + [JsonProperty("success")] + public bool Success { get; init; } + + [JsonPropertyName("errorInfo")] + [JsonProperty("errorInfo")] + public object ErrorInfo { get; init; } +} \ No newline at end of file diff --git a/clio/Command/UploadLicensesCommand.cs b/clio/Command/UploadLicensesCommand.cs index 5830c96..95df9d8 100644 --- a/clio/Command/UploadLicensesCommand.cs +++ b/clio/Command/UploadLicensesCommand.cs @@ -1,6 +1,8 @@ namespace Clio.Command.PackageCommand { + using System; using System.IO; + using System.Text.Json; using Clio.Common; public class UploadLicensesCommand : RemoteCommand @@ -18,5 +20,32 @@ protected override string GetRequestData(UploadLicensesOptions options) { return "{\"licData\":\"" + fileBody + "\"}"; } + protected override void ProceedResponse(string response, UploadLicensesOptions options) { + var json = JsonDocument.Parse(response); + if (json.RootElement.TryGetProperty("success", out var successProperty) && + successProperty.GetBoolean() == false) { + if (json.RootElement.TryGetProperty("errorInfo", out var errorInfo)) { + var errorMessage = errorInfo.TryGetProperty("message", out var messageProperty) + ? messageProperty.GetString() + : "Unknown error message"; + var errorCode = errorInfo.TryGetProperty("errorCode", out var codeProperty) + ? codeProperty.GetString() + : "UNKNOWN_CODE"; + throw new LicenseInstallationException( + $"License not installed. ErrorCode: {errorCode}, Message: {errorMessage}"); + } + throw new LicenseInstallationException("License not installed: Unknown error details"); + } + if (response.ToLower().Contains("authentication failed")) { + throw new LicenseInstallationException("License not installed: Authentication failed."); + } + base.ProceedResponse(response, options); + } } + + public class LicenseInstallationException : Exception + { + public LicenseInstallationException(string message) : base(message) { } + } + } diff --git a/clio/clio.csproj b/clio/clio.csproj index d470ca6..fe1c2bf 100644 --- a/clio/clio.csproj +++ b/clio/clio.csproj @@ -9,7 +9,7 @@ creatio rnd team cli ATF clio creatio en - 8.0.1.7 + 8.0.1.10 $(AssemblyVersion) $(AssemblyVersion) CLI interface for Creatio