From c701fd195a629b6648d762542f124bb69d002e26 Mon Sep 17 00:00:00 2001 From: "jan.hajek@zerops.io" Date: Sat, 9 Mar 2024 22:22:56 +0100 Subject: [PATCH] commands refactor --- src/cmd/project.go | 2 - src/cmd/root.go | 137 ++++++++++++++++-- src/cmd/status.go | 15 -- src/cmd/statusInfo.go | 55 ------- src/cmd/version.go | 2 +- src/cmd/vpn.go | 4 +- src/cmd/{vpnDisconnect.go => vpnDown.go} | 10 +- src/cmd/{vpnConnect.go => vpnUp.go} | 10 +- ...ilderBuildCobraCmd.go => buildCobraCmd.go} | 10 +- src/cmdBuilder/cmd.go | 6 + src/cmdBuilder/cmdBuilder.go | 13 -- ...ilderCreateRunFunc.go => createRunFunc.go} | 42 +++--- ...eRunFunc_test.go => createRunFunc_test.go} | 0 ...derExecuteRootCmd.go => executeRootCmd.go} | 62 +------- src/entity/project.go | 3 +- src/entity/repository/appVersion.go | 84 +++++------ src/entity/repository/container.go | 29 ++-- src/entity/repository/project.go | 31 ++-- src/entity/repository/service.go | 20 ++- src/i18n/en.go | 18 ++- src/i18n/i18n.go | 18 ++- src/uxHelpers/project.go | 5 +- .../handler_get_projects_by_client.go | 98 ------------- .../handler_get_service_stacks_by_project.go | 105 -------------- 24 files changed, 302 insertions(+), 477 deletions(-) delete mode 100644 src/cmd/status.go delete mode 100644 src/cmd/statusInfo.go rename src/cmd/{vpnDisconnect.go => vpnDown.go} (77%) rename src/cmd/{vpnConnect.go => vpnUp.go} (95%) rename src/cmdBuilder/{cmdBuilderBuildCobraCmd.go => buildCobraCmd.go} (86%) delete mode 100644 src/cmdBuilder/cmdBuilder.go rename src/cmdBuilder/{cmdBuilderCreateRunFunc.go => createRunFunc.go} (80%) rename src/cmdBuilder/{cmdBuilderCreateRunFunc_test.go => createRunFunc_test.go} (100%) rename src/cmdBuilder/{cmdBuilderExecuteRootCmd.go => executeRootCmd.go} (52%) delete mode 100644 src/zeropsRestApiClient/handler_get_projects_by_client.go delete mode 100644 src/zeropsRestApiClient/handler_get_service_stacks_by_project.go diff --git a/src/cmd/project.go b/src/cmd/project.go index 477c8da6..6f55db1d 100644 --- a/src/cmd/project.go +++ b/src/cmd/project.go @@ -11,8 +11,6 @@ func projectCmd() *cmdBuilder.Cmd { Short(i18n.T(i18n.CmdProject)). HelpFlag(i18n.T(i18n.ProjectHelp)). AddChildrenCmd(projectListCmd()). - AddChildrenCmd(projectStartCmd()). - AddChildrenCmd(projectStopCmd()). AddChildrenCmd(projectDeleteCmd()). AddChildrenCmd(projectServiceImportCmd()). AddChildrenCmd(projectImportCmd()) diff --git a/src/cmd/root.go b/src/cmd/root.go index 5d5fac2f..b846a141 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -1,19 +1,138 @@ package cmd import ( + "context" + "fmt" + "github.com/zeropsio/zcli/src/cmdBuilder" + "github.com/zeropsio/zcli/src/constants" + repository2 "github.com/zeropsio/zcli/src/entity/repository" + "github.com/zeropsio/zcli/src/errorsx" + "github.com/zeropsio/zcli/src/i18n" + "github.com/zeropsio/zcli/src/uxBlock" + "github.com/zeropsio/zcli/src/uxBlock/styles" ) func ExecuteCmd() error { - builder := cmdBuilder.NewCmdBuilder() + return cmdBuilder.ExecuteRootCmd(rootCmd()) +} + +func rootCmd() *cmdBuilder.Cmd { + return cmdBuilder.NewCmd(). + Use("zcli"). + SetHelpTemplate(getRootTemplate()). + AddChildrenCmd(loginCmd()). + AddChildrenCmd(versionCmd()). + AddChildrenCmd(scopeCmd()). + AddChildrenCmd(projectCmd()). + AddChildrenCmd(serviceCmd()). + AddChildrenCmd(vpnCmd()). + AddChildrenCmd(statusShowDebugLogsCmd()). + AddChildrenCmd(servicePushCmd()). + GuestRunFunc(func(ctx context.Context, cmdData *cmdBuilder.GuestCmdData) error { + body := &uxBlock.TableBody{} + + body.AddStringsRow(i18n.T(i18n.StatusInfoLoggedUser), "-") + + guestInfoPart(body) + + cmdData.UxBlocks.Table(body) + + return nil + }). + LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { + body := &uxBlock.TableBody{} + + loggedUser := "" + if info, err := cmdData.RestApiClient.GetUserInfo(ctx); err != nil { + loggedUser = err.Error() + } else { + if infoOutput, err := info.Output(); err != nil { + loggedUser = err.Error() + } else { + loggedUser = fmt.Sprintf("%s <%s>", infoOutput.FullName, infoOutput.Email) + } + } + + body.AddStringsRow(i18n.T(i18n.StatusInfoLoggedUser), loggedUser) + + guestInfoPart(body) + + if cmdData.CliStorage.Data().ScopeProjectId.Filled() { + // project scope is set + projectId, _ := cmdData.CliStorage.Data().ScopeProjectId.Get() + project, err := repository2.GetProjectById(ctx, cmdData.RestApiClient, projectId) + if err != nil { + if errorsx.IsUserError(err) { + cmdData.UxBlocks.PrintWarning(styles.WarningLine(i18n.T(i18n.ScopedProjectNotFound))) + } + + return err + } + + body.AddStringsRow(i18n.T(i18n.ScopedProject), fmt.Sprintf("%s [%s]", project.Name.String(), project.ID.Native())) + } + + cmdData.UxBlocks.Table(body) + + return nil + }) +} + +func guestInfoPart(tableBody *uxBlock.TableBody) { + cliDataFilePath, err := constants.CliDataFilePath() + if err != nil { + cliDataFilePath = err.Error() + } + tableBody.AddStringsRow(i18n.T(i18n.StatusInfoCliDataFilePath), cliDataFilePath) + + logFilePath, err := constants.LogFilePath() + if err != nil { + logFilePath = err.Error() + } + tableBody.AddStringsRow(i18n.T(i18n.StatusInfoLogFilePath), logFilePath) + + wgConfigFilePath, err := constants.WgConfigFilePath() + if err != nil { + wgConfigFilePath = err.Error() + } + tableBody.AddStringsRow(i18n.T(i18n.StatusInfoWgConfigFilePath), wgConfigFilePath) +} + +func getRootTemplate() string { + return styles.CobraSectionColor().SetString("Usage:").String() + `{{if .Runnable}} + {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} + {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} + +` + styles.CobraSectionColor().SetString("Aliases:").String() + ` + {{.NameAndAliases}}{{end}}{{if .HasExample}} + +` + styles.CobraSectionColor().SetString("Examples:").String() + ` +{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}} + +` + styles.CobraSectionColor().SetString("Available Commands:").String() + `{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}} + ` + styles.CobraItemNameColor().SetString("{{rpad .Name .NamePadding }}").String() + ` {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}} + +{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}} + ` + styles.CobraItemNameColor().SetString("{{rpad .Name .NamePadding }}").String() + ` {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}} + +` + styles.CobraSectionColor().SetString("Additional Commands:").String() + `{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}} + ` + styles.CobraItemNameColor().SetString("{{rpad .Name .NamePadding }}").String() + ` {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} + +` + styles.CobraSectionColor().SetString("Flags:").String() + ` +{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} + +` + styles.CobraSectionColor().SetString("Global Flags:").String() + ` +{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} + +` + styles.CobraSectionColor().SetString("Additional help topics:").String() + `{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} + ` + styles.CobraItemNameColor().SetString("{{rpad .CommandPath .CommandPathPadding}}").String() + ` {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} - builder.AddCommand(loginCmd()) - builder.AddCommand(versionCmd()) - builder.AddCommand(scopeCmd()) - builder.AddCommand(projectCmd()) - builder.AddCommand(serviceCmd()) - builder.AddCommand(statusCmd()) - builder.AddCommand(vpnCmd()) +` + styles.CobraSectionColor().SetString("Global Env Variables:").String() + ` + ` + styles.CobraItemNameColor().SetString(constants.CliLogFilePathEnvVar).String() + ` ` + i18n.T(i18n.CliLogFilePathEnvVar) + ` + ` + styles.CobraItemNameColor().SetString(constants.CliDataFilePathEnvVar).String() + ` ` + i18n.T(i18n.CliDataFilePathEnvVar) + ` + ` + styles.CobraItemNameColor().SetString(constants.CliTerminalMode).String() + ` ` + i18n.T(i18n.CliTerminalModeEnvVar) + ` - return builder.CreateAndExecuteRootCobraCmd() +Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} +` } diff --git a/src/cmd/status.go b/src/cmd/status.go deleted file mode 100644 index d38e1ff5..00000000 --- a/src/cmd/status.go +++ /dev/null @@ -1,15 +0,0 @@ -package cmd - -import ( - "github.com/zeropsio/zcli/src/cmdBuilder" - "github.com/zeropsio/zcli/src/i18n" -) - -func statusCmd() *cmdBuilder.Cmd { - return cmdBuilder.NewCmd(). - Use("status"). - Short(i18n.T(i18n.CmdStatus)). - HelpFlag(i18n.T(i18n.StatusHelp)). - AddChildrenCmd(statusShowDebugLogsCmd()). - AddChildrenCmd(statusInfoCmd()) -} diff --git a/src/cmd/statusInfo.go b/src/cmd/statusInfo.go deleted file mode 100644 index 2e4094cb..00000000 --- a/src/cmd/statusInfo.go +++ /dev/null @@ -1,55 +0,0 @@ -package cmd - -import ( - "context" - _ "embed" - - "github.com/zeropsio/zcli/src/cmdBuilder" - "github.com/zeropsio/zcli/src/constants" - repository2 "github.com/zeropsio/zcli/src/entity/repository" - "github.com/zeropsio/zcli/src/errorsx" - "github.com/zeropsio/zcli/src/i18n" - "github.com/zeropsio/zcli/src/uxBlock" - "github.com/zeropsio/zcli/src/uxBlock/styles" -) - -func statusInfoCmd() *cmdBuilder.Cmd { - return cmdBuilder.NewCmd(). - Use("info"). - Short(i18n.T(i18n.CmdStatusInfo)). - HelpFlag(i18n.T(i18n.StatusInfoHelp)). - LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { - body := &uxBlock.TableBody{} - - cliDataFilePath, err := constants.CliDataFilePath() - if err != nil { - cliDataFilePath = err.Error() - } - body.AddStringsRow(i18n.T(i18n.StatusInfoCliDataFilePath), cliDataFilePath) - - logFilePath, err := constants.LogFilePath() - if err != nil { - logFilePath = err.Error() - } - body.AddStringsRow(i18n.T(i18n.StatusInfoLogFilePath), logFilePath) - - if cmdData.CliStorage.Data().ScopeProjectId.Filled() { - // project scope is set - projectId, _ := cmdData.CliStorage.Data().ScopeProjectId.Get() - project, err := repository2.GetProjectById(ctx, cmdData.RestApiClient, projectId) - if err != nil { - if errorsx.IsUserError(err) { - cmdData.UxBlocks.PrintWarning(styles.WarningLine(i18n.T(i18n.ScopedProjectNotFound))) - } - - return err - } - - body.AddStringsRow(i18n.T(i18n.ScopedProject), project.Name.String()) - } - - cmdData.UxBlocks.Table(body) - - return nil - }) -} diff --git a/src/cmd/version.go b/src/cmd/version.go index b34cc389..96f44b5e 100644 --- a/src/cmd/version.go +++ b/src/cmd/version.go @@ -16,7 +16,7 @@ func versionCmd() *cmdBuilder.Cmd { Use("version"). Short(i18n.T(i18n.CmdVersion)). HelpFlag(i18n.T(i18n.VersionHelp)). - LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { + 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) return nil diff --git a/src/cmd/vpn.go b/src/cmd/vpn.go index 3f7a8bf3..c0001e4a 100644 --- a/src/cmd/vpn.go +++ b/src/cmd/vpn.go @@ -10,6 +10,6 @@ func vpnCmd() *cmdBuilder.Cmd { Use("vpn"). Short(i18n.T(i18n.CmdVpn)). HelpFlag(i18n.T(i18n.VpnHelp)). - AddChildrenCmd(vpnConnectCmd()). - AddChildrenCmd(vpnDisconnectCmd()) + AddChildrenCmd(vpnUpCmd()). + AddChildrenCmd(vpnDownCmd()) } diff --git a/src/cmd/vpnDisconnect.go b/src/cmd/vpnDown.go similarity index 77% rename from src/cmd/vpnDisconnect.go rename to src/cmd/vpnDown.go index bf3ac775..cce1db00 100644 --- a/src/cmd/vpnDisconnect.go +++ b/src/cmd/vpnDown.go @@ -10,11 +10,11 @@ import ( "github.com/zeropsio/zcli/src/uxBlock/styles" ) -func vpnDisconnectCmd() *cmdBuilder.Cmd { +func vpnDownCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). - Use("disconnect"). - Short(i18n.T(i18n.CmdVpnDisconnect)). - HelpFlag(i18n.T(i18n.VpnDisconnectHelp)). + Use("down"). + Short(i18n.T(i18n.CmdVpnDown)). + HelpFlag(i18n.T(i18n.VpnDownHelp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { uxBlocks := cmdData.UxBlocks @@ -28,7 +28,7 @@ func vpnDisconnectCmd() *cmdBuilder.Cmd { return err } - uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.VpnDisconnected))) + uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.VpnDown))) return nil }) diff --git a/src/cmd/vpnConnect.go b/src/cmd/vpnUp.go similarity index 95% rename from src/cmd/vpnConnect.go rename to src/cmd/vpnUp.go index 578e1c5d..7cd1fe00 100644 --- a/src/cmd/vpnConnect.go +++ b/src/cmd/vpnUp.go @@ -23,13 +23,13 @@ import ( "github.com/zeropsio/zcli/src/uxBlock/styles" ) -func vpnConnectCmd() *cmdBuilder.Cmd { +func vpnUpCmd() *cmdBuilder.Cmd { return cmdBuilder.NewCmd(). - Use("connect"). - Short(i18n.T(i18n.CmdVpnConnect)). + Use("up"). + Short(i18n.T(i18n.CmdVpnUp)). ScopeLevel(scope.Project). Arg(scope.ProjectArgName, cmdBuilder.OptionalArg()). - HelpFlag(i18n.T(i18n.VpnConnectHelp)). + HelpFlag(i18n.T(i18n.VpnUpHelp)). LoggedUserRunFunc(func(ctx context.Context, cmdData *cmdBuilder.LoggedUserCmdData) error { uxBlocks := cmdData.UxBlocks @@ -115,7 +115,7 @@ func vpnConnectCmd() *cmdBuilder.Cmd { // TODO - janhajek ping {{.Ipv4NetworkGateway}} - uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.VpnConnected))) + uxBlocks.PrintInfo(styles.InfoLine(i18n.T(i18n.VpnUp))) return nil }) diff --git a/src/cmdBuilder/cmdBuilderBuildCobraCmd.go b/src/cmdBuilder/buildCobraCmd.go similarity index 86% rename from src/cmdBuilder/cmdBuilderBuildCobraCmd.go rename to src/cmdBuilder/buildCobraCmd.go index 01b5d2ac..6bf44744 100644 --- a/src/cmdBuilder/cmdBuilderBuildCobraCmd.go +++ b/src/cmdBuilder/buildCobraCmd.go @@ -10,7 +10,7 @@ import ( "github.com/zeropsio/zcli/src/uxBlock" ) -func (b *CmdBuilder) buildCobraCmd( +func buildCobraCmd( cmd *Cmd, flagParams *flagParams.Handler, uxBlocks uxBlock.UxBlocks, @@ -21,6 +21,10 @@ func (b *CmdBuilder) buildCobraCmd( SilenceUsage: cmd.silenceUsage, } + if cmd.helpTemplate != "" { + cobraCmd.SetHelpTemplate(cmd.helpTemplate) + } + argNames := make([]string, len(cmd.args)) for i, arg := range cmd.args { argName := arg.name @@ -59,11 +63,11 @@ func (b *CmdBuilder) buildCobraCmd( } if cmd.guestRunFunc != nil || cmd.loggedUserRunFunc != nil { - cobraCmd.RunE = b.createCmdRunFunc(cmd, flagParams, uxBlocks, cliStorage) + cobraCmd.RunE = createCmdRunFunc(cmd, flagParams, uxBlocks, cliStorage) } for _, childrenCmd := range cmd.childrenCmds { - cobraChildrenCmd, err := b.buildCobraCmd(childrenCmd, flagParams, uxBlocks, cliStorage) + cobraChildrenCmd, err := buildCobraCmd(childrenCmd, flagParams, uxBlocks, cliStorage) if err != nil { return nil, err } diff --git a/src/cmdBuilder/cmd.go b/src/cmdBuilder/cmd.go index fab3fd30..6f8742ac 100644 --- a/src/cmdBuilder/cmd.go +++ b/src/cmdBuilder/cmd.go @@ -17,6 +17,7 @@ type Cmd struct { use string short string long string + helpTemplate string loggedUserRunFunc loggedUserRunFunc guestRunFunc guestRunFunc silenceUsage bool @@ -59,6 +60,11 @@ func (cmd *Cmd) Use(use string) *Cmd { return cmd } +func (cmd *Cmd) SetHelpTemplate(template string) *Cmd { + cmd.helpTemplate = template + return cmd +} + func (cmd *Cmd) Short(short string) *Cmd { cmd.short = short return cmd diff --git a/src/cmdBuilder/cmdBuilder.go b/src/cmdBuilder/cmdBuilder.go deleted file mode 100644 index 4d5cfc69..00000000 --- a/src/cmdBuilder/cmdBuilder.go +++ /dev/null @@ -1,13 +0,0 @@ -package cmdBuilder - -type CmdBuilder struct { - commands []*Cmd -} - -func NewCmdBuilder() *CmdBuilder { - return &CmdBuilder{} -} - -func (b *CmdBuilder) AddCommand(cmd *Cmd) { - b.commands = append(b.commands, cmd) -} diff --git a/src/cmdBuilder/cmdBuilderCreateRunFunc.go b/src/cmdBuilder/createRunFunc.go similarity index 80% rename from src/cmdBuilder/cmdBuilderCreateRunFunc.go rename to src/cmdBuilder/createRunFunc.go index 97c43646..0d053d1b 100644 --- a/src/cmdBuilder/cmdBuilderCreateRunFunc.go +++ b/src/cmdBuilder/createRunFunc.go @@ -63,7 +63,7 @@ type LoggedUserCmdData struct { VpnKeys map[uuid.ProjectId]entity.VpnKey } -func (b *CmdBuilder) createCmdRunFunc( +func createCmdRunFunc( cmd *Cmd, flagParams *flagParams.Handler, uxBlocks uxBlock.UxBlocks, @@ -88,31 +88,35 @@ func (b *CmdBuilder) createCmdRunFunc( Params: newCmdParamReader(cobraCmd, flagParams), } - if cmd.loggedUserRunFunc != nil { - storedData := cliStorage.Data() + storedData := cliStorage.Data() - token := storedData.Token - if token == "" { - return errors.New(i18n.T(i18n.UnauthenticatedUser)) + token := storedData.Token + if token == "" { + if cmd.guestRunFunc != nil { + return cmd.guestRunFunc(ctx, guestCmdData) } + return errors.New(i18n.T(i18n.UnauthenticatedUser)) + } - cmdData := &LoggedUserCmdData{ - GuestCmdData: guestCmdData, - VpnKeys: storedData.VpnKeys, - } + // user is logged in but there is only the guest run func + if cmd.loggedUserRunFunc == nil { + return cmd.guestRunFunc(ctx, guestCmdData) + } - cmdData.RestApiClient = zeropsRestApiClient.NewAuthorizedClient(token, "https://"+storedData.RegionData.Address) + cmdData := &LoggedUserCmdData{ + GuestCmdData: guestCmdData, + VpnKeys: storedData.VpnKeys, + } + + cmdData.RestApiClient = zeropsRestApiClient.NewAuthorizedClient(token, "https://"+storedData.RegionData.Address) - for _, dep := range getScopeListFromRoot(cmd.scopeLevel) { - err := dep.LoadSelectedScope(ctx, cmd, cmdData) - if err != nil { - return err - } + for _, dep := range getScopeListFromRoot(cmd.scopeLevel) { + err := dep.LoadSelectedScope(ctx, cmd, cmdData) + if err != nil { + return err } - return cmd.loggedUserRunFunc(ctx, cmdData) } - - return cmd.guestRunFunc(ctx, guestCmdData) + return cmd.loggedUserRunFunc(ctx, cmdData) } } diff --git a/src/cmdBuilder/cmdBuilderCreateRunFunc_test.go b/src/cmdBuilder/createRunFunc_test.go similarity index 100% rename from src/cmdBuilder/cmdBuilderCreateRunFunc_test.go rename to src/cmdBuilder/createRunFunc_test.go diff --git a/src/cmdBuilder/cmdBuilderExecuteRootCmd.go b/src/cmdBuilder/executeRootCmd.go similarity index 52% rename from src/cmdBuilder/cmdBuilderExecuteRootCmd.go rename to src/cmdBuilder/executeRootCmd.go index 4ae16837..04373c44 100644 --- a/src/cmdBuilder/cmdBuilderExecuteRootCmd.go +++ b/src/cmdBuilder/executeRootCmd.go @@ -9,7 +9,6 @@ import ( "github.com/mattn/go-isatty" "github.com/pkg/errors" - "github.com/spf13/cobra" "github.com/zeropsio/zcli/src/cliStorage" "github.com/zeropsio/zcli/src/constants" "github.com/zeropsio/zcli/src/errorsx" @@ -25,7 +24,7 @@ import ( "gopkg.in/yaml.v3" ) -func (b *CmdBuilder) CreateAndExecuteRootCobraCmd() (err error) { +func ExecuteRootCmd(rootCmd *Cmd) (err error) { ctx, cancel := context.WithCancel(context.Background()) regSignals(cancel) ctx = support.Context(ctx) @@ -54,17 +53,12 @@ func (b *CmdBuilder) CreateAndExecuteRootCobraCmd() (err error) { flagParams := flagParams.New() - rootCmd := createRootCommand() - - for _, cmd := range b.commands { - cobraCmd, err := b.buildCobraCmd(cmd, flagParams, uxBlocks, cliStorage) - if err != nil { - return err - } - rootCmd.AddCommand(cobraCmd) + cobraCmd, err := buildCobraCmd(rootCmd, flagParams, uxBlocks, cliStorage) + if err != nil { + return err } - err = rootCmd.ExecuteContext(ctx) + err = cobraCmd.ExecuteContext(ctx) if err != nil { printError(err, uxBlocks) } @@ -72,52 +66,6 @@ func (b *CmdBuilder) CreateAndExecuteRootCobraCmd() (err error) { return nil } -func createRootCommand() *cobra.Command { - rootCmd := &cobra.Command{ - Use: "zcli", - CompletionOptions: cobra.CompletionOptions{HiddenDefaultCmd: true}, - SilenceErrors: true, - } - - rootCmd.SetHelpTemplate(`` + styles.CobraSectionColor().SetString("Usage:").String() + `{{if .Runnable}} - {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} - {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} - -` + styles.CobraSectionColor().SetString("Aliases:").String() + ` - {{.NameAndAliases}}{{end}}{{if .HasExample}} - -` + styles.CobraSectionColor().SetString("Examples:").String() + ` -{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}} - -` + styles.CobraSectionColor().SetString("Available Commands:").String() + `{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}} - ` + styles.CobraItemNameColor().SetString("{{rpad .Name .NamePadding }}").String() + ` {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}} - -{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}} - ` + styles.CobraItemNameColor().SetString("{{rpad .Name .NamePadding }}").String() + ` {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}} - -` + styles.CobraSectionColor().SetString("Additional Commands:").String() + `{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}} - ` + styles.CobraItemNameColor().SetString("{{rpad .Name .NamePadding }}").String() + ` {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} - -` + styles.CobraSectionColor().SetString("Flags:").String() + ` -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -` + styles.CobraSectionColor().SetString("Global Flags:").String() + ` -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} - -` + styles.CobraSectionColor().SetString("Additional help topics:").String() + `{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} - ` + styles.CobraItemNameColor().SetString("{{rpad .CommandPath .CommandPathPadding}}").String() + ` {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} - -` + styles.CobraSectionColor().SetString("Global Env Variables:").String() + ` - ` + styles.CobraItemNameColor().SetString(constants.CliLogFilePathEnvVar).String() + ` ` + i18n.T(i18n.CliLogFilePathEnvVar) + ` - ` + styles.CobraItemNameColor().SetString(constants.CliDataFilePathEnvVar).String() + ` ` + i18n.T(i18n.CliDataFilePathEnvVar) + ` - ` + styles.CobraItemNameColor().SetString(constants.CliTerminalMode).String() + ` ` + i18n.T(i18n.CliTerminalModeEnvVar) + ` - -Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} -`) - - return rootCmd -} - func printError(err error, uxBlocks uxBlock.UxBlocks) { uxBlocks.LogDebug(fmt.Sprintf("error: %+v", err)) diff --git a/src/entity/project.go b/src/entity/project.go index f5d39c37..ee9f28fe 100644 --- a/src/entity/project.go +++ b/src/entity/project.go @@ -9,7 +9,8 @@ import ( type Project struct { ID uuid.ProjectId Name types.String - ClientId uuid.ClientId + OrgId uuid.ClientId + OrgName types.String Description types.Text Status enum.ProjectStatusEnum } diff --git a/src/entity/repository/appVersion.go b/src/entity/repository/appVersion.go index 36e98565..0726e784 100644 --- a/src/entity/repository/appVersion.go +++ b/src/entity/repository/appVersion.go @@ -15,24 +15,25 @@ func GetAllAppVersionByService( restApiClient *zeropsRestApiClient.Handler, service entity.Service, ) ([]entity.AppVersion, error) { - var searchData []body.EsSearchItem - searchData = append(searchData, body.EsSearchItem{ - Name: "clientId", - Operator: "eq", - Value: service.ClientId.TypedString(), - }, body.EsSearchItem{ - Name: "serviceStackId", - Operator: "eq", - Value: service.ID.TypedString(), - }, body.EsSearchItem{ - Name: "build.serviceStackId", - Operator: "ne", - Value: "", - }) + esFilter := body.EsFilter{ + Search: []body.EsSearchItem{ + { + Name: "clientId", + Operator: "eq", + Value: service.ClientId.TypedString(), + }, { + Name: "serviceStackId", + Operator: "eq", + Value: service.ID.TypedString(), + }, { + Name: "build.serviceStackId", + Operator: "ne", + Value: "", + }, + }, + } - response, err := restApiClient.PostAppVersionSearch(ctx, body.EsFilter{ - Search: searchData, - }) + response, err := restApiClient.PostAppVersionSearch(ctx, esFilter) if err != nil { return nil, err } @@ -55,31 +56,32 @@ func GetLatestAppVersionByService( restApiClient *zeropsRestApiClient.Handler, service entity.Service, ) ([]entity.AppVersion, error) { - var searchData []body.EsSearchItem - searchData = append(searchData, body.EsSearchItem{ - Name: "clientId", - Operator: "eq", - Value: service.ClientId.TypedString(), - }, body.EsSearchItem{ - Name: "serviceStackId", - Operator: "eq", - Value: service.ID.TypedString(), - }, body.EsSearchItem{ - Name: "build.serviceStackId", - Operator: "ne", - Value: "", - }) - var sortData []body.EsSortItem - sortData = append(sortData, body.EsSortItem{ - Name: "sequence", - Ascending: types.NewBoolNull(false), - }) + esFilter := body.EsFilter{ + Search: []body.EsSearchItem{ + { + Name: "clientId", + Operator: "eq", + Value: service.ClientId.TypedString(), + }, { + Name: "serviceStackId", + Operator: "eq", + Value: service.ID.TypedString(), + }, { + Name: "build.serviceStackId", + Operator: "ne", + Value: "", + }, + }, + Sort: []body.EsSortItem{ + { + Name: "sequence", + Ascending: types.NewBoolNull(false), + }, + }, + Limit: types.NewIntNull(1), + } - response, err := restApiClient.PostAppVersionSearch(ctx, body.EsFilter{ - Search: searchData, - Sort: sortData, - Limit: types.NewIntNull(1), - }) + response, err := restApiClient.PostAppVersionSearch(ctx, esFilter) if err != nil { return nil, err } diff --git a/src/entity/repository/container.go b/src/entity/repository/container.go index aa957456..773b823f 100644 --- a/src/entity/repository/container.go +++ b/src/entity/repository/container.go @@ -14,20 +14,21 @@ func GetAllContainers( restApiClient *zeropsRestApiClient.Handler, service entity.Service, ) ([]entity.Container, error) { - var searchData []body.EsSearchItem - searchData = append(searchData, body.EsSearchItem{ - Name: "clientId", - Operator: "eq", - Value: service.ClientId.TypedString(), - }, body.EsSearchItem{ - Name: "serviceStackId", - Operator: "eq", - Value: service.ID.TypedString(), - }) - - response, err := restApiClient.PostContainerSearch(ctx, body.EsFilter{ - Search: searchData, - }) + esFilter := body.EsFilter{ + Search: []body.EsSearchItem{ + { + Name: "clientId", + Operator: "eq", + Value: service.ClientId.TypedString(), + }, { + Name: "serviceStackId", + Operator: "eq", + Value: service.ID.TypedString(), + }, + }, + } + + response, err := restApiClient.PostContainerSearch(ctx, esFilter) if err != nil { return nil, err } diff --git a/src/entity/repository/project.go b/src/entity/repository/project.go index a20563cd..2a3ac4d7 100644 --- a/src/entity/repository/project.go +++ b/src/entity/repository/project.go @@ -5,6 +5,7 @@ import ( "github.com/zeropsio/zcli/src/entity" "github.com/zeropsio/zcli/src/zeropsRestApiClient" + "github.com/zeropsio/zerops-go/dto/input/body" "github.com/zeropsio/zerops-go/dto/input/path" "github.com/zeropsio/zerops-go/dto/output" "github.com/zeropsio/zerops-go/types/uuid" @@ -33,19 +34,24 @@ func GetAllProjects( ctx context.Context, restApiClient *zeropsRestApiClient.Handler, ) ([]entity.Project, error) { - info, err := restApiClient.GetUserInfo(ctx) - if err != nil { - return nil, err - } - - output, err := info.Output() + orgs, err := GetAllOrgs(ctx, restApiClient) if err != nil { return nil, err } var projects []entity.Project - for _, clientUser := range output.ClientUserList { - response, err := restApiClient.GetProjectsByClient(ctx, clientUser.ClientId) + for _, org := range orgs { + esFilter := body.EsFilter{ + Search: []body.EsSearchItem{ + { + Name: "clientId", + Operator: "eq", + Value: org.ID.TypedString(), + }, + }, + } + + response, err := restApiClient.PostProjectSearch(ctx, esFilter) if err != nil { return nil, err } @@ -55,20 +61,21 @@ func GetAllProjects( } for _, project := range projectsResponse.Items { - projects = append(projects, projectFromEsSearch(project)) + projects = append(projects, projectFromEsSearch(org, project)) } } return projects, nil } -func projectFromEsSearch(esProject zeropsRestApiClient.EsProject) entity.Project { +func projectFromEsSearch(org entity.Org, esProject output.EsProject) entity.Project { description, _ := esProject.Description.Get() return entity.Project{ ID: esProject.Id, Name: esProject.Name, - ClientId: esProject.ClientId, + OrgId: org.ID, + OrgName: org.Name, Description: description, Status: esProject.Status, } @@ -80,7 +87,7 @@ func projectFromApiOutput(project output.Project) entity.Project { return entity.Project{ ID: project.Id, Name: project.Name, - ClientId: project.ClientId, + OrgId: project.ClientId, Description: description, Status: project.Status, } diff --git a/src/entity/repository/service.go b/src/entity/repository/service.go index 445b9235..8864c6a1 100644 --- a/src/entity/repository/service.go +++ b/src/entity/repository/service.go @@ -6,6 +6,7 @@ import ( "github.com/zeropsio/zcli/src/entity" "github.com/zeropsio/zcli/src/errorsx" "github.com/zeropsio/zcli/src/zeropsRestApiClient" + "github.com/zeropsio/zerops-go/dto/input/body" "github.com/zeropsio/zerops-go/dto/input/path" "github.com/zeropsio/zerops-go/dto/output" "github.com/zeropsio/zerops-go/types" @@ -78,7 +79,22 @@ func GetNonSystemServicesByProject( restApiClient *zeropsRestApiClient.Handler, project entity.Project, ) ([]entity.Service, error) { - servicesResponse, err := restApiClient.GetServiceStackByProject(ctx, project.ID, project.ClientId) + esFilter := body.EsFilter{ + Search: []body.EsSearchItem{ + { + Name: "projectId", + Operator: "eq", + Value: project.ID.TypedString(), + }, + { + Name: "clientId", + Operator: "eq", + Value: project.OrgId.TypedString(), + }, + }, + } + + servicesResponse, err := restApiClient.PostServiceStackSearch(ctx, esFilter) if err != nil { return nil, err } @@ -98,7 +114,7 @@ func GetNonSystemServicesByProject( return services, nil } -func serviceFromEsSearch(esServiceStack zeropsRestApiClient.EsServiceStack) entity.Service { +func serviceFromEsSearch(esServiceStack output.EsServiceStack) entity.Service { return entity.Service{ ID: esServiceStack.Id, ClientId: esServiceStack.ClientId, diff --git a/src/i18n/en.go b/src/i18n/en.go index f9f42cb1..e2790bd8 100644 --- a/src/i18n/en.go +++ b/src/i18n/en.go @@ -36,8 +36,8 @@ var en = map[string]string{ BucketCreateHelp: "the bucket create command.", BucketDeleteHelp: "the bucket delete command.", VpnHelp: "the vpn command.", - VpnConnectHelp: "the vpn connect command.", - VpnDisconnectHelp: "the vpn disconnect command.", + VpnUpHelp: "the vpn up command.", + VpnDownHelp: "the vpn down command.", // cmd short CmdDeployDesc: "Deploys your application to Zerops.", @@ -72,8 +72,8 @@ var en = map[string]string{ CmdBucketCreate: "Creates a bucket in an existing object storage.", CmdBucketDelete: "Deletes a bucket from an existing object storage.", CmdVpn: "VPN commands group", - CmdVpnConnect: "Connects to the Zerops VPN.", - CmdVpnDisconnect: "Disconnects from the Zerops VPN.", + 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.", @@ -214,15 +214,17 @@ var en = map[string]string{ BucketS3BucketAlreadyExists: "The bucket name already exists under a different object storage user. Set a different bucket name.", // status info - StatusInfoCliDataFilePath: "Zerops CLI data file path", - StatusInfoLogFilePath: "Zerops CLI log file path", + StatusInfoCliDataFilePath: "Zerops CLI data file path", + StatusInfoLogFilePath: "Zerops CLI log file path", + StatusInfoWgConfigFilePath: "Zerops CLI wg config file path", + StatusInfoLoggedUser: "Logged user", // debug logs DebugLogsNotFound: "Debug logs not found", // vpn - VpnConnected: "VPN connected", - VpnDisconnected: "VPN disconnected", + 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", diff --git a/src/i18n/i18n.go b/src/i18n/i18n.go index c714534a..7b2da6a6 100644 --- a/src/i18n/i18n.go +++ b/src/i18n/i18n.go @@ -49,8 +49,8 @@ const ( BucketCreateHelp = "BucketCreateHelp" BucketDeleteHelp = "BucketDeleteHelp" VpnHelp = "VpnHelp" - VpnConnectHelp = "VpnConnectHelp" - VpnDisconnectHelp = "VpnDisconnectHelp" + VpnUpHelp = "VpnUpHelp" + VpnDownHelp = "VpnDownHelp" // cmd short CmdDeployDesc = "CmdDeployDesc" @@ -85,8 +85,8 @@ const ( CmdBucketCreate = "CmdBucketCreate" CmdBucketDelete = "CmdBucketDelete" CmdVpn = "CmdVpn" - CmdVpnConnect = "CmdVpnConnect" - CmdVpnDisconnect = "CmdVpnDisconnect" + CmdVpnUp = "CmdVpnUp" + CmdVpnDown = "CmdVpnDown" // cmd long CmdProjectImportLong = "CmdProjectImportLong" @@ -252,15 +252,17 @@ const ( BucketS3BucketAlreadyExists = "BucketAlreadyExists" // status info - StatusInfoCliDataFilePath = "StatusInfoCliDataFilePath" - StatusInfoLogFilePath = "StatusInfoLogFilePath" + StatusInfoCliDataFilePath = "StatusInfoCliDataFilePath" + StatusInfoLogFilePath = "StatusInfoLogFilePath" + StatusInfoWgConfigFilePath = "StatusInfoWgConfigFilePath" + StatusInfoLoggedUser = "StatusInfoLoggedUser" // debug logs DebugLogsNotFound = "DebugLogsNotFound" // vpn - VpnConnected = "VpnConnected" - VpnDisconnected = "VpnDisconnected" + VpnUp = "VpnUp" + VpnDown = "VpnDown" VpnConfigSaved = "VpnConfigSaved" VpnPrivateKeyCorrupted = "VpnPrivateKeyCorrupted" VpnPrivateKeyCreated = "VpnPrivateKeyCreated" diff --git a/src/uxHelpers/project.go b/src/uxHelpers/project.go index 33ebe306..6e1ee6b7 100644 --- a/src/uxHelpers/project.go +++ b/src/uxHelpers/project.go @@ -68,7 +68,7 @@ func PrintProjectList( func createProjectTableRows(projects []entity.Project) (*uxBlock.TableRow, *uxBlock.TableBody) { // TODO - janhajek translation - header := (&uxBlock.TableRow{}).AddStringCells("ID", "Name", "Description", "Org ID", "Status") + header := (&uxBlock.TableRow{}).AddStringCells("ID", "Name", "Description", "Org Name", "Org ID", "Status") tableBody := &uxBlock.TableBody{} for _, project := range projects { @@ -76,7 +76,8 @@ func createProjectTableRows(projects []entity.Project) (*uxBlock.TableRow, *uxBl string(project.ID), project.Name.String(), project.Description.Native(), - project.ClientId.Native(), + project.OrgName.Native(), + project.OrgId.Native(), project.Status.String(), ) } diff --git a/src/zeropsRestApiClient/handler_get_projects_by_client.go b/src/zeropsRestApiClient/handler_get_projects_by_client.go deleted file mode 100644 index c548af3a..00000000 --- a/src/zeropsRestApiClient/handler_get_projects_by_client.go +++ /dev/null @@ -1,98 +0,0 @@ -// Package zeropsRestApiClient provides a client for the zerops rest api -package zeropsRestApiClient - -import ( - "context" - "encoding/json" - "net/http" - - "github.com/pkg/errors" - "github.com/zeropsio/zerops-go/apiError" - "github.com/zeropsio/zerops-go/sdkBase" - "github.com/zeropsio/zerops-go/types" - "github.com/zeropsio/zerops-go/types/enum" - "github.com/zeropsio/zerops-go/types/uuid" -) - -func (h *Handler) GetProjectsByClient( - ctx context.Context, - clientId uuid.ClientId, -) (GetProjectsByClientResponse, error) { - var response GetProjectsByClientResponse - - u := "/api/rest/public/project/search" - - filter := EsFilter{ - Search: []EsSearchItem{ - { - Name: "clientId", - Operator: "eq", - Value: clientId.Native(), - }, - }, - } - - sdkResponse := sdkBase.Post(ctx, h.env, u, filter) - - if sdkResponse.Err != nil { - return response, sdkResponse.Err - } - response.responseHeaders = sdkResponse.HttpResponse.Header - response.responseStatusCode = sdkResponse.HttpResponse.StatusCode - - decoder := json.NewDecoder(sdkResponse.ResponseData) - if sdkResponse.HttpResponse.StatusCode < http.StatusMultipleChoices { - if err := decoder.Decode(&response.success); err != nil { - return response, err - } - } else { - responseString := sdkResponse.ResponseData.String() - apiErrorResponse := struct { - Error apiError.Error `json:"error"` - }{} - err := decoder.Decode(&apiErrorResponse) - if err != nil { - return response, errors.New(sdkResponse.HttpResponse.Status + ": " + responseString) - } - apiErrorResponse.Error.HttpStatusCode = sdkResponse.HttpResponse.StatusCode - response.err = apiErrorResponse.Error - } - - return response, nil -} - -type EsFilter struct { - Search []EsSearchItem `json:"search"` -} - -type EsSearchItem struct { - Name string `json:"name"` - Operator string `json:"operator"` - Value string `json:"value"` -} - -type GetProjectsByClientResponse struct { - success EsProjectResponse - err error - responseHeaders http.Header - responseStatusCode int -} - -func (r GetProjectsByClientResponse) Output() (output EsProjectResponse, err error) { - return r.success, r.err -} - -type EsProjectResponse struct { - Limit int64 `json:"limit"` - Offset int64 `json:"offset"` - TotalHits int64 `json:"totalHits"` - Items []EsProject `json:"items"` -} - -type EsProject struct { - Id uuid.ProjectId `json:"id"` - ClientId uuid.ClientId `json:"clientId"` - Name types.String `json:"name"` - Description types.TextNull `json:"description"` - Status enum.ProjectStatusEnum `json:"status"` -} diff --git a/src/zeropsRestApiClient/handler_get_service_stacks_by_project.go b/src/zeropsRestApiClient/handler_get_service_stacks_by_project.go deleted file mode 100644 index 96d51c64..00000000 --- a/src/zeropsRestApiClient/handler_get_service_stacks_by_project.go +++ /dev/null @@ -1,105 +0,0 @@ -// Package zeropsRestApiClient provides a client for the zerops rest api -package zeropsRestApiClient - -import ( - "context" - "encoding/json" - "errors" - "net/http" - - "github.com/zeropsio/zerops-go/apiError" - "github.com/zeropsio/zerops-go/sdkBase" - "github.com/zeropsio/zerops-go/types" - "github.com/zeropsio/zerops-go/types/enum" - "github.com/zeropsio/zerops-go/types/stringId" - "github.com/zeropsio/zerops-go/types/uuid" -) - -func (h *Handler) GetServiceStackByProject( - ctx context.Context, - projectId uuid.ProjectId, - clientId uuid.ClientId, -) (GetServiceStackByProjectResponse, error) { - var response GetServiceStackByProjectResponse - - u := "/api/rest/public/service-stack/search" - - filter := EsFilter{ - Search: []EsSearchItem{ - { - Name: "projectId", - Operator: "eq", - Value: projectId.Native(), - }, - { - Name: "clientId", - Operator: "eq", - Value: clientId.Native(), - }, - }, - } - - sdkResponse := sdkBase.Post(ctx, h.env, u, filter) - - if sdkResponse.Err != nil { - return response, sdkResponse.Err - } - response.responseHeaders = sdkResponse.HttpResponse.Header - response.responseStatusCode = sdkResponse.HttpResponse.StatusCode - - decoder := json.NewDecoder(sdkResponse.ResponseData) - if sdkResponse.HttpResponse.StatusCode < http.StatusMultipleChoices { - if err := decoder.Decode(&response.success); err != nil { - return response, err - } - } else { - responseString := sdkResponse.ResponseData.String() - apiErrorResponse := struct { - Error apiError.Error `json:"error"` - }{} - err := decoder.Decode(&apiErrorResponse) - if err != nil { - return response, errors.New(sdkResponse.HttpResponse.Status + ": " + responseString) - } - apiErrorResponse.Error.HttpStatusCode = sdkResponse.HttpResponse.StatusCode - response.err = apiErrorResponse.Error - } - - return response, nil -} - -type GetServiceStackByProjectResponse struct { - success EsServiceStackResponse - err error - responseHeaders http.Header - responseStatusCode int -} - -func (r GetServiceStackByProjectResponse) Output() (output EsServiceStackResponse, err error) { - return r.success, r.err -} - -type EsServiceStackResponse struct { - Limit int `json:"limit"` - Offset int `json:"offset"` - TotalHits int `json:"totalHits"` - Items []EsServiceStack `json:"items"` -} - -type EsServiceStack struct { - Id uuid.ServiceStackId `json:"id"` - ProjectId uuid.ProjectId `json:"projectId"` - ClientId uuid.ClientId `json:"clientId"` - ServiceStackTypeId stringId.ServiceStackTypeId `json:"serviceStackTypeId"` - ServiceStackTypeVersionId stringId.ServiceStackTypeVersionId `json:"serviceStackTypeVersionId"` - Status enum.ServiceStackStatusEnum `json:"status"` - Name types.String `json:"name"` - IsSystem types.Bool `json:"isSystem"` - ServiceStackTypeInfo EsServiceStackInfoJsonObject `json:"serviceStackTypeInfo"` -} - -type EsServiceStackInfoJsonObject struct { - ServiceStackTypeName types.String `json:"serviceStackTypeName"` // serviceStackTypeName - types.String - ServiceStackTypeCategory enum.ServiceStackTypeCategoryEnum `json:"serviceStackTypeCategory"` // serviceStackTypeCategory - enum.ServiceStackTypeCategoryEnum - ServiceStackTypeVersionName types.String `json:"serviceStackTypeVersionName"` // serviceStackTypeVersionName - types.String -}