From 770f1686b8c3183cded705daf108e43d596e60d1 Mon Sep 17 00:00:00 2001 From: "jan.hajek@zerops.io" Date: Fri, 29 Mar 2024 17:02:15 +0100 Subject: [PATCH] fix: errors and tests refactor --- src/cmd/login.go | 4 +- src/cmd/project.go | 4 +- src/cmd/projectDelete.go | 6 +- src/cmd/projectImport.go | 6 +- src/cmd/projectList.go | 4 +- src/cmd/projectServiceImport.go | 4 +- src/cmd/root.go | 13 +- src/cmd/scope.go | 4 +- src/cmd/scope/scopeProject.go | 21 +- src/cmd/scope/scopeService.go | 10 +- src/cmd/scopeProject.go | 6 +- src/cmd/scopeReset.go | 4 +- src/cmd/service.go | 4 +- src/cmd/serviceDelete.go | 6 +- src/cmd/serviceDeploy.go | 22 +- src/cmd/serviceEnableSubdomain.go | 6 +- src/cmd/serviceList.go | 4 +- src/cmd/serviceLog.go | 6 +- src/cmd/servicePush.go | 20 +- src/cmd/servicePushDeployShared.go | 67 ++++-- src/cmd/serviceStart.go | 6 +- src/cmd/serviceStop.go | 6 +- src/cmd/statusShowDebugLogs.go | 4 +- src/cmd/version.go | 4 +- src/cmd/vpn.go | 4 +- src/cmd/vpnDown.go | 4 +- src/cmd/vpnUp.go | 77 ++----- src/entity/repository/service.go | 15 +- src/errorsx/checker.go | 94 +++------ src/errorsx/checker_test.go | 67 ++++++ src/errorsx/checks.go | 149 +++++++++++++ src/errorsx/checks_test.go | 94 +++++++++ src/i18n/en.go | 327 +++++++++++++++++------------ src/i18n/i18n.go | 305 +++++++++++++++------------ 34 files changed, 866 insertions(+), 511 deletions(-) create mode 100644 src/errorsx/checker_test.go create mode 100644 src/errorsx/checks.go create mode 100644 src/errorsx/checks_test.go diff --git a/src/cmd/login.go b/src/cmd/login.go index d5f5094c..adc9a432 100644 --- a/src/cmd/login.go +++ b/src/cmd/login.go @@ -19,10 +19,10 @@ import ( func loginCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("login"). - Short(i18n.T(i18n.CmdLogin)). + Short(i18n.T(i18n.CmdDescLogin)). StringFlag("regionUrl", constants.DefaultRegionUrl, i18n.T(i18n.RegionUrlFlag), cmdBuilder.HiddenFlag()). StringFlag("region", "", i18n.T(i18n.RegionFlag), cmdBuilder.HiddenFlag()). - HelpFlag(i18n.T(i18n.LoginHelp)). + HelpFlag(i18n.T(i18n.CmdHelpLogin)). Arg("token"). GuestRunFunc(func(ctx context.Context, cmdData *cmdBuilder.GuestCmdData) error { uxBlocks := cmdData.UxBlocks diff --git a/src/cmd/project.go b/src/cmd/project.go index 6f55db1d..c494b836 100644 --- a/src/cmd/project.go +++ b/src/cmd/project.go @@ -8,8 +8,8 @@ import ( func projectCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("project"). - Short(i18n.T(i18n.CmdProject)). - HelpFlag(i18n.T(i18n.ProjectHelp)). + Short(i18n.T(i18n.CmdDescProject)). + HelpFlag(i18n.T(i18n.CmdHelpProject)). AddChildrenCmd(projectListCmd()). AddChildrenCmd(projectDeleteCmd()). AddChildrenCmd(projectServiceImportCmd()). diff --git a/src/cmd/projectDelete.go b/src/cmd/projectDelete.go index 2abf53cc..9c2fdc42 100644 --- a/src/cmd/projectDelete.go +++ b/src/cmd/projectDelete.go @@ -15,11 +15,11 @@ import ( func projectDeleteCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("delete"). - Short(i18n.T(i18n.CmdProjectDelete)). + Short(i18n.T(i18n.CmdDescProjectDelete)). ScopeLevel(scope.Project). Arg(scope.ProjectArgName, cmdBuilder.OptionalArg()). BoolFlag("confirm", false, i18n.T(i18n.ConfirmFlag)). - HelpFlag(i18n.T(i18n.ProjectDeleteHelp)). + HelpFlag(i18n.T(i18n.CmdHelpProjectDelete)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { if !cmdData.Params.GetBool("confirm") { confirmed, err := uxHelpers.YesNoPrompt( @@ -58,7 +58,7 @@ func projectDeleteCmd() *cmdBuilder.Cmd { []uxHelpers.Process{{ F: uxHelpers.CheckZeropsProcess(processId, cmdData.RestApiClient), RunningMessage: i18n.T(i18n.ProjectDeleting), - ErrorMessageMessage: i18n.T(i18n.ProjectDeleting), + ErrorMessageMessage: i18n.T(i18n.ProjectDeleteFailed), SuccessMessage: i18n.T(i18n.ProjectDeleted), }}, ) diff --git a/src/cmd/projectImport.go b/src/cmd/projectImport.go index bbdb382a..7fdcd1bd 100644 --- a/src/cmd/projectImport.go +++ b/src/cmd/projectImport.go @@ -19,12 +19,12 @@ const projectImportArgName = "importYamlPath" func projectImportCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("project-import"). - Short(i18n.T(i18n.CmdProjectImport)). - Long(i18n.T(i18n.CmdProjectImportLong)). + Short(i18n.T(i18n.CmdDescProjectImport)). + Long(i18n.T(i18n.CmdDescProjectImportLong)). Arg(projectImportArgName). StringFlag("orgId", "", i18n.T(i18n.OrgIdFlag)). StringFlag("workingDie", "./", i18n.T(i18n.BuildWorkingDir)). - HelpFlag(i18n.T(i18n.ProjectImportHelp)). + HelpFlag(i18n.T(i18n.CmdHelpProjectImport)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { uxBlocks := cmdData.UxBlocks diff --git a/src/cmd/projectList.go b/src/cmd/projectList.go index e98c7688..be825e78 100644 --- a/src/cmd/projectList.go +++ b/src/cmd/projectList.go @@ -11,8 +11,8 @@ import ( func projectListCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("list"). - Short(i18n.T(i18n.CmdProjectList)). - HelpFlag(i18n.T(i18n.ProjectListHelp)). + Short(i18n.T(i18n.CmdDescProjectList)). + HelpFlag(i18n.T(i18n.CmdHelpProjectList)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { err := uxHelpers.PrintProjectList(ctx, cmdData.UxBlocks, cmdData.RestApiClient) if err != nil { diff --git a/src/cmd/projectServiceImport.go b/src/cmd/projectServiceImport.go index 961d760e..281bcb7a 100644 --- a/src/cmd/projectServiceImport.go +++ b/src/cmd/projectServiceImport.go @@ -18,10 +18,10 @@ const serviceImportArgName = "importYamlPath" func projectServiceImportCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("service-import"). - Short(i18n.T(i18n.CmdServiceImport)). + Short(i18n.T(i18n.CmdDescProjectServiceImport)). ScopeLevel(scope.Project). Arg(serviceImportArgName). - HelpFlag(i18n.T(i18n.ProjectServiceImportHelp)). + HelpFlag(i18n.T(i18n.CmdHelpProjectServiceImport)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { uxBlocks := cmdData.UxBlocks diff --git a/src/cmd/root.go b/src/cmd/root.go index 946ffdd9..1c19c60b 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -3,7 +3,6 @@ package cmd import ( "context" "fmt" - "time" "github.com/zeropsio/zcli/src/cmd/scope" "github.com/zeropsio/zcli/src/cmdBuilder" @@ -11,7 +10,6 @@ import ( "github.com/zeropsio/zcli/src/entity/repository" "github.com/zeropsio/zcli/src/errorsx" "github.com/zeropsio/zcli/src/i18n" - "github.com/zeropsio/zcli/src/nettools" "github.com/zeropsio/zcli/src/uxBlock" "github.com/zeropsio/zcli/src/uxBlock/styles" "github.com/zeropsio/zerops-go/errorCode" @@ -71,7 +69,7 @@ func rootCmd() *cmdBuilder.Cmd { projectId, _ := cmdData.CliStorage.Data().ScopeProjectId.Get() project, err := repository.GetProjectById(ctx, cmdData.RestApiClient, projectId) if err != nil { - if errorsx.Check(err, errorsx.CheckErrorCode(errorCode.ProjectNotFound)) { + if errorsx.Is(err, errorsx.ErrorCode(errorCode.ProjectNotFound)) { err := scope.ProjectScopeReset(cmdData) if err != nil { return err @@ -84,13 +82,10 @@ func rootCmd() *cmdBuilder.Cmd { } } - pingCtx, cancel := context.WithTimeout(ctx, time.Second) - defer cancel() - - if err := nettools.Ping(pingCtx, vpnCheckAddress); err != nil { - body.AddStringsRow(i18n.T(i18n.StatusInfoVpnStatus), i18n.T(i18n.VpnCheckingConnectionIsNotActive)) - } else { + if isVpnUp(ctx) { body.AddStringsRow(i18n.T(i18n.StatusInfoVpnStatus), i18n.T(i18n.VpnCheckingConnectionIsActive)) + } else { + body.AddStringsRow(i18n.T(i18n.StatusInfoVpnStatus), i18n.T(i18n.VpnCheckingConnectionIsNotActive)) } cmdData.UxBlocks.Table(body) diff --git a/src/cmd/scope.go b/src/cmd/scope.go index e2a9b8d2..b3c07e30 100644 --- a/src/cmd/scope.go +++ b/src/cmd/scope.go @@ -8,8 +8,8 @@ import ( func scopeCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("scope"). - Short(i18n.T(i18n.CmdScope)). - HelpFlag(i18n.T(i18n.ScopeHelp)). + Short(i18n.T(i18n.CmdDescScope)). + HelpFlag(i18n.T(i18n.CmdHelpScope)). AddChildrenCmd(scopeProjectCmd()). AddChildrenCmd(scopeResetCmd()) } diff --git a/src/cmd/scope/scopeProject.go b/src/cmd/scope/scopeProject.go index 372312a2..883eee09 100644 --- a/src/cmd/scope/scopeProject.go +++ b/src/cmd/scope/scopeProject.go @@ -11,6 +11,7 @@ import ( "github.com/zeropsio/zcli/src/i18n" "github.com/zeropsio/zcli/src/uxBlock/styles" "github.com/zeropsio/zcli/src/uxHelpers" + "github.com/zeropsio/zerops-go/apiError" "github.com/zeropsio/zerops-go/errorCode" "github.com/zeropsio/zerops-go/types/uuid" ) @@ -39,7 +40,7 @@ func (p *project) LoadSelectedScope(ctx context.Context, cmd *cmdBuilder.Cmd, cm project, err = repository.GetProjectById(ctx, cmdData.RestApiClient, projectId) if err != nil { - if errorsx.Check(err, errorsx.CheckErrorCode(errorCode.ProjectNotFound)) { + if errorsx.Is(err, errorsx.ErrorCode(errorCode.ProjectNotFound)) { err := ProjectScopeReset(cmdData) if err != nil { return err @@ -57,7 +58,14 @@ func (p *project) LoadSelectedScope(ctx context.Context, cmd *cmdBuilder.Cmd, cm if err != nil { return errorsx.Convert( err, - errorsx.ConvertInvalidUserInput("id", i18n.T(i18n.ErrorInvalidProjectId)), + errorsx.InvalidUserInput( + "id", + errorsx.InvalidUserInputErrorMessage( + func(_ apiError.Error, metaItemTyped map[string]interface{}) string { + return i18n.T(i18n.ErrorInvalidProjectId, projectId, metaItemTyped["message"]) + }, + ), + ), ) } @@ -70,7 +78,14 @@ func (p *project) LoadSelectedScope(ctx context.Context, cmd *cmdBuilder.Cmd, cm if err != nil { return errorsx.Convert( err, - errorsx.ConvertInvalidUserInput("id", i18n.T(i18n.ErrorInvalidProjectId)), + errorsx.InvalidUserInput( + "id", + errorsx.InvalidUserInputErrorMessage( + func(_ apiError.Error, metaItemTyped map[string]interface{}) string { + return i18n.T(i18n.ErrorInvalidProjectId, projectId, metaItemTyped["message"]) + }, + ), + ), ) } diff --git a/src/cmd/scope/scopeService.go b/src/cmd/scope/scopeService.go index 48377065..6c5492a2 100644 --- a/src/cmd/scope/scopeService.go +++ b/src/cmd/scope/scopeService.go @@ -10,6 +10,7 @@ import ( "github.com/zeropsio/zcli/src/i18n" "github.com/zeropsio/zcli/src/uxBlock/styles" "github.com/zeropsio/zcli/src/uxHelpers" + "github.com/zeropsio/zerops-go/apiError" "github.com/zeropsio/zerops-go/types/uuid" ) @@ -50,7 +51,14 @@ func (s *service) LoadSelectedScope(ctx context.Context, _ *cmdBuilder.Cmd, cmdD if err != nil { return errorsx.Convert( err, - errorsx.ConvertInvalidUserInput("id", i18n.T(i18n.ErrorInvalidServiceId)), + errorsx.InvalidUserInput( + "id", + errorsx.InvalidUserInputErrorMessage( + func(_ apiError.Error, metaItemTyped map[string]interface{}) string { + return i18n.T(i18n.ErrorInvalidServiceId, serviceId, metaItemTyped["message"]) + }, + ), + ), ) } } diff --git a/src/cmd/scopeProject.go b/src/cmd/scopeProject.go index e64437e8..53004386 100644 --- a/src/cmd/scopeProject.go +++ b/src/cmd/scopeProject.go @@ -19,15 +19,15 @@ import ( func scopeProjectCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("project"). - Short(i18n.T(i18n.CmdScopeProject)). + Short(i18n.T(i18n.CmdDescScopeProject)). Arg(scope.ProjectArgName, cmdBuilder.OptionalArg()). - HelpFlag(i18n.T(i18n.ScopeProjectHelp)). + HelpFlag(i18n.T(i18n.CmdHelpScopeProject)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { projectId, projectSet := cmdData.CliStorage.Data().ScopeProjectId.Get() if projectSet { project, err := repository.GetProjectById(ctx, cmdData.RestApiClient, projectId) if err != nil { - if errorsx.Check(err, errorsx.CheckErrorCode(errorCode.ProjectNotFound)) { + if errorsx.Is(err, errorsx.ErrorCode(errorCode.ProjectNotFound)) { cmdData.UxBlocks.PrintWarning(styles.WarningLine(err.Error())) } else { return err diff --git a/src/cmd/scopeReset.go b/src/cmd/scopeReset.go index d4799558..cc76ba1d 100644 --- a/src/cmd/scopeReset.go +++ b/src/cmd/scopeReset.go @@ -12,8 +12,8 @@ import ( func scopeResetCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("reset"). - Short(i18n.T(i18n.CmdScopeReset)). - HelpFlag(i18n.T(i18n.ScopeResetHelp)). + Short(i18n.T(i18n.CmdDescScopeReset)). + HelpFlag(i18n.T(i18n.CmdHelpScopeReset)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { err := scope.ProjectScopeReset(cmdData) if err != nil { diff --git a/src/cmd/service.go b/src/cmd/service.go index afb2bf6e..3f9ab873 100644 --- a/src/cmd/service.go +++ b/src/cmd/service.go @@ -8,8 +8,8 @@ import ( func serviceCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("service"). - Short(i18n.T(i18n.CmdService)). - HelpFlag(i18n.T(i18n.ServiceHelp)). + Short(i18n.T(i18n.CmdDescService)). + HelpFlag(i18n.T(i18n.CmdHelpService)). AddChildrenCmd(serviceDeleteCmd()). AddChildrenCmd(serviceListCmd()). AddChildrenCmd(serviceLogCmd()). diff --git a/src/cmd/serviceDelete.go b/src/cmd/serviceDelete.go index 629d0884..bd0c2442 100644 --- a/src/cmd/serviceDelete.go +++ b/src/cmd/serviceDelete.go @@ -15,11 +15,11 @@ import ( func serviceDeleteCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("delete"). - Short(i18n.T(i18n.CmdServiceDelete)). + Short(i18n.T(i18n.CmdDescServiceDelete)). ScopeLevel(scope.Service). Arg(scope.ServiceArgName, cmdBuilder.OptionalArg()). BoolFlag("confirm", false, i18n.T(i18n.ConfirmFlag)). - HelpFlag(i18n.T(i18n.ServiceDeleteHelp)). + HelpFlag(i18n.T(i18n.CmdHelpServiceDelete)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { if !cmdData.Params.GetBool("confirm") { confirmed, err := uxHelpers.YesNoPrompt( @@ -58,7 +58,7 @@ func serviceDeleteCmd() *cmdBuilder.Cmd { []uxHelpers.Process{{ F: uxHelpers.CheckZeropsProcess(processId, cmdData.RestApiClient), RunningMessage: i18n.T(i18n.ServiceDeleting), - ErrorMessageMessage: i18n.T(i18n.ServiceDeleting), + ErrorMessageMessage: i18n.T(i18n.ServiceDeleteFailed), SuccessMessage: i18n.T(i18n.ServiceDeleted), }}, ) diff --git a/src/cmd/serviceDeploy.go b/src/cmd/serviceDeploy.go index cce8a116..f6e8a514 100644 --- a/src/cmd/serviceDeploy.go +++ b/src/cmd/serviceDeploy.go @@ -22,8 +22,8 @@ import ( func serviceDeployCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("deploy"). - Short(i18n.T(i18n.CmdDeployDesc)). - Long(i18n.T(i18n.CmdDeployDesc)+"\n\n"+i18n.T(i18n.DeployDescLong)+"\n\n"+i18n.T(i18n.DeployHintPush)). + Short(i18n.T(i18n.CmdDescDeploy)). + Long(i18n.T(i18n.CmdDescDeployLong)). ScopeLevel(scope.Service). Arg("pathToFileOrDir", cmdBuilder.ArrayArg()). StringFlag("workingDir", "./", i18n.T(i18n.BuildWorkingDir)). @@ -31,7 +31,7 @@ func serviceDeployCmd() *cmdBuilder.Cmd { StringFlag("versionName", "", i18n.T(i18n.BuildVersionName)). StringFlag("zeropsYamlPath", "", i18n.T(i18n.ZeropsYamlLocation)). BoolFlag("deployGitFolder", false, i18n.T(i18n.ZeropsYamlLocation)). - HelpFlag(i18n.T(i18n.ServiceDeployHelp)). + HelpFlag(i18n.T(i18n.CmdHelpServiceDeploy)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { uxBlocks := cmdData.UxBlocks @@ -53,7 +53,7 @@ func serviceDeployCmd() *cmdBuilder.Cmd { return err } - uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.BuildDeployCreatingPackageStart))) + uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.PushDeployCreatingPackageStart))) appVersion, err := createAppVersion( ctx, @@ -132,16 +132,22 @@ func serviceDeployCmd() *cmdBuilder.Cmd { } return nil }, - RunningMessage: i18n.T(i18n.BuildDeployUploadingPackageStart), - ErrorMessageMessage: i18n.T(i18n.BuildDeployUploadPackageFailed), - SuccessMessage: i18n.T(i18n.BuildDeployUploadingPackageDone), + RunningMessage: i18n.T(i18n.PushDeployUploadingPackageStart), + ErrorMessageMessage: i18n.T(i18n.PushDeployUploadPackageFailed), + SuccessMessage: i18n.T(i18n.PushDeployUploadingPackageDone), }}, ) if err != nil { return err } - uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.BuildDeployDeployingStart))) + uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.PushDeployCreatingPackageDone))) + + if cmdData.Params.GetString("archiveFilePath") != "" { + uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.PushDeployPackageSavedInto, cmdData.Params.GetString("archiveFilePath")))) + } + + uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.PushDeployDeployingStart))) deployResponse, err := cmdData.RestApiClient.PutAppVersionDeploy( ctx, diff --git a/src/cmd/serviceEnableSubdomain.go b/src/cmd/serviceEnableSubdomain.go index c5881131..ffee9cf0 100644 --- a/src/cmd/serviceEnableSubdomain.go +++ b/src/cmd/serviceEnableSubdomain.go @@ -14,10 +14,10 @@ import ( func serviceEnableSubdomainCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("enable-subdomain"). - Short(i18n.T(i18n.CmdServiceEnableSubdomain)). + Short(i18n.T(i18n.CmdDescServiceEnableSubdomain)). ScopeLevel(scope.Service). Arg(scope.ServiceArgName, cmdBuilder.OptionalArg()). - HelpFlag(i18n.T(i18n.ServiceEnableSubdomainHelp)). + HelpFlag(i18n.T(i18n.CmdHelpServiceEnableSubdomain)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { enableSubdomainResponse, err := cmdData.RestApiClient.PutServiceStackEnableSubdomainAccess( ctx, @@ -42,7 +42,7 @@ func serviceEnableSubdomainCmd() *cmdBuilder.Cmd { []uxHelpers.Process{{ F: uxHelpers.CheckZeropsProcess(processId, cmdData.RestApiClient), RunningMessage: i18n.T(i18n.ServiceEnablingSubdomain), - ErrorMessageMessage: i18n.T(i18n.ServiceEnablingSubdomain), + ErrorMessageMessage: i18n.T(i18n.ServiceEnableSubdomainFailed), SuccessMessage: i18n.T(i18n.ServiceEnabledSubdomain), }}, ) diff --git a/src/cmd/serviceList.go b/src/cmd/serviceList.go index 89ccaeeb..edf61761 100644 --- a/src/cmd/serviceList.go +++ b/src/cmd/serviceList.go @@ -12,10 +12,10 @@ import ( func serviceListCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("list"). - Short(i18n.T(i18n.CmdServiceList)). + Short(i18n.T(i18n.CmdDescServiceList)). ScopeLevel(scope.Project). Arg(scope.ProjectArgName, cmdBuilder.OptionalArg()). - HelpFlag(i18n.T(i18n.ServiceListHelp)). + HelpFlag(i18n.T(i18n.CmdHelpServiceList)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { err := uxHelpers.PrintServiceList(ctx, cmdData.UxBlocks, cmdData.RestApiClient, *cmdData.Project) if err != nil { diff --git a/src/cmd/serviceLog.go b/src/cmd/serviceLog.go index 3d11caa4..e11f5f0a 100644 --- a/src/cmd/serviceLog.go +++ b/src/cmd/serviceLog.go @@ -16,8 +16,8 @@ import ( func serviceLogCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("log"). - Short(i18n.T(i18n.CmdServiceLog)). - Long(i18n.T(i18n.CmdServiceLogLong)+i18n.T(i18n.ServiceLogAdditional)). + Short(i18n.T(i18n.CmdDescServiceLog)). + Long(i18n.T(i18n.CmdDescServiceLogLong)). ScopeLevel(scope.Service). IntFlag("limit", 100, i18n.T(i18n.LogLimitFlag)). StringFlag("minimumSeverity", "", i18n.T(i18n.LogMinSeverityFlag)). @@ -26,7 +26,7 @@ func serviceLogCmd() *cmdBuilder.Cmd { StringFlag("formatTemplate", "", i18n.T(i18n.LogFormatTemplateFlag)). BoolFlag("follow", false, i18n.T(i18n.LogFollowFlag)). BoolFlag("showBuildLogs", false, i18n.T(i18n.LogShowBuildFlag)). - HelpFlag(i18n.T(i18n.ServiceLogHelp)). + HelpFlag(i18n.T(i18n.CmdHelpServiceLog)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { handler := serviceLogs.New( serviceLogs.Config{}, diff --git a/src/cmd/servicePush.go b/src/cmd/servicePush.go index c9bddeda..8e92568e 100644 --- a/src/cmd/servicePush.go +++ b/src/cmd/servicePush.go @@ -22,15 +22,15 @@ import ( func servicePushCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("push"). - Short(i18n.T(i18n.CmdPushDesc)). - Long(i18n.T(i18n.CmdPushDesc)+"\n\n"+i18n.T(i18n.PushDescLong)). + Short(i18n.T(i18n.CmdDescPush)). + Long(i18n.T(i18n.CmdDescPushLong)). ScopeLevel(scope.Service). StringFlag("workingDir", "./", i18n.T(i18n.BuildWorkingDir)). StringFlag("archiveFilePath", "", i18n.T(i18n.BuildArchiveFilePath)). StringFlag("versionName", "", i18n.T(i18n.BuildVersionName)). StringFlag("zeropsYamlPath", "", i18n.T(i18n.ZeropsYamlLocation)). BoolFlag("deployGitFolder", false, i18n.T(i18n.UploadGitFolder)). - HelpFlag(i18n.T(i18n.ServicePushHelp)). + HelpFlag(i18n.T(i18n.CmdHelpPush)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { uxBlocks := cmdData.UxBlocks @@ -38,7 +38,7 @@ func servicePushCmd() *cmdBuilder.Cmd { DeployGitFolder: cmdData.Params.GetBool("deployGitFolder"), }) - uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.BuildDeployCreatingPackageStart))) + uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.PushDeployCreatingPackageStart))) configContent, err := getValidConfigContent( uxBlocks, @@ -126,22 +126,22 @@ func servicePushCmd() *cmdBuilder.Cmd { } return nil }, - RunningMessage: i18n.T(i18n.BuildDeployUploadingPackageStart), - ErrorMessageMessage: i18n.T(i18n.BuildDeployUploadPackageFailed), - SuccessMessage: i18n.T(i18n.BuildDeployUploadingPackageDone), + RunningMessage: i18n.T(i18n.PushDeployUploadingPackageStart), + ErrorMessageMessage: i18n.T(i18n.PushDeployUploadPackageFailed), + SuccessMessage: i18n.T(i18n.PushDeployUploadingPackageDone), }}, ) if err != nil { return err } - uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.BuildDeployCreatingPackageDone))) + uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.PushDeployCreatingPackageDone))) if cmdData.Params.GetString("archiveFilePath") != "" { - uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.BuildDeployPackageSavedInto, cmdData.Params.GetString("archiveFilePath")))) + uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.PushDeployPackageSavedInto, cmdData.Params.GetString("archiveFilePath")))) } - uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.BuildDeployDeployingStart))) + uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.PushDeployDeployingStart))) deployResponse, err := cmdData.RestApiClient.PutAppVersionBuildAndDeploy(ctx, dtoPath.AppVersionId{ diff --git a/src/cmd/servicePushDeployShared.go b/src/cmd/servicePushDeployShared.go index 0cb2b1d7..9efc1bd3 100644 --- a/src/cmd/servicePushDeployShared.go +++ b/src/cmd/servicePushDeployShared.go @@ -6,22 +6,22 @@ import ( "net/http" "os" "path/filepath" + "strings" "github.com/pkg/errors" - "github.com/zeropsio/zcli/src/entity" + "github.com/zeropsio/zcli/src/errorsx" "github.com/zeropsio/zcli/src/httpClient" "github.com/zeropsio/zcli/src/i18n" "github.com/zeropsio/zcli/src/uxBlock" "github.com/zeropsio/zcli/src/uxBlock/styles" "github.com/zeropsio/zcli/src/zeropsRestApiClient" + "github.com/zeropsio/zerops-go/apiError" "github.com/zeropsio/zerops-go/dto/input/body" "github.com/zeropsio/zerops-go/dto/output" "github.com/zeropsio/zerops-go/types" ) -const ZeropsYamlFileName = "zerops.yml" - func createAppVersion( ctx context.Context, restApiClient *zeropsRestApiClient.Handler, @@ -88,41 +88,51 @@ func packageUpload(ctx context.Context, client *httpClient.Handler, uploadUrl st return err } if cephResponse.StatusCode != http.StatusOK { - return errors.New(i18n.T(i18n.BuildDeployUploadPackageFailed)) + return errors.New(i18n.T(i18n.PushDeployUploadPackageFailed)) } return nil } -func getValidConfigContent(uxBlocks uxBlock.UxBlocks, workingDir string, zeropsYamlPath string) ([]byte, error) { - workingDir, err := filepath.Abs(workingDir) +func getValidConfigContent(uxBlocks uxBlock.UxBlocks, selectedWorkingDir string, selectedZeropsYamlPath string) ([]byte, error) { + workingDir, err := filepath.Abs(selectedWorkingDir) if err != nil { return nil, err } - if zeropsYamlPath != "" { - workingDir = filepath.Join(workingDir, zeropsYamlPath) + var pathsToCheck []string + if selectedZeropsYamlPath != "" { + if filepath.IsAbs(selectedZeropsYamlPath) { + pathsToCheck = append(pathsToCheck, selectedZeropsYamlPath) + } else { + pathsToCheck = append(pathsToCheck, filepath.Join(workingDir, selectedZeropsYamlPath)) + } + } else { + pathsToCheck = append(pathsToCheck, filepath.Join(workingDir, "zerops.yaml")) + pathsToCheck = append(pathsToCheck, filepath.Join(workingDir, "zerops.yml")) } - zeropsYamlPath = filepath.Join(workingDir, ZeropsYamlFileName) + zeropsYamlPath, err := func() (string, error) { + for _, path := range pathsToCheck { + zeropsYamlStat, err := os.Stat(path) + if err == nil { + uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.PushDeployZeropsYamlFound, path))) - zeropsYamlStat, err := os.Stat(zeropsYamlPath) - if err != nil { - if os.IsNotExist(err) { - return nil, errors.New(i18n.T(i18n.BuildDeployZeropsYamlNotFound, zeropsYamlPath)) + if zeropsYamlStat.Size() == 0 { + return "", errors.New(i18n.T(i18n.PushDeployZeropsYamlEmpty)) + } + if zeropsYamlStat.Size() > 10*1024 { + return "", errors.New(i18n.T(i18n.PushDeployZeropsYamlTooLarge)) + } + return path, nil + } } + return "", errors.New(i18n.T(i18n.PushDeployZeropsYamlNotFound, strings.Join(pathsToCheck, ", "))) + }() + if err != nil { return nil, err } - uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.BuildDeployZeropsYamlFound, zeropsYamlPath))) - - if zeropsYamlStat.Size() == 0 { - return nil, errors.New(i18n.T(i18n.BuildDeployZeropsYamlEmpty)) - } - if zeropsYamlStat.Size() > 10*1024 { - return nil, errors.New(i18n.T(i18n.BuildDeployZeropsYamlTooLarge)) - } - yamlContent, err := os.ReadFile(zeropsYamlPath) if err != nil { return nil, err @@ -147,7 +157,18 @@ func validateZeropsYamlContent( return err } if _, err = resp.Output(); err != nil { - return err + return errorsx.Convert( + err, + errorsx.And( + errorsx.ErrorCode("zeropsYamlServiceNotFound"), + errorsx.Meta(func(_ apiError.Error, metaItem map[string]interface{}) string { + if name, ok := metaItem["name"]; ok { + return i18n.T(i18n.ErrorServiceNotFound, name) + } + return "" + }), + ), + ) } return nil diff --git a/src/cmd/serviceStart.go b/src/cmd/serviceStart.go index 5e75140e..32909f53 100644 --- a/src/cmd/serviceStart.go +++ b/src/cmd/serviceStart.go @@ -13,10 +13,10 @@ import ( func serviceStartCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("start"). - Short(i18n.T(i18n.CmdServiceStart)). + Short(i18n.T(i18n.CmdDescServiceStart)). ScopeLevel(scope.Service). Arg(scope.ServiceArgName, cmdBuilder.OptionalArg(), cmdBuilder.OptionalArgLabel("{serviceName | serviceId}")). - HelpFlag(i18n.T(i18n.ServiceStartHelp)). + HelpFlag(i18n.T(i18n.CmdHelpServiceStart)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { startServiceResponse, err := cmdData.RestApiClient.PutServiceStackStart( ctx, @@ -41,7 +41,7 @@ func serviceStartCmd() *cmdBuilder.Cmd { []uxHelpers.Process{{ F: uxHelpers.CheckZeropsProcess(processId, cmdData.RestApiClient), RunningMessage: i18n.T(i18n.ServiceStarting), - ErrorMessageMessage: i18n.T(i18n.ServiceStarting), + ErrorMessageMessage: i18n.T(i18n.ServiceStartFailed), SuccessMessage: i18n.T(i18n.ServiceStarted), }}, ) diff --git a/src/cmd/serviceStop.go b/src/cmd/serviceStop.go index cd9548d3..2247bd15 100644 --- a/src/cmd/serviceStop.go +++ b/src/cmd/serviceStop.go @@ -14,10 +14,10 @@ import ( func serviceStopCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("stop"). - Short(i18n.T(i18n.CmdServiceStop)). + Short(i18n.T(i18n.CmdDescServiceStop)). ScopeLevel(scope.Service). Arg(scope.ServiceArgName, cmdBuilder.OptionalArg()). - HelpFlag(i18n.T(i18n.ServiceStopHelp)). + HelpFlag(i18n.T(i18n.CmdHelpServiceStop)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { stopServiceResponse, err := cmdData.RestApiClient.PutServiceStackStop( ctx, @@ -42,7 +42,7 @@ func serviceStopCmd() *cmdBuilder.Cmd { []uxHelpers.Process{{ F: uxHelpers.CheckZeropsProcess(processId, cmdData.RestApiClient), RunningMessage: i18n.T(i18n.ServiceStopping), - ErrorMessageMessage: i18n.T(i18n.ServiceStopping), + ErrorMessageMessage: i18n.T(i18n.ServiceStopFailed), SuccessMessage: i18n.T(i18n.ServiceStopped), }}, ) diff --git a/src/cmd/statusShowDebugLogs.go b/src/cmd/statusShowDebugLogs.go index 5e4c93ec..500910ed 100644 --- a/src/cmd/statusShowDebugLogs.go +++ b/src/cmd/statusShowDebugLogs.go @@ -18,8 +18,8 @@ import ( func statusShowDebugLogsCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("show-debug-logs"). - Short(i18n.T(i18n.CmdStatusShowDebugLogs)). - HelpFlag(i18n.T(i18n.StatusShowDebugLogsHelp)). + Short(i18n.T(i18n.CmdDescStatusShowDebugLogs)). + HelpFlag(i18n.T(i18n.CmdHelpStatusShowDebugLogs)). GuestRunFunc(func(ctx context.Context, cmdData *cmdBuilder.GuestCmdData) error { logFilePath, fileMode, err := constants.LogFilePath() if err != nil { diff --git a/src/cmd/version.go b/src/cmd/version.go index 3b3ade57..32f64d62 100644 --- a/src/cmd/version.go +++ b/src/cmd/version.go @@ -14,8 +14,8 @@ var version string func versionCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("version"). - Short(i18n.T(i18n.CmdVersion)). - HelpFlag(i18n.T(i18n.VersionHelp)). + Short(i18n.T(i18n.CmdDescVersion)). + HelpFlag(i18n.T(i18n.CmdHelpVersion)). GuestRunFunc(func(ctx context.Context, cmdData *cmdBuilder.GuestCmdData) error { fmt.Printf("zcli version %s (%s) %s/%s\n", version, runtime.Version(), runtime.GOOS, runtime.GOARCH) diff --git a/src/cmd/vpn.go b/src/cmd/vpn.go index c0001e4a..5d4589f5 100644 --- a/src/cmd/vpn.go +++ b/src/cmd/vpn.go @@ -8,8 +8,8 @@ import ( func vpnCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("vpn"). - Short(i18n.T(i18n.CmdVpn)). - HelpFlag(i18n.T(i18n.VpnHelp)). + Short(i18n.T(i18n.CmdDescVpn)). + HelpFlag(i18n.T(i18n.CmdHelpVpn)). AddChildrenCmd(vpnUpCmd()). AddChildrenCmd(vpnDownCmd()) } diff --git a/src/cmd/vpnDown.go b/src/cmd/vpnDown.go index a7a22a85..3641e184 100644 --- a/src/cmd/vpnDown.go +++ b/src/cmd/vpnDown.go @@ -19,8 +19,8 @@ import ( func vpnDownCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("down"). - Short(i18n.T(i18n.CmdVpnDown)). - HelpFlag(i18n.T(i18n.VpnDownHelp)). + Short(i18n.T(i18n.CmdDescVpnDown)). + HelpFlag(i18n.T(i18n.CmdHelpVpnDown)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { return disconnectVpn(ctx, cmdData.UxBlocks) }) diff --git a/src/cmd/vpnUp.go b/src/cmd/vpnUp.go index ad58c63d..d02c70c4 100644 --- a/src/cmd/vpnUp.go +++ b/src/cmd/vpnUp.go @@ -19,7 +19,6 @@ import ( "github.com/zeropsio/zcli/src/file" "github.com/zeropsio/zcli/src/i18n" "github.com/zeropsio/zcli/src/nettools" - "github.com/zeropsio/zcli/src/uxBlock" "github.com/zeropsio/zcli/src/uxBlock/styles" "github.com/zeropsio/zcli/src/uxHelpers" "github.com/zeropsio/zerops-go/dto/input/body" @@ -33,20 +32,15 @@ const vpnCheckAddress = "logger.core.zerops" func vpnUpCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). Use("up"). - Short(i18n.T(i18n.CmdVpnUp)). + Short(i18n.T(i18n.CmdDescVpnUp)). ScopeLevel(scope.Project). Arg(scope.ProjectArgName, cmdBuilder.OptionalArg()). BoolFlag("auto-disconnect", false, i18n.T(i18n.VpnAutoDisconnectFlag)). - HelpFlag(i18n.T(i18n.VpnUpHelp)). + HelpFlag(i18n.T(i18n.CmdHelpVpnUp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { uxBlocks := cmdData.UxBlocks - _, err := exec.LookPath("wg-quick") - if err != nil { - return errors.New(i18n.T(i18n.VpnWgQuickIsNotInstalled)) - } - - if !isVpnDisconnect(ctx, uxBlocks) { + if isVpnUp(ctx) { if cmdData.Params.GetBool("auto-disconnect") { err := disconnectVpn(ctx, uxBlocks) if err != nil { @@ -142,22 +136,35 @@ func vpnUpCmd() *cmdBuilder.Cmd { return err } + _, err = exec.LookPath("wg-quick") + if err != nil { + return errors.New(i18n.T(i18n.VpnWgQuickIsNotInstalled)) + } + c := exec.CommandContext(ctx, "wg-quick", "up", filePath) _, err = cmdRunner.Run(c) if err != nil { return err } - if !isVpnConnect(ctx, uxBlocks) { + if isVpnUp(ctx) { uxBlocks.PrintWarning(styles.WarningLine(i18n.T(i18n.VpnPingFailed))) + } else { + uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.VpnUp))) } - uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.VpnUp))) - return nil }) } +func isVpnUp(ctx context.Context) bool { + ctx, cancel := context.WithTimeout(ctx, time.Second) + defer cancel() + + err := nettools.Ping(ctx, vpnCheckAddress) + return err == nil +} + func getOrCreatePrivateVpnKey(cmdData *cmdBuilder.LoggedUserCmdData) (wgtypes.Key, error) { projectId := cmdData.Project.ID @@ -180,52 +187,6 @@ func getOrCreatePrivateVpnKey(cmdData *cmdBuilder.LoggedUserCmdData) (wgtypes.Ke return vpnKey, nil } -func isVpnConnect(ctx context.Context, uxBlocks uxBlock.UxBlocks) bool { - p := []uxHelpers.Process{ - { - F: func(ctx context.Context) error { - pingCtx, cancel := context.WithTimeout(ctx, time.Second) - defer cancel() - - return nettools.Ping(pingCtx, vpnCheckAddress) - }, - RunningMessage: i18n.T(i18n.VpnCheckingConnection), - ErrorMessageMessage: i18n.T(i18n.VpnCheckingConnectionIsNotActive), - SuccessMessage: i18n.T(i18n.VpnCheckingConnectionIsActive), - }, - } - - err := uxHelpers.ProcessCheckWithSpinner(ctx, uxBlocks, p) - - return err == nil -} - -func isVpnDisconnect(ctx context.Context, uxBlocks uxBlock.UxBlocks) bool { - p := []uxHelpers.Process{ - { - F: func(ctx context.Context) error { - ctx, cancel := context.WithTimeout(ctx, time.Second*5) - defer cancel() - - err := nettools.Ping(ctx, vpnCheckAddress) - if err != nil { - //nolint:nilerr // Why: error is good in this case - return nil - } else { - return errors.New("vpn is connected") - } - }, - RunningMessage: i18n.T(i18n.VpnCheckingConnection), - ErrorMessageMessage: i18n.T(i18n.VpnCheckingConnectionIsActive), - SuccessMessage: i18n.T(i18n.VpnCheckingConnectionIsNotActive), - }, - } - - err := uxHelpers.ProcessCheckWithSpinner(ctx, uxBlocks, p) - - return err == nil -} - var vpnTmpl = ` [Interface] PrivateKey = {{.PrivateKey}} diff --git a/src/entity/repository/service.go b/src/entity/repository/service.go index 4ba908eb..eb0edd98 100644 --- a/src/entity/repository/service.go +++ b/src/entity/repository/service.go @@ -7,6 +7,7 @@ import ( "github.com/zeropsio/zcli/src/errorsx" "github.com/zeropsio/zcli/src/i18n" "github.com/zeropsio/zcli/src/zeropsRestApiClient" + "github.com/zeropsio/zerops-go/apiError" "github.com/zeropsio/zerops-go/dto/input/body" "github.com/zeropsio/zerops-go/dto/input/path" "github.com/zeropsio/zerops-go/dto/output" @@ -23,15 +24,19 @@ func GetServiceByIdOrName( ) (*entity.Service, error) { service, err := GetServiceById(ctx, restApiClient, uuid.ServiceStackId(serviceIdOrName)) if err != nil { - if errorsx.Check(err, - errorsx.CheckErrorCode(errorCode.InvalidUserInput), - errorsx.CheckErrorCode(errorCode.ServiceStackNotFound), - ) { + if errorsx.Is(err, errorsx.Or( + errorsx.ErrorCode(errorCode.InvalidUserInput), + errorsx.ErrorCode(errorCode.ServiceStackNotFound), + )) { service, err = GetServiceByName(ctx, restApiClient, projectId, types.String(serviceIdOrName)) if err != nil { return nil, errorsx.Convert( err, - errorsx.ConvertErrorCode(errorCode.ServiceStackNotFound, i18n.T(i18n.ErrorServiceNotFound, serviceIdOrName)), + errorsx.ErrorCode(errorCode.ServiceStackNotFound, errorsx.ErrorCodeErrorMessage( + func(_ apiError.Error) string { + return i18n.T(i18n.ErrorServiceNotFound, serviceIdOrName) + }, + )), ) } } diff --git a/src/errorsx/checker.go b/src/errorsx/checker.go index 09d0ecdd..096198dd 100644 --- a/src/errorsx/checker.go +++ b/src/errorsx/checker.go @@ -1,81 +1,34 @@ package errorsx -import ( - "fmt" +type Check func(err error) error - "github.com/pkg/errors" - "github.com/zeropsio/zerops-go/apiError" - "github.com/zeropsio/zerops-go/errorCode" -) - -type convertor func(err error) error -type check func(err error, errMessage string) error - -func Check(err error, checks ...check) bool { - if err == nil { +func Is(err error, check Check) bool { + if check == nil { return false } - - for _, check := range checks { - if err := check(err, ""); err != nil { - return true - } - } - - return false + return check(err) != nil } -func Convert(err error, convertors ...convertor) error { - if err == nil { - return nil +func Convert(err error, check Check) error { + if check == nil { + return err } - for _, convertor := range convertors { - if err := convertor(err); err != nil { - return err - } + if newErr := check(err); newErr != nil { + return newErr } - return err } -func CheckErrorCode(errorCode errorCode.ErrorCode) check { - return func(err error, errMessage string) error { - var apiErr apiError.Error - if !errors.As(err, &apiErr) { - return nil - } - if string(errorCode) != apiErr.GetErrorCode() { - return nil - } - - return NewUserError(errMessage, err) - } -} - -func CheckInvalidUserInput(parameterName string) check { - return func(err error, errMessage string) error { - var apiErr apiError.Error - if !errors.As(err, &apiErr) { - return nil - } - - if string(errorCode.InvalidUserInput) != apiErr.GetErrorCode() { - return nil - } - - meta, ok := apiErr.GetMeta().([]interface{}) - if !ok { +func Or(checks ...Check) Check { + return func(err error) error { + if err == nil { return nil } - for _, metaItem := range meta { - if metaItemTyped, ok := metaItem.(map[string]interface{}); ok { - if parameterValue, ok := metaItemTyped["parameter"]; ok { - if parameterValue == parameterName { - return NewUserError(fmt.Sprintf(errMessage, metaItemTyped["message"]), err) - } - } + for _, convertor := range checks { + if err := convertor(err); err != nil { + return err } } @@ -83,14 +36,15 @@ func CheckInvalidUserInput(parameterName string) check { } } -func ConvertErrorCode(errorCode errorCode.ErrorCode, errMessage string) convertor { +func And(checks ...Check) Check { return func(err error) error { - return CheckErrorCode(errorCode)(err, errMessage) - } -} - -func ConvertInvalidUserInput(parameterName string, errMessage string) convertor { - return func(err error) error { - return CheckInvalidUserInput(parameterName)(err, errMessage) + var lastResponse error + for _, check := range checks { + lastResponse = check(err) + if lastResponse == nil { + return nil + } + } + return lastResponse } } diff --git a/src/errorsx/checker_test.go b/src/errorsx/checker_test.go new file mode 100644 index 00000000..52c7afe0 --- /dev/null +++ b/src/errorsx/checker_test.go @@ -0,0 +1,67 @@ +package errorsx + +import ( + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeropsio/zerops-go/apiError" + "github.com/zeropsio/zerops-go/errorCode" +) + +func TestCascading(t *testing.T) { + inputError := apiError.Error{ + HttpStatusCode: 400, + ErrorCode: string(errorCode.InvalidUserInput), + Message: "apiErrorMessage", + } + + tests := []struct { + name string + inputErr error + check Check + convertedError error + }{ + { + name: "No checks", + inputErr: inputError, + check: nil, + convertedError: inputError, + }, + { + name: "or", + inputErr: inputError, + check: Or( + ErrorCode(errorCode.PaymentFailedError), + ErrorCode(errorCode.InvalidUserInput), + ErrorCode(errorCode.ServiceStackNotFound), + ), + convertedError: NewUserError(inputError.Message, inputError), + }, + { + name: "and", + inputErr: inputError, + check: And( + HttpStatusCode(400), + ErrorCode(errorCode.InvalidUserInput), + ), + convertedError: NewUserError(inputError.Message, inputError), + }, + { + name: "complex", + inputErr: inputError, + check: Or( + And(ErrorCode(errorCode.PaymentNotFound)), + And(HttpStatusCode(400), ErrorCode(errorCode.InvalidUserInput)), + And(ErrorCode(errorCode.ServiceStackNotFound)), + ), + convertedError: NewUserError(inputError.Message, inputError), + }, + } + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + response := Convert(tt.inputErr, tt.check) + require.Equal(t, tt.convertedError, response) + }) + } +} diff --git a/src/errorsx/checks.go b/src/errorsx/checks.go new file mode 100644 index 00000000..2eb091c7 --- /dev/null +++ b/src/errorsx/checks.go @@ -0,0 +1,149 @@ +package errorsx + +import ( + "github.com/pkg/errors" + "github.com/zeropsio/zerops-go/apiError" + "github.com/zeropsio/zerops-go/errorCode" +) + +type errorCodeConfig struct { + errorMessage func(apiError.Error) string +} + +func ErrorCodeErrorMessage(f func(apiErr apiError.Error) string) errorCodeOption { + return func(cfg *errorCodeConfig) { + cfg.errorMessage = f + } +} + +type errorCodeOption = func(cfg *errorCodeConfig) + +func ErrorCode(errorCode errorCode.ErrorCode, auxOptions ...errorCodeOption) Check { + return func(err error) error { + cfg := errorCodeConfig{} + for _, opt := range auxOptions { + opt(&cfg) + } + + var apiErr apiError.Error + if !errors.As(err, &apiErr) { + return nil + } + + if string(errorCode) != apiErr.GetErrorCode() { + return nil + } + + if cfg.errorMessage != nil { + return NewUserError(cfg.errorMessage(apiErr), err) + } else { + return NewUserError(apiErr.GetMessage(), err) + } + } +} + +type httpStatusCodeConfig struct { + errorMessage func(apiError.Error) string +} + +func HttpStatusCodeErrorMessage(f func(apiErr apiError.Error) string) httpStatusCodeOption { + return func(cfg *httpStatusCodeConfig) { + cfg.errorMessage = f + } +} + +type httpStatusCodeOption = func(cfg *httpStatusCodeConfig) + +func HttpStatusCode(httpStatusCode int, auxOptions ...httpStatusCodeOption) Check { + return func(err error) error { + cfg := httpStatusCodeConfig{} + for _, opt := range auxOptions { + opt(&cfg) + } + + var apiErr apiError.Error + if !errors.As(err, &apiErr) { + return nil + } + + if httpStatusCode != apiErr.GetHttpStatusCode() { + return nil + } + + if cfg.errorMessage != nil { + return NewUserError(cfg.errorMessage(apiErr), err) + } else { + return NewUserError(apiErr.GetMessage(), err) + } + } +} + +type invalidUserInputConfig struct { + errorMessage func(apiError.Error, map[string]interface{}) string +} + +func InvalidUserInputErrorMessage( + f func(apiErr apiError.Error, metaItem map[string]interface{}) string, +) invalidUserInputOption { + return func(cfg *invalidUserInputConfig) { + cfg.errorMessage = f + } +} + +type invalidUserInputOption = func(cfg *invalidUserInputConfig) + +func InvalidUserInput(parameterName string, auxOptions ...invalidUserInputOption) Check { + return func(err error) error { + cfg := invalidUserInputConfig{} + for _, opt := range auxOptions { + opt(&cfg) + } + + if err := ErrorCode(errorCode.InvalidUserInput)(err); err == nil { + return nil + } + + var apiErr apiError.Error + if !errors.As(err, &apiErr) { + return nil + } + + meta, ok := apiErr.GetMeta().([]interface{}) + if !ok { + return nil + } + + for _, metaItem := range meta { + if metaItemTyped, ok := metaItem.(map[string]interface{}); ok { + if metaParameterName, ok := metaItemTyped["parameter"]; ok { + if metaParameterName == parameterName { + if cfg.errorMessage != nil { + return NewUserError(cfg.errorMessage(apiErr, metaItemTyped), err) + } else { + return NewUserError(apiErr.GetMessage(), err) + } + } + } + } + } + + return nil + } +} + +func Meta( + errorMessage func(apiErr apiError.Error, metaItem map[string]interface{}) string, +) Check { + return func(err error) error { + var apiErr apiError.Error + if !errors.As(err, &apiErr) { + return nil + } + + if metaTyped, ok := apiErr.GetMeta().(map[string]interface{}); ok { + return NewUserError(errorMessage(apiErr, metaTyped), err) + } + + return nil + } +} diff --git a/src/errorsx/checks_test.go b/src/errorsx/checks_test.go new file mode 100644 index 00000000..eb40c93f --- /dev/null +++ b/src/errorsx/checks_test.go @@ -0,0 +1,94 @@ +package errorsx + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + "github.com/zeropsio/zerops-go/apiError" + "github.com/zeropsio/zerops-go/errorCode" +) + +func TestCheckInvalidUserInput(t *testing.T) { + inputErr := apiError.Error{ + ErrorCode: string(errorCode.InvalidUserInput), + Message: "invalid user input", + Meta: []interface{}{ + map[string]interface{}{"parameter": "name", "message": "name message"}, + map[string]interface{}{"parameter": "id", "message": "id message"}, + }, + } + + { + check := Or(InvalidUserInput("id")) + + expectedError := NewUserError("invalid user input", inputErr) + + require.True(t, Is(inputErr, check)) + require.Equal(t, expectedError, Convert(inputErr, check)) + } + { + check := Or(InvalidUserInput( + "id", + InvalidUserInputErrorMessage(func(apiErr apiError.Error, metaItem map[string]interface{}) string { + return fmt.Sprintf("%s:%s", apiErr.GetMessage(), metaItem["message"]) + }), + )) + + expectedError := NewUserError("invalid user input:id message", inputErr) + + require.True(t, Is(inputErr, check)) + require.Equal(t, expectedError, Convert(inputErr, check)) + } + { + check := Or(InvalidUserInput("age")) + + require.False(t, Is(inputErr, check)) + require.Equal(t, inputErr, Convert(inputErr, check)) + } +} + +func TestErrorCode(t *testing.T) { + inputErr := apiError.Error{ + ErrorCode: string(errorCode.InvalidUserInput), + Message: "invalid user input", + } + + { + check := Or(ErrorCode(errorCode.InvalidUserInput)) + + expectedError := NewUserError("invalid user input", inputErr) + + require.True(t, Is(inputErr, check)) + require.Equal(t, expectedError, Convert(inputErr, check)) + } + { + check := Or(ErrorCode(errorCode.PaymentFailedError)) + + require.False(t, Is(inputErr, check)) + require.Equal(t, inputErr, Convert(inputErr, check)) + } +} + +func TestHttpStatusCode(t *testing.T) { + inputErr := apiError.Error{ + HttpStatusCode: 400, + ErrorCode: string(errorCode.InvalidUserInput), + Message: "invalid user input", + } + + { + check := Or(HttpStatusCode(400)) + + expectedError := NewUserError("invalid user input", inputErr) + + require.True(t, Is(inputErr, check)) + require.Equal(t, expectedError, Convert(inputErr, check)) + } + { + check := Or(HttpStatusCode(500)) + + require.False(t, Is(inputErr, check)) + require.Equal(t, inputErr, Convert(inputErr, check)) + } +} diff --git a/src/i18n/en.go b/src/i18n/en.go index 2c74d871..e9294d40 100644 --- a/src/i18n/en.go +++ b/src/i18n/en.go @@ -1,62 +1,186 @@ package i18n +import "fmt" + var en = map[string]string{ - // help - LoginHelp: "the login command.", - ProjectHelp: "the project command.", - ProjectListHelp: "the project list command.", - ScopeHelp: "the scope command.", - ScopeProjectHelp: "the scope project command.", - ScopeResetHelp: "the scope reset command.", - ProjectDeleteHelp: "the project delete command.", - ProjectImportHelp: "the project import command.", - ProjectServiceImportHelp: "the project service import command.", - ServiceHelp: "the service command.", - ServiceStartHelp: "the service start command.", - ServiceStopHelp: "the enable Zerops subdomain command.", - ServiceDeleteHelp: "the service delete command.", - ServiceLogHelp: "the service log command.", - ServiceDeployHelp: "the service deploy command.", - ServiceListHelp: "the service list command.", - ServicePushHelp: "the service push command.", - ServiceEnableSubdomainHelp: "the service stop command.", - StatusShowDebugLogsHelp: "the status show debug logs command.", - VersionHelp: "the version command.", - VpnHelp: "the vpn command.", - VpnUpHelp: "the vpn up command.", - VpnDownHelp: "the vpn down command.", - - // cmd short - CmdDeployDesc: "Deploys your application to Zerops.", - CmdPushDesc: "Builds your application in Zerops and deploys it.", - CmdLogin: "Logs you into Zerops. Use a generated Zerops token or your login e-mail and password.", - CmdStatusShowDebugLogs: "Shows zCLI debug logs.", - CmdVersion: "Shows the current zCLI version.", - CmdProject: "Project commands group.", - CmdService: "Zerops service commands group.", - CmdProjectList: "Lists all projects.", - CmdScope: "Scope commands group", - CmdScopeProject: "Sets the scope for project. All commands that require project ID will use the selected one.", - CmdScopeReset: "Resets the scope for project and service.", - CmdProjectDelete: "Deletes a project and all of its services.", - CmdProjectImport: "Creates a new project with one or more services.", - CmdServiceList: "Lists all services in the project.", - CmdServiceImport: "Creates one or more Zerops services in an existing project.", - CmdServiceStart: "Starts the Zerops service.", - CmdServiceStop: "Stops the Zerops service.", - CmdServiceDelete: "Deletes the Zerops service.", - CmdServiceLog: "Get service runtime or build log to stdout.", - CmdServiceEnableSubdomain: "Enables access through Zerops subdomain.", - CmdVpn: "VPN commands group", - CmdVpnUp: "Connects to the Zerops VPN.", - CmdVpnDown: "Disconnects from the Zerops VPN.", - - // cmd long - CmdProjectImportLong: "Creates a new project with one or more services according to the definition in the import YAML file.", - DeployDescLong: "pathToFileOrDir defines a path to one or more directories and/or files relative to the working\ndirectory. The working directory is by default the current directory and can be changed\nusing the --workingDir flag. zCLI deploys selected directories and/or files to Zerops.", - PushDescLong: "The command triggers the build pipeline defined in zerops.yml. Zerops.yml must be in the working\ndirectory. The working directory is by default the current directory and can be changed\nusing the --workingDir flag. zCLI uploads all files and subdirectories of the working\ndirectory to Zerops and starts the build pipeline. Files found in the .gitignore\nfile will be ignored.\n\nIf you just want to deploy your application to Zerops, use the zcli deploy command instead.", - CmdServiceLogLong: "Returns service runtime or build log to stdout. By default, the command returns the last 100\nlog messages from all service runtime containers and exits.\n", - ServiceLogAdditional: "\nUse the alone in the command to return log messages from all runtime containers.\nSet @1 to return log messages from the first runtime container only.\nSet @build to return log messages from the last build if available.", + // login + CmdHelpLogin: "the login command.", + CmdDescLogin: "Logs you into Zerops. Use a generated Zerops token or your login e-mail and password.", + LoginSuccess: "You are logged as %s <%s>", + RegionNotFound: "Selected region %s not found", + RegionTableColumnName: "Name", + + // scope + CmdHelpScope: "the scope command.", + CmdDescScope: "Scope commands group", + + // scope project + CmdHelpScopeProject: "the scope project command.", + CmdDescScopeProject: "Sets the scope for project. All commands that require project ID will use the selected one.", + + // scope reset + CmdHelpScopeReset: "the scope reset command.", + CmdDescScopeReset: "Resets the scope for project and service.", + + // project + CmdHelpProject: "the project command.", + CmdDescProject: "Project commands group.", + + // project lit + CmdHelpProjectList: "the project list command.", + CmdDescProjectList: "Lists all projects.", + + // project delete + CmdHelpProjectDelete: "the project delete command.", + CmdDescProjectDelete: "Deletes a project and all of its services.", + ProjectDeleteConfirm: "Project %s will be deleted? \n Are you sure?", + ServiceDeleteConfirm: "Service %s will be deleted? \n Are you sure?", + ProjectDeleting: "Project is being deleted", + ProjectDeleteFailed: "Project deletion failed", + ProjectDeleted: "Project was deleted", + + // project import + CmdHelpProjectImport: "the project import command.", + CmdDescProjectImport: "Creates a new project with one or more services.", + CmdDescProjectImportLong: "Creates a new project with one or more services according to the definition in the import YAML file.", + ProjectImported: "project imported", + + // project service import + CmdHelpProjectServiceImport: "the project service import command.", + CmdDescProjectServiceImport: "Creates one or more Zerops services in an existing project.", + ServiceImported: "service(s) imported", + + // service + CmdHelpService: "the service command.", + CmdDescService: "Zerops service commands group.", + + // service start + CmdHelpServiceStart: "the service start command.", + CmdDescServiceStart: "Starts the Zerops service.", + ServiceStarting: "Service is being started", + ServiceStartFailed: "Service start failed", + ServiceStarted: "Service was started", + + // service stop + CmdHelpServiceStop: "the enable Zerops subdomain command.", + CmdDescServiceStop: "Starts the Zerops service.", + ServiceStopping: "Service is being stopped", + ServiceStopFailed: "Service stop failed", + ServiceStopped: "Service was stopped", + + // service delete + CmdHelpServiceDelete: "the service delete command.", + CmdDescServiceDelete: "Deletes the Zerops service.", + ServiceDeleting: "Service is being deleted", + ServiceDeleteFailed: "Service deletion failed", + ServiceDeleted: "Service was deleted", + + // service log + CmdHelpServiceLog: "the service log command.", + CmdDescServiceLog: "Get service runtime or build log to stdout.", + CmdDescServiceLogLong: "Returns service runtime or build log to stdout. By default, the command returns the last 100\n" + + "log messages from all service runtime containers and exits.\n\n" + + "Use the alone in the command to return log messages from all runtime containers.\n" + + "Set @1 to return log messages from the first runtime container only.\n" + + "Set @build to return log messages from the last build if available.", + LogLimitInvalid: "Invalid --limit value. Allowed interval is <1;1000>", + LogMinSeverityInvalid: "Invalid --minimumSeverity value.", + LogMinSeverityStringLimitErr: "Allowed values are EMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFORMATIONAL, DEBUG.", + LogMinSeverityNumLimitErr: "Allowed interval is <0;7>.", + LogFormatInvalid: "Invalid --format value. Allowed values are FULL, SHORT, JSON, JSONSTREAM.", + LogFormatTemplateMismatch: "--formatTemplate can be used only in combination with --format=FULL.", + LogFormatStreamMismatch: "--format=JSON cannot be used in combination with --follow. Use --format=JSONSTREAM instead.", + LogFormatTemplateInvalid: "Invalid --formatTemplate content. The custom template failed with following error:", + LogFormatTemplateNoSpace: "Template items must be split by a (single) space.", + LogNoBuildFound: "No build was found for this service.", + LogBuildStatusUploading: "Service status UPLOADING, need to wait for app version data.", + LogAccessFailed: "Request for access to logs failed.", + LogMsgTypeInvalid: "Invalid --messageType value. Allowed values are APPLICATION, WEBSERVER.", + LogReadingFailed: "Log reading failed.", + + // service deploy + CmdHelpServiceDeploy: "the service deploy command.", + CmdDescDeploy: "Deploys your application to Zerops.", + CmdDescDeployLong: "Deploys your application to Zerops. \n\n" + + "pathToFileOrDir defines a path to one or more directories and/or files relative to the working\n" + + "directory. The working directory is by default the current directory and can be changed\n" + + "using the --workingDir flag. zCLI deploys selected directories and/or files to Zerops. \n\n" + + "To build your application in Zerops, use the zcli push command instead.", + + // push + CmdHelpPush: "the service push command.", + CmdDescPush: "Builds your application in Zerops and deploys it.", + CmdDescPushLong: "Builds your application in Zerops and deploys it. \n\n" + + "The command triggers the build pipeline defined in zerops.yml. Zerops.yml must be in the working\n" + + "directory. The working directory is by default the current directory and can be changed\n" + + "using the --workingDir flag. zCLI uploads all files and subdirectories of the working\n" + + "directory to Zerops and starts the build pipeline. Files found in the .gitignore\n" + + "file will be ignored.\n\n" + + "If you just want to deploy your application to Zerops, use the zcli deploy command instead.", + PushRunning: "Push is running", + PushFinished: "Push finished", + PushFailed: "Push failed", + + // push && deploy + PushDeployCreatingPackageStart: "creating package", + PushDeployCreatingPackageDone: "package created", + PushDeployPackageSavedInto: "package file saved into: %s", + PushDeployUploadingPackageStart: "uploading package", + PushDeployUploadingPackageDone: "package uploaded", + PushDeployUploadPackageFailed: "package upload failed", + PushDeployDeployingStart: "deploying service", + PushDeployZeropsYamlEmpty: "config file zerops.yml is empty", + PushDeployZeropsYamlTooLarge: "max. size of zerops.yml is 10 KB", + PushDeployZeropsYamlFound: "File zerops.yml found. Path: %s.", + PushDeployZeropsYamlNotFound: "File zerops.yml not found. Checked paths: [%s]. \n" + + " Please, create a zerops.yml file in the root directory of your project. \n" + + " Alternatively you can use the --zeropsYaml flag to specify the path to the zerops.yml file or \n" + + " use the --workingDir flag to set the working directory to the directory where the zerops.yml file is located.", + + // service list + CmdHelpServiceList: "the service list command.", + CmdDescServiceList: "Lists all services in the project.", + + // service enable subdomain + CmdHelpServiceEnableSubdomain: "the service stop command.", + CmdDescServiceEnableSubdomain: "Enables access through Zerops subdomain.", + ServiceEnablingSubdomain: "enabling subdomain access", + ServiceEnableSubdomainFailed: "subdomain access enabling failed", + ServiceEnabledSubdomain: "subdomain access enabled", + + // status show debug logs + CmdHelpStatusShowDebugLogs: "the status show debug logs command.", + CmdDescStatusShowDebugLogs: "Shows zCLI debug logs.", + DebugLogsNotFound: "Debug logs not found", + + // version + CmdHelpVersion: "the version command.", + CmdDescVersion: "Shows the current zCLI version.", + + // vpn + CmdHelpVpn: "the vpn command.", + CmdDescVpn: "VPN commands group", + + // vpn up + CmdHelpVpnUp: "the vpn up command.", + CmdDescVpnUp: "Connects to the Zerops VPN.", + VpnUp: "VPN connected", + + VpnConfigSaved: "VPN config saved", + VpnPrivateKeyCorrupted: "VPN private key corrupted, a new one will be created", + VpnPrivateKeyCreated: "VPN private key created", + VpnDisconnectionPrompt: "VPN is active, do you want to disconnect?", + VpnDisconnectionPromptNo: "VPN is active, you can disconnect using the 'zcli vpn down' command", + VpnPingFailed: fmt.Sprintf("Wireguard adapter was created, but are not able to establish a connection,"+ + "this could indicate a problem on our side. Please contact our support team %s.", CustomerSupportLink), + + // vpn down + CmdHelpVpnDown: "the vpn down command.", + CmdDescVpnDown: "Disconnects from the Zerops VPN.", + VpnDown: "VPN disconnected", + + // vpn shared + VpnWgQuickIsNotInstalled: "wg-quick is not installed, please install it and try again", // flags description RegionFlag: "Choose one of Zerops regions. Use the \"zcli region list\" command to list all Zerops regions.", @@ -79,9 +203,6 @@ var en = map[string]string{ ProjectIdFlag: "If you have access to more than one project, you must specify the project ID for which the\ncommand is to be executed.", VpnAutoDisconnectFlag: "If set, zCLI will automatically disconnect from the VPN if it is already connected.", - // process - ProcessInvalidState: "last command has finished with error, identifier for communication with our support: %s", - // archiveClient ArchClientWorkingDirectory: "working directory: %s", ArchClientMaxOneTilde: "only one ~(tilde) is allowed", @@ -89,13 +210,6 @@ var en = map[string]string{ ArchClientPackingFile: "packing file: %s", ArchClientFileAlreadyExists: "file [%s] already exists", - // login - LoginSuccess: "You are logged as %s <%s>", - - // region - RegionNotFound: "Selected region %s not found", - RegionTableColumnName: "Name", - // import ImportYamlOk: "Yaml file was checked", ImportYamlEmpty: "Config file import yaml is empty", @@ -107,84 +221,19 @@ var en = map[string]string{ QueuedProcesses: "Queued processes: %d", CoreServices: "Core services activation started", - // project + service - ProjectDeleteConfirm: "Project %s will be deleted? \n Are you sure?", - ServiceDeleteConfirm: "Service %s will be deleted? \n Are you sure?", - ProjectDeleting: "Project is being deleted", - ProjectDeleted: "Project was deleted", - ServiceStarting: "Service is being started", - ServiceStarted: "Service was started", - ServiceStopping: "Service is being stopped", - ServiceStopped: "Service was stopped", - ServiceDeleting: "Service is being deleted", - ServiceDeleted: "Service was deleted", - ProjectImported: "project imported", - ServiceImported: "service(s) imported", - ServiceEnablingSubdomain: "enabling subdomain access", - ServiceEnabledSubdomain: "subdomain access enabled", - - // service logs - LogLimitInvalid: "Invalid --limit value. Allowed interval is <1;1000>", - LogMinSeverityInvalid: "Invalid --minimumSeverity value.", - LogMinSeverityStringLimitErr: "Allowed values are EMERGENCY, ALERT, CRITICAL, ERROR, WARNING, NOTICE, INFORMATIONAL, DEBUG.", - LogMinSeverityNumLimitErr: "Allowed interval is <0;7>.", - LogFormatInvalid: "Invalid --format value. Allowed values are FULL, SHORT, JSON, JSONSTREAM.", - LogFormatTemplateMismatch: "--formatTemplate can be used only in combination with --format=FULL.", - LogFormatStreamMismatch: "--format=JSON cannot be used in combination with --follow. Use --format=JSONSTREAM instead.", - LogFormatTemplateInvalid: "Invalid --formatTemplate content. The custom template failed with following error:", - LogFormatTemplateNoSpace: "Template items must be split by a (single) space.", - LogNoBuildFound: "No build was found for this service.", - LogBuildStatusUploading: "Service status UPLOADING, need to wait for app version data.", - LogAccessFailed: "Request for access to logs failed.", - LogMsgTypeInvalid: "Invalid --messageType value. Allowed values are APPLICATION, WEBSERVER.", - LogReadingFailed: "Log reading failed.", - - // push - PushRunning: "Push is running", - PushFinished: "Push finished", - PushFailed: "Push failed", - - // deploy - DeployHintPush: "To build your application in Zerops, use the zcli push command instead.", - BuildDeployCreatingPackageStart: "creating package", - BuildDeployCreatingPackageDone: "package created", - BuildDeployPackageSavedInto: "package file saved into: %s", - BuildDeployUploadingPackageStart: "uploading package", - BuildDeployUploadingPackageDone: "package uploaded", - BuildDeployUploadPackageFailed: "package upload failed", - BuildDeployDeployingStart: "deploying service", - BuildDeployZeropsYamlEmpty: "config file zerops.yml is empty", - BuildDeployZeropsYamlTooLarge: "max. size of zerops.yml is 10 KB", - BuildDeployZeropsYamlFound: "File zerops.yml found. Path: %s.", - BuildDeployZeropsYamlNotFound: "File zerops.yml not found. Expected path: %s.", - // status info - StatusInfoCliDataFilePath: "Zerops CLI data file path", - StatusInfoLogFilePath: "Zerops CLI log file path", - StatusInfoWgConfigFilePath: "Zerops CLI wg config file path", - StatusInfoLoggedUser: "Logged user", - StatusInfoVpnStatus: "VPN status", - - // debug logs - DebugLogsNotFound: "Debug logs not found", - - // vpn - VpnUp: "VPN connected", - VpnDown: "VPN disconnected", - VpnConfigSaved: "VPN config saved", - VpnPrivateKeyCorrupted: "VPN private key corrupted, a new one will be created", - VpnPrivateKeyCreated: "VPN private key created", - VpnWgQuickIsNotInstalled: "wg-quick is not installed, please install it and try again", - VpnDisconnectionPrompt: "VPN is active, do you want to disconnect?", - VpnDisconnectionPromptNo: "VPN is active, you can disconnect using the 'zcli vpn down' command", - VpnPingFailed: "VPN ping failed, this could indicate a problem with the VPN connection", - VpnCheckingConnection: "Checking VPN connection", + StatusInfoCliDataFilePath: "Zerops CLI data file path", + StatusInfoLogFilePath: "Zerops CLI log file path", + StatusInfoWgConfigFilePath: "Zerops CLI wg config file path", + StatusInfoLoggedUser: "Logged user", + StatusInfoVpnStatus: "VPN status", VpnCheckingConnectionIsActive: "VPN connection is active", VpnCheckingConnectionIsNotActive: "VPN connection is not active", //////////// // global // //////////// + ProcessInvalidState: "last command has finished with error, identifier for communication with our support: %s", CliTerminalModeEnvVar: "If enabled provides a rich UI to communicate with a user. Possible values: auto, enabled, disabled. Default value is auto.", CliLogFilePathEnvVar: "Path to a log file.", @@ -229,8 +278,8 @@ more info: https://docs.zerops.io/documentation/cli/authorization.html`, DestructiveOperationConfirmationFailed: "You have to confirm a destructive operation.", // errors - ErrorInvalidProjectId: "Invalid project ID [%s]", + ErrorInvalidProjectId: "Invalid project ID [%s], %s", // values: projectId, message ErrorInvalidScopedProjectId: "Invalid ID of the scoped project [%s], select a different project using `zcli scope project` command.", - ErrorInvalidServiceId: "Invalid service ID [%s]", + ErrorInvalidServiceId: "Invalid service ID [%s], %s", // values: serviceId, message ErrorServiceNotFound: "Service [%s] not found", } diff --git a/src/i18n/i18n.go b/src/i18n/i18n.go index aadad0a4..82acfe2a 100644 --- a/src/i18n/i18n.go +++ b/src/i18n/i18n.go @@ -13,63 +13,168 @@ func T(textConst string, args ...interface{}) string { return translation } +const CustomerSupportLink = "https://discord.com/channels/735781031147208777/1047090585774870528" + const ( - // help - LoginHelp = "LoginHelp" - ProjectHelp = "ProjectHelp" - ProjectListHelp = "ProjectListHelp" - ScopeHelp = "ScopeHelp" - ScopeProjectHelp = "ScopeProjectHelp" - ScopeResetHelp = "ScopeResetHelp" - ProjectDeleteHelp = "ProjectDeleteHelp" - ProjectImportHelp = "ProjectImportHelp" - ProjectServiceImportHelp = "ProjectServiceImportHelp" - ServiceHelp = "ServiceHelp" - ServiceStartHelp = "ServiceStartHelp" - ServiceStopHelp = "ServiceStopHelp" - ServiceDeleteHelp = "ServiceDeleteHelp" - ServiceLogHelp = "ServiceLogHelp" - ServiceDeployHelp = "ServiceDeployHelp" - ServiceListHelp = "ServiceListHelp" - ServicePushHelp = "ServicePushHelp" - ServiceEnableSubdomainHelp = "ServiceEnableSubdomainHelp" - StatusShowDebugLogsHelp = "StatusShowDebugLogsHelp" - VersionHelp = "VersionHelp" - VpnHelp = "VpnHelp" - VpnUpHelp = "VpnUpHelp" - VpnDownHelp = "VpnDownHelp" - - // cmd short - CmdDeployDesc = "CmdDeployDesc" - CmdPushDesc = "CmdPushDesc" - CmdLogin = "CmdLogin" - CmdStatusShowDebugLogs = "CmdStatusShowDebugLogs" - CmdVersion = "CmdVersion" - CmdProject = "CmdProject" - CmdService = "CmdService" - CmdProjectList = "CmdProjectList" - CmdScope = "CmdScope" - CmdScopeProject = "CmdScopeProject" - CmdScopeReset = "CmdScopeReset" - CmdProjectDelete = "CmdProjectDelete" - CmdProjectImport = "CmdProjectImport" - CmdServiceList = "CmdServiceList" - CmdServiceImport = "CmdServiceImport" - CmdServiceStart = "CmdServiceStart" - CmdServiceStop = "CmdServiceStop" - CmdServiceDelete = "CmdServiceDelete" - CmdServiceLog = "CmdServiceLog" - CmdServiceEnableSubdomain = "CmdServiceEnableSubdomain" - CmdVpn = "CmdVpn" - CmdVpnUp = "CmdVpnUp" - CmdVpnDown = "CmdVpnDown" - - // cmd long - CmdProjectImportLong = "CmdProjectImportLong" - DeployDescLong = "DeployDescLong" - PushDescLong = "PushDescLong" - CmdServiceLogLong = "CmdServiceLogLong" - ServiceLogAdditional = "ServiceLogAdditional" + // login + CmdHelpLogin = "CmdHelpLogin" + CmdDescLogin = "CmdDescLogin" + LoginSuccess = "LoginSuccess" + RegionNotFound = "RegionNotFound" + RegionTableColumnName = "RegionTableColumnName" + + // scope + CmdHelpScope = "CmdHelpScope" + CmdDescScope = "CmdDescScope" + + // scope project + CmdHelpScopeProject = "CmdHelpScopeProject" + CmdDescScopeProject = "CmdDescScopeProject" + + // scope reset + CmdHelpScopeReset = "CmdHelpScopeReset" + CmdDescScopeReset = "CmdDescScopeReset" + + // project + CmdHelpProject = "CmdHelpProject" + CmdDescProject = "CmdDescProject" + + // project lit + CmdHelpProjectList = "CmdHelpProjectList" + CmdDescProjectList = "CmdDescProjectList" + + // project delete + CmdHelpProjectDelete = "CmdHelpProjectDelete" + CmdDescProjectDelete = "CmdDescProjectDelete" + ProjectDeleteConfirm = "ProjectDeleteConfirm" + ServiceDeleteConfirm = "ServiceDeleteConfirm" + ProjectDeleting = "ProjectDeleting" + ProjectDeleteFailed = "ProjectDeleteFailed" + ProjectDeleted = "ProjectDeleted" + + // project import + CmdHelpProjectImport = "CmdHelpProjectImport" + CmdDescProjectImport = "CmdDescProjectImport" + CmdDescProjectImportLong = "CmdDescProjectImportLong" + ProjectImported = "ProjectImported" + + // project service import + CmdHelpProjectServiceImport = "CmdHelpProjectServiceImport" + CmdDescProjectServiceImport = "CmdDescProjectServiceImport" + ServiceImported = "ServiceImported" + + // service + CmdHelpService = "CmdHelpService" + CmdDescService = "CmdDescService" + + // service start + CmdHelpServiceStart = "CmdHelpServiceStart" + CmdDescServiceStart = "CmdDescServiceStart" + ServiceStarting = "ServiceStarting" + ServiceStartFailed = "ServiceStartFailed" + ServiceStarted = "ServiceStarted" + + // service stop + CmdHelpServiceStop = "CmdHelpServiceStop" + CmdDescServiceStop = "CmdDescServiceStop" + ServiceStopping = "ServiceStopping" + ServiceStopFailed = "ServiceStopFailed" + ServiceStopped = "ServiceStopped" + + // service delete + CmdHelpServiceDelete = "CmdHelpServiceDelete" + CmdDescServiceDelete = "CmdDescServiceDelete" + ServiceDeleting = "ServiceDeleting" + ServiceDeleteFailed = "ServiceDeleteFailed" + ServiceDeleted = "ServiceDeleted" + + // service log + CmdHelpServiceLog = "CmdHelpServiceLog" + CmdDescServiceLog = "CmdDescServiceLog" + CmdDescServiceLogLong = "CmdDescServiceLogLong" + LogLimitInvalid = "LogLimitInvalid" + LogMinSeverityInvalid = "LogMinSeverityInvalid" + LogMinSeverityStringLimitErr = "LogMinSeverityStringLimitErr" + LogMinSeverityNumLimitErr = "LogMinSeverityNumLimitErr" + LogFormatInvalid = "LogFormatInvalid" + LogFormatTemplateMismatch = "LogFormatTemplateMismatch" + LogFormatStreamMismatch = "LogFormatStreamMismatch" + LogFormatTemplateInvalid = "LogFormatTemplateInvalid" + LogFormatTemplateNoSpace = "LogFormatTemplateNoSpace" + LogNoBuildFound = "LogNoBuildFound" + LogBuildStatusUploading = "LogBuildStatusUploading" + LogAccessFailed = "LogAccessFailed" + LogMsgTypeInvalid = "LogMsgTypeInvalid" + LogReadingFailed = "LogReadingFailed" + + // service deploy + CmdHelpServiceDeploy = "CmdHelpServiceDeploy" + CmdDescDeploy = "CmdDescDeploy" + CmdDescDeployLong = "CmdDescDeployLong" + + // push + CmdHelpPush = "CmdHelpPush" + CmdDescPush = "CmdDescPush" + CmdDescPushLong = "CmdDescPushLong" + PushRunning = "PushRunning" + PushFailed = "PushFailed" + PushFinished = "PushFinished" + + // push && deploy + PushDeployCreatingPackageStart = "PushDeployCreatingPackageStart" + PushDeployCreatingPackageDone = "PushDeployCreatingPackageDone" + PushDeployPackageSavedInto = "PushDeployPackageSavedInto" + PushDeployUploadingPackageStart = "PushDeployUploadingPackageStart" + PushDeployUploadingPackageDone = "PushDeployUploadingPackageDone" + PushDeployUploadPackageFailed = "PushDeployUploadPackageFailed" + PushDeployDeployingStart = "PushDeployDeployingStart" + PushDeployZeropsYamlEmpty = "PushDeployZeropsYamlEmpty" + PushDeployZeropsYamlTooLarge = "PushDeployZeropsYamlTooLarge" + PushDeployZeropsYamlFound = "PushDeployZeropsYamlFound" + PushDeployZeropsYamlNotFound = "PushDeployZeropsYamlNotFound" + + // service list + CmdHelpServiceList = "CmdHelpServiceList" + CmdDescServiceList = "CmdDescServiceList" + + // service enable subdomain + CmdHelpServiceEnableSubdomain = "CmdHelpServiceEnableSubdomain" + CmdDescServiceEnableSubdomain = "CmdDescServiceEnableSubdomain" + ServiceEnablingSubdomain = "ServiceEnablingSubdomain" + ServiceEnableSubdomainFailed = "ServiceEnableSubdomainFailed" + ServiceEnabledSubdomain = "ServiceEnabledSubdomain" + + // status show debug logs + CmdHelpStatusShowDebugLogs = "CmdHelpStatusShowDebugLogs" + CmdDescStatusShowDebugLogs = "CmdDescStatusShowDebugLogs" + DebugLogsNotFound = "DebugLogsNotFound" + + // version + CmdHelpVersion = "CmdHelpVersion" + CmdDescVersion = "CmdDescVersion" + + // vpn + CmdHelpVpn = "CmdHelpVpn" + CmdDescVpn = "CmdDescVpn" + + // vpn up + CmdHelpVpnUp = "CmdHelpVpnUp" + CmdDescVpnUp = "CmdDescVpnUp" + VpnUp = "VpnUp" + VpnConfigSaved = "VpnConfigSaved" + VpnPrivateKeyCorrupted = "VpnPrivateKeyCorrupted" + VpnPrivateKeyCreated = "VpnPrivateKeyCreated" + VpnDisconnectionPrompt = "VpnDisconnectionPrompt" + VpnDisconnectionPromptNo = "VpnDisconnectionPromptNo" + VpnPingFailed = "VpnPingFailed" + + // vpn down + CmdHelpVpnDown = "CmdHelpVpnDown" + CmdDescVpnDown = "CmdDescVpnDown" + VpnDown = "VpnDown" + + // vpn shared + VpnWgQuickIsNotInstalled = "VpnWgQuickIsNotInstalled" // flags description RegionFlag = "RegionFlag" @@ -92,9 +197,6 @@ const ( ProjectIdFlag = "ProjectIdFlag" VpnAutoDisconnectFlag = "VpnAutoDisconnectFlag" - // process - ProcessInvalidState = "ProcessInvalidState" - // archiveClient ArchClientWorkingDirectory = "ArchClientWorkingDirectory" ArchClientMaxOneTilde = "ArchClientMaxOneTilde" @@ -102,13 +204,6 @@ const ( ArchClientPackingFile = "ArchClientPackingFile" ArchClientFileAlreadyExists = "ArchClientFileAlreadyExists" - // login - LoginSuccess = "LoginSuccess" - - // region - RegionNotFound = "RegionNotFound" - RegionTableColumnName = "RegionTableColumnName" - // import ImportYamlOk = "ImportYamlOk" ImportYamlEmpty = "ImportYamlEmpty" @@ -120,84 +215,20 @@ const ( QueuedProcesses = "QueuedProcesses" CoreServices = "CoreServices" - // project + service - ProjectDeleteConfirm = "ProjectDeleteConfirm" - ServiceDeleteConfirm = "ServiceDeleteConfirm" - ProjectDeleting = "ProjectDeleting" - ProjectDeleted = "ProjectDeleted" - ServiceStarting = "ServiceStarting" - ServiceStarted = "ServiceStarted" - ServiceStopping = "ServiceStopping" - ServiceStopped = "ServiceStopped" - ServiceEnablingSubdomain = "ServiceEnablingSubdomain" - ServiceEnabledSubdomain = "ServiceEnabledSubdomain" - ServiceDeleting = "ServiceDeleting" - ServiceDeleted = "ServiceDeleted" - ProjectImported = "ProjectImported" - ServiceImported = "ServiceImported" - - // service logs - LogLimitInvalid = "LogLimitInvalid" - LogMinSeverityInvalid = "LogMinSeverityInvalid" - LogMinSeverityStringLimitErr = "LogMinSeverityStringLimitErr" - LogMinSeverityNumLimitErr = "LogMinSeverityNumLimitErr" - LogFormatInvalid = "LogFormatInvalid" - LogFormatTemplateMismatch = "LogFormatTemplateMismatch" - LogFormatStreamMismatch = "LogFormatStreamMismatch" - LogFormatTemplateInvalid = "LogFormatTemplateInvalid" - LogFormatTemplateNoSpace = "LogFormatTemplateNoSpace" - LogNoBuildFound = "LogNoBuildFound" - LogBuildStatusUploading = "LogBuildStatusUploading" - LogAccessFailed = "LogAccessFailed" - LogMsgTypeInvalid = "LogMsgTypeInvalid" - LogReadingFailed = "LogReadingFailed" - - // push - PushRunning = "PushRunning" - PushFailed = "PushFailed" - PushFinished = "PushFinished" - - // deploy - DeployHintPush = "DeployHintPush" - BuildDeployCreatingPackageStart = "BuildDeployCreatingPackageStart" - BuildDeployCreatingPackageDone = "BuildDeployCreatingPackageDone" - BuildDeployPackageSavedInto = "BuildDeployPackageSavedInto" - BuildDeployUploadingPackageStart = "BuildDeployUploadingPackageStart" - BuildDeployUploadingPackageDone = "BuildDeployUploadingPackageDone" - BuildDeployUploadPackageFailed = "BuildDeployUploadPackageFailed" - BuildDeployDeployingStart = "BuildDeployDeployingStart" - BuildDeployZeropsYamlEmpty = "BuildDeployZeropsYamlEmpty" - BuildDeployZeropsYamlTooLarge = "BuildDeployZeropsYamlTooLarge" - BuildDeployZeropsYamlFound = "BuildDeployZeropsYamlFound" - BuildDeployZeropsYamlNotFound = "BuildDeployZeropsYamlNotFound" - // status info - StatusInfoCliDataFilePath = "StatusInfoCliDataFilePath" - StatusInfoLogFilePath = "StatusInfoLogFilePath" - StatusInfoWgConfigFilePath = "StatusInfoWgConfigFilePath" - StatusInfoLoggedUser = "StatusInfoLoggedUser" - StatusInfoVpnStatus = "StatusInfoVpnStatus" - - // debug logs - DebugLogsNotFound = "DebugLogsNotFound" - - // vpn - VpnUp = "VpnUp" - VpnDown = "VpnDown" - VpnConfigSaved = "VpnConfigSaved" - VpnPrivateKeyCorrupted = "VpnPrivateKeyCorrupted" - VpnPrivateKeyCreated = "VpnPrivateKeyCreated" - VpnWgQuickIsNotInstalled = "VpnWgQuickIsNotInstalled" - VpnDisconnectionPrompt = "VpnDisconnectionPrompt" - VpnDisconnectionPromptNo = "VpnDisconnectionPromptNo" - VpnPingFailed = "VpnPingFailed" - VpnCheckingConnection = "VpnCheckingConnection" + StatusInfoCliDataFilePath = "StatusInfoCliDataFilePath" + StatusInfoLogFilePath = "StatusInfoLogFilePath" + StatusInfoWgConfigFilePath = "StatusInfoWgConfigFilePath" + StatusInfoLoggedUser = "StatusInfoLoggedUser" + StatusInfoVpnStatus = "StatusInfoVpnStatus" VpnCheckingConnectionIsActive = "VpnCheckingConnectionIsActive" VpnCheckingConnectionIsNotActive = "VpnCheckingConnectionIsNotActive" //////////// // global // //////////// + ProcessInvalidState = "ProcessInvalidState" + CliTerminalModeEnvVar = "TerminalModeEnv" CliLogFilePathEnvVar = "CliLogFilePathEnvVar" CliDataFilePathEnvVar = "CliDataFilePathEnvVar"