From 371e400ce8d6ce7c03b934777cb6a649394b281e Mon Sep 17 00:00:00 2001 From: Cristian Lupu Date: Wed, 3 Apr 2024 11:50:17 +0300 Subject: [PATCH] Add support for template variables in template list and environment create/update --- cmd/environment/action/create.go | 145 +------- .../action/update.configuration.go | 138 +------- pkg/api/environment/action_create.go | 44 ++- .../environment/action_edit_configuration.go | 43 ++- pkg/api/environment/action_genesis_source.go | 310 ++++++++++++++++++ pkg/formatter/stylish.template.go | 2 + pkg/formatter/stylish.template.variable.go | 200 +++++++++++ 7 files changed, 607 insertions(+), 275 deletions(-) create mode 100644 pkg/api/environment/action_genesis_source.go create mode 100644 pkg/formatter/stylish.template.variable.go diff --git a/cmd/environment/action/create.go b/cmd/environment/action/create.go index 788bac7..a706513 100644 --- a/cmd/environment/action/create.go +++ b/cmd/environment/action/create.go @@ -2,58 +2,23 @@ package action import ( "errors" - "io" - "os" - "bunnyshell.com/cli/pkg/api" "bunnyshell.com/cli/pkg/api/environment" "bunnyshell.com/cli/pkg/config" "bunnyshell.com/cli/pkg/lib" "bunnyshell.com/cli/pkg/util" - "bunnyshell.com/sdk" "github.com/spf13/cobra" ) var ( - errCreateSourceNotProvided = errors.New("template id, content or git repository must be provided") errK8SIntegrationNotProvided = errors.New("kubernetes integration must be provided when deploying") ) -type CreateSource struct { - TemplateID string - - Git string - - YamlPath string - - GitRepo string - GitBranch string - GitPath string -} - -func (createSource *CreateSource) UpdateCommandFlags(command *cobra.Command) { - flags := command.Flags() - - flags.StringVar(&createSource.Git, "from-git", createSource.Git, "Use a template git repository during creation") - flags.StringVar(&createSource.TemplateID, "from-template", createSource.TemplateID, "Use a TemplateID during creation") - flags.StringVar(&createSource.YamlPath, "from-path", createSource.YamlPath, "Use a local bunnyshell.yaml during creation") - flags.StringVar(&createSource.GitRepo, "from-git-repo", createSource.GitRepo, "Git repository for the environment") - flags.StringVar(&createSource.GitBranch, "from-git-branch", createSource.GitBranch, "Git branch for the environment") - flags.StringVar(&createSource.GitPath, "from-git-path", createSource.GitPath, "Git path for the environment") - - command.MarkFlagsMutuallyExclusive("from-git", "from-template", "from-path", "from-git-repo") - command.MarkFlagsRequiredTogether("from-git-branch", "from-git-repo") - command.MarkFlagsRequiredTogether("from-git-path", "from-git-repo") - - _ = command.MarkFlagFilename("from-path", "yaml", "yml") -} - func init() { options := config.GetOptions() settings := config.GetSettings() createOptions := environment.NewCreateOptions() - createSource := CreateSource{} command := &cobra.Command{ Use: "create", @@ -61,8 +26,8 @@ func init() { ValidArgsFunction: cobra.NoFileCompletions, PreRunE: func(cmd *cobra.Command, args []string) error { - if createSource.Git == "" && createSource.TemplateID == "" && createSource.YamlPath == "" && createSource.GitRepo == "" { - return errCreateSourceNotProvided + if err := createOptions.Validate(); err != nil { + return err } if createOptions.WithDeploy && createOptions.GetKubernetesIntegration() == "" { @@ -77,19 +42,13 @@ func init() { RunE: func(cmd *cobra.Command, args []string) error { createOptions.Project = settings.Profile.Context.Project - if err := parseCreateOptions(createSource, createOptions); err != nil { + if err := createOptions.AttachGenesis(); err != nil { return lib.FormatCommandError(cmd, err) } model, err := environment.Create(createOptions) if err != nil { - var apiError api.Error - - if errors.As(err, &apiError) { - return handleCreateErrors(cmd, apiError, createOptions) - } - - return lib.FormatCommandError(cmd, err) + return createOptions.HandleError(cmd, err) } if !createOptions.WithDeploy { @@ -112,101 +71,7 @@ func init() { util.FlagRequired, )) - createOptions.UpdateFlagSet(flags) - - createSource.UpdateCommandFlags(command) + createOptions.UpdateCommandFlags(command) mainCmd.AddCommand(command) } - -func handleCreateErrors(cmd *cobra.Command, apiError api.Error, createOptions *environment.CreateOptions) error { - genesisName := getCreateGenesisName(createOptions) - - if len(apiError.Violations) == 0 { - return apiError - } - - for _, violation := range apiError.Violations { - cmd.Printf("Problem with %s: %s\n", genesisName, violation.GetMessage()) - } - - return lib.ErrGeneric -} - -func getCreateGenesisName(createOptions *environment.CreateOptions) string { - if createOptions.Genesis.FromGitSpec != nil { - return "--from-git" - } - - if createOptions.Genesis.FromTemplate != nil { - return "--from-template" - } - - if createOptions.Genesis.FromString != nil { - return "--from-path" - } - - return "arguments" -} - -func parseCreateOptions(createSource CreateSource, createOptions *environment.CreateOptions) error { - createOptions.Genesis = &sdk.EnvironmentCreateActionGenesis{} - - if createSource.Git != "" { - fromGitSpec := sdk.NewFromGitSpec() - fromGitSpec.Spec = &createSource.Git - - createOptions.Genesis.FromGitSpec = fromGitSpec - - return nil - } - - if createSource.TemplateID != "" { - fromTemplate := sdk.NewFromTemplate() - fromTemplate.Template = &createSource.TemplateID - - createOptions.Genesis.FromTemplate = fromTemplate - - return nil - } - - if createSource.YamlPath != "" { - fromString := sdk.NewFromString() - - bytes, err := readFile(createSource.YamlPath) - if err != nil { - return err - } - - content := string(bytes) - fromString.Yaml = &content - - createOptions.Genesis.FromString = fromString - - return nil - } - - if createSource.GitRepo != "" { - fromGit := sdk.NewFromGit() - fromGit.Url = &createSource.GitRepo - fromGit.Branch = &createSource.GitBranch - fromGit.YamlPath = &createSource.GitPath - - createOptions.Genesis.FromGit = fromGit - - return nil - } - - return errCreateSourceNotProvided -} - -func readFile(fileName string) ([]byte, error) { - file, err := os.Open(fileName) - if err != nil { - return nil, err - } - - defer file.Close() - - return io.ReadAll(file) -} diff --git a/cmd/environment/action/update.configuration.go b/cmd/environment/action/update.configuration.go index 37ff319..699e9ed 100644 --- a/cmd/environment/action/update.configuration.go +++ b/cmd/environment/action/update.configuration.go @@ -1,73 +1,37 @@ package action import ( - "errors" - - "bunnyshell.com/cli/pkg/api" "bunnyshell.com/cli/pkg/api/environment" "bunnyshell.com/cli/pkg/config" "bunnyshell.com/cli/pkg/lib" - "bunnyshell.com/sdk" "github.com/spf13/cobra" ) -type EditSource struct { - TemplateID string - - Git string - - YamlPath string - - GitRepo string - GitBranch string - GitPath string -} - -func (es *EditSource) UpdateCommandFlags(command *cobra.Command) { - flags := command.Flags() - - flags.StringVar(&es.Git, "from-git", es.Git, "Use a template git repository during update") - flags.StringVar(&es.TemplateID, "from-template", es.TemplateID, "Use a template ID during update") - flags.StringVar(&es.YamlPath, "from-path", es.YamlPath, "Use a local environment yaml during update") - flags.StringVar(&es.GitRepo, "from-git-repo", es.GitRepo, "Git repository for the environment template") - flags.StringVar(&es.GitBranch, "from-git-branch", es.GitBranch, "Git branch for the environment template") - flags.StringVar(&es.GitPath, "from-git-path", es.GitPath, "Git path for the environment template") - - command.MarkFlagsMutuallyExclusive("from-git", "from-template", "from-path", "from-git-repo") - command.MarkFlagsRequiredTogether("from-git-branch", "from-git-repo") - command.MarkFlagsRequiredTogether("from-git-path", "from-git-repo") - - _ = command.MarkFlagFilename("from-path", "yaml", "yml") -} - func init() { options := config.GetOptions() settings := config.GetSettings() editConfigurationOptions := environment.NewEditConfigurationOptions("") - editSource := EditSource{} command := &cobra.Command{ Use: "update-configuration", ValidArgsFunction: cobra.NoFileCompletions, + PreRunE: func(cmd *cobra.Command, args []string) error { + return editConfigurationOptions.Validate() + }, + RunE: func(cmd *cobra.Command, args []string) error { editConfigurationOptions.ID = settings.Profile.Context.Environment - if err := parseEditConfigurationOptions(editSource, editConfigurationOptions); err != nil { + if err := editConfigurationOptions.AttachGenesis(); err != nil { return lib.FormatCommandError(cmd, err) } model, err := environment.EditConfiguration(editConfigurationOptions) if err != nil { - var apiError api.Error - - if errors.As(err, &apiError) { - return handleEditErrors(cmd, apiError, editConfigurationOptions) - } - - return lib.FormatCommandError(cmd, err) + return editConfigurationOptions.HandleError(cmd, err) } if !editConfigurationOptions.WithDeploy { @@ -87,95 +51,7 @@ func init() { flags.AddFlag(idFlag) _ = command.MarkFlagRequired(idFlag.Name) - editConfigurationOptions.UpdateFlagSet(flags) - editSource.UpdateCommandFlags(command) + editConfigurationOptions.UpdateCommandFlags(command) mainCmd.AddCommand(command) } - -func handleEditErrors(cmd *cobra.Command, apiError api.Error, editOptions *environment.EditConfigurationOptions) error { - genesisName := getEditGenesisName(editOptions) - - if len(apiError.Violations) == 0 { - return apiError - } - - for _, violation := range apiError.Violations { - cmd.Printf("Problem with %s: %s\n", genesisName, violation.GetMessage()) - } - - return lib.ErrGeneric -} - -func getEditGenesisName(editOptions *environment.EditConfigurationOptions) string { - configuration := editOptions.Configuration - - if configuration.FromGitSpec != nil { - return "--from-git" - } - - if configuration.FromTemplate != nil { - return "--from-template" - } - - if configuration.FromString != nil { - return "--from-path" - } - - return "arguments" -} - -//nolint:dupl -func parseEditConfigurationOptions( - editSource EditSource, - editConfigurationOptions *environment.EditConfigurationOptions, -) error { - editConfigurationOptions.Configuration = &sdk.EnvironmentEditConfigurationConfiguration{} - - if editSource.Git != "" { - fromGitSpec := sdk.NewFromGitSpec() - fromGitSpec.Spec = &editSource.Git - - editConfigurationOptions.Configuration.FromGitSpec = fromGitSpec - - return nil - } - - if editSource.TemplateID != "" { - fromTemplate := sdk.NewFromTemplate() - fromTemplate.Template = &editSource.TemplateID - - editConfigurationOptions.Configuration.FromTemplate = fromTemplate - - return nil - } - - if editSource.YamlPath != "" { - fromString := sdk.NewFromString() - - bytes, err := readFile(editSource.YamlPath) - if err != nil { - return err - } - - content := string(bytes) - fromString.Yaml = &content - - editConfigurationOptions.Configuration.FromString = fromString - - return nil - } - - if editSource.GitRepo != "" { - fromGit := sdk.NewFromGit() - fromGit.Url = &editSource.GitRepo - fromGit.Branch = &editSource.GitBranch - fromGit.YamlPath = &editSource.GitPath - - editConfigurationOptions.Configuration.FromGit = fromGit - - return nil - } - - return errCreateSourceNotProvided -} diff --git a/pkg/api/environment/action_create.go b/pkg/api/environment/action_create.go index db113f6..c2baa92 100644 --- a/pkg/api/environment/action_create.go +++ b/pkg/api/environment/action_create.go @@ -1,13 +1,15 @@ package environment import ( + "errors" "net/http" "bunnyshell.com/cli/pkg/api" "bunnyshell.com/cli/pkg/lib" "bunnyshell.com/cli/pkg/util" "bunnyshell.com/sdk" - "github.com/spf13/pflag" + + "github.com/spf13/cobra" ) type CreateOptions struct { @@ -15,6 +17,8 @@ type CreateOptions struct { sdk.EnvironmentCreateAction + genesisSourceOptions GenesisSourceOptions + WithDeploy bool } @@ -31,10 +35,14 @@ func NewCreateOptions() *CreateOptions { DeployOptions: *NewDeployOptions(""), EnvironmentCreateAction: *environmentCreateAction, + + genesisSourceOptions: *NewGenesisSourceOptions(), } } -func (co *CreateOptions) UpdateFlagSet(flags *pflag.FlagSet) { +func (co *CreateOptions) UpdateCommandFlags(command *cobra.Command) { + flags := command.Flags() + k8sIntegration := co.KubernetesIntegration.Get() flags.StringVar(&co.Name, "name", co.Name, "Unique name for the environment") @@ -52,6 +60,38 @@ func (co *CreateOptions) UpdateFlagSet(flags *pflag.FlagSet) { flags.StringVar(ephemeralsK8sIntegration, "ephemerals-k8s", *ephemeralsK8sIntegration, "The Kubernetes integration to be used for the ephemeral environments triggered by this environment") co.DeployOptions.UpdateFlagSet(flags) + + co.genesisSourceOptions.updateCommandFlags(command, "creation") +} + +func (co *CreateOptions) Validate() error { + return co.genesisSourceOptions.validate() +} + +func (co *CreateOptions) AttachGenesis() error { + fromGit, fromGitSpec, fromString, fromTemplate, err := co.genesisSourceOptions.getGenesis() + if err != nil { + return err + } + + co.Genesis = &sdk.EnvironmentCreateActionGenesis{ + FromGit: fromGit, + FromGitSpec: fromGitSpec, + FromTemplate: fromTemplate, + FromString: fromString, + } + + return nil +} + +func (co *CreateOptions) HandleError(cmd *cobra.Command, err error) error { + var apiError api.Error + + if errors.As(err, &apiError) { + return co.genesisSourceOptions.handleError(cmd, apiError) + } + + return lib.FormatCommandError(cmd, err) } func Create(options *CreateOptions) (*sdk.EnvironmentItem, error) { diff --git a/pkg/api/environment/action_edit_configuration.go b/pkg/api/environment/action_edit_configuration.go index a9ee509..e30e258 100644 --- a/pkg/api/environment/action_edit_configuration.go +++ b/pkg/api/environment/action_edit_configuration.go @@ -1,12 +1,13 @@ package environment import ( + "errors" "net/http" "bunnyshell.com/cli/pkg/api" "bunnyshell.com/cli/pkg/lib" "bunnyshell.com/sdk" - "github.com/spf13/pflag" + "github.com/spf13/cobra" ) type EditConfigurationOptions struct { @@ -17,6 +18,8 @@ type EditConfigurationOptions struct { EditConfigurationData WithDeploy bool + + genesisSourceOptions GenesisSourceOptions } type EditConfigurationData struct { @@ -30,10 +33,14 @@ func NewEditConfigurationOptions(environment string) *EditConfigurationOptions { DeployOptions: *NewDeployOptions(environment), EnvironmentEditConfiguration: *environmentEditConfiguration, + + genesisSourceOptions: *NewGenesisSourceOptions(), } } -func (eco *EditConfigurationOptions) UpdateFlagSet(flags *pflag.FlagSet) { +func (eco *EditConfigurationOptions) UpdateCommandFlags(command *cobra.Command) { + flags := command.Flags() + data := &eco.EditConfigurationData flags.StringVar(&data.K8SIntegration, "k8s", data.K8SIntegration, "Set Kubernetes integration for the environment (if not set)") @@ -41,6 +48,38 @@ func (eco *EditConfigurationOptions) UpdateFlagSet(flags *pflag.FlagSet) { flags.BoolVar(&eco.WithDeploy, "deploy", eco.WithDeploy, "Deploy the environment after update") eco.DeployOptions.UpdateFlagSet(flags) + + eco.genesisSourceOptions.updateCommandFlags(command, "update") +} + +func (eco *EditConfigurationOptions) Validate() error { + return eco.genesisSourceOptions.validate() +} + +func (eco *EditConfigurationOptions) AttachGenesis() error { + fromGit, fromGitSpec, fromString, fromTemplate, err := eco.genesisSourceOptions.getGenesis() + if err != nil { + return err + } + + eco.Configuration = &sdk.EnvironmentEditConfigurationConfiguration{ + FromGit: fromGit, + FromGitSpec: fromGitSpec, + FromTemplate: fromTemplate, + FromString: fromString, + } + + return nil +} + +func (eco *EditConfigurationOptions) HandleError(cmd *cobra.Command, err error) error { + var apiError api.Error + + if errors.As(err, &apiError) { + return eco.genesisSourceOptions.handleError(cmd, apiError) + } + + return lib.FormatCommandError(cmd, err) } func EditConfiguration(options *EditConfigurationOptions) (*sdk.EnvironmentItem, error) { diff --git a/pkg/api/environment/action_genesis_source.go b/pkg/api/environment/action_genesis_source.go new file mode 100644 index 0000000..6840c75 --- /dev/null +++ b/pkg/api/environment/action_genesis_source.go @@ -0,0 +1,310 @@ +package environment + +import ( + "errors" + "fmt" + "io" + "os" + "strings" + + "bunnyshell.com/cli/pkg/api" + "bunnyshell.com/cli/pkg/api/template" + "bunnyshell.com/cli/pkg/lib" + "bunnyshell.com/sdk" + "github.com/spf13/cast" + "github.com/spf13/cobra" +) + +type GenesisSourceOptions struct { + TemplateID string + TemplateVariablePairs []string + + Git string + + YamlPath string + + GitRepo string + GitBranch string + GitPath string +} + +const variableSplitSize = 2 + +var ( + errGenesisSourceNotProvided = errors.New("template id, content or git repository must be provided") + errInvalidVarDefinition = errors.New("invalid template variable definition") + errUnknownVar = errors.New("unknown variable") + errUnknownEnum = errors.New("unknown enum value") +) + +func NewGenesisSourceOptions() *GenesisSourceOptions { + return &GenesisSourceOptions{} +} + +func (gs *GenesisSourceOptions) updateCommandFlags(command *cobra.Command, genesis string) { + flags := command.Flags() + + flags.StringVar(&gs.TemplateID, "from-template", gs.TemplateID, "Use a TemplateID during environment "+genesis) + flags.StringArrayVar( + &gs.TemplateVariablePairs, + "template-var", + gs.TemplateVariablePairs, + "Template variables to use during environment "+genesis, + ) + + flags.StringVar(&gs.YamlPath, "from-path", gs.YamlPath, "Use a local bunnyshell.yaml during environment "+genesis) + + flags.StringVar(&gs.Git, "from-git", gs.Git, "Use a template git repository during environment "+genesis) + + flags.StringVar(&gs.GitRepo, "from-git-repo", gs.GitRepo, "Git repository for the environment template") + flags.StringVar(&gs.GitBranch, "from-git-branch", gs.GitBranch, "Git branch for the environment template") + flags.StringVar(&gs.GitPath, "from-git-path", gs.GitPath, "Git path for the environment template") + + command.MarkFlagsMutuallyExclusive("from-git", "from-template", "from-path", "from-git-repo") + command.MarkFlagsRequiredTogether("from-git-branch", "from-git-repo") + command.MarkFlagsRequiredTogether("from-git-path", "from-git-repo") + + _ = command.MarkFlagFilename("from-path", "yaml", "yml") +} + +func (gs *GenesisSourceOptions) validate() error { + if gs.Git == "" && gs.TemplateID == "" && gs.YamlPath == "" && gs.GitRepo == "" { + return errGenesisSourceNotProvided + } + + return nil +} +func (gs *GenesisSourceOptions) handleError(cmd *cobra.Command, apiError api.Error) error { + genesisName := gs.getGenesisName() + + if len(apiError.Violations) == 0 { + return apiError + } + + for _, violation := range apiError.Violations { + cmd.Printf("Problem with %s: %s\n", genesisName, violation.GetMessage()) + } + + return lib.ErrGeneric +} + +func (gs *GenesisSourceOptions) getGenesisName() string { + if gs.Git != "" { + return "--from-git" + } + + if gs.TemplateID != "" { + return "--from-template" + } + + if gs.YamlPath != "" { + return "--from-path" + } + + return "arguments" +} + +func (gs *GenesisSourceOptions) getGenesis() (*sdk.FromGit, *sdk.FromGitSpec, *sdk.FromString, *sdk.FromTemplate, error) { + if gs.Git != "" { + return nil, gs.getFromGitSpec(), nil, nil, nil + } + + if gs.GitRepo != "" { + return gs.getFromGit(), nil, nil, nil, nil + } + + if gs.TemplateID != "" { + fromTemplate, err := gs.getFromTemplate() + if err != nil { + return nil, nil, nil, nil, err + } + + return nil, nil, nil, fromTemplate, nil + } + + if gs.YamlPath != "" { + fromString, err := gs.getFromString() + if err != nil { + return nil, nil, nil, nil, err + } + + return nil, nil, fromString, nil, nil + } + + return nil, nil, nil, nil, errGenesisSourceNotProvided +} + +func (gs *GenesisSourceOptions) getFromGit() *sdk.FromGit { + fromGit := sdk.NewFromGit() + fromGit.Url = &gs.GitRepo + fromGit.Branch = &gs.GitBranch + fromGit.YamlPath = &gs.GitPath + + return fromGit + +} + +func (gs *GenesisSourceOptions) getFromGitSpec() *sdk.FromGitSpec { + fromGitSpec := sdk.NewFromGitSpec() + fromGitSpec.Spec = &gs.Git + + return fromGitSpec +} + +func (gs *GenesisSourceOptions) getFromString() (*sdk.FromString, error) { + fromString := sdk.NewFromString() + + bytes, err := readFile(gs.YamlPath) + if err != nil { + return nil, err + } + + content := string(bytes) + fromString.Yaml = &content + + return fromString, nil +} + +func (gs *GenesisSourceOptions) getFromTemplate() (*sdk.FromTemplate, error) { + fromTemplate := sdk.NewFromTemplate() + fromTemplate.Template = &gs.TemplateID + + if len(gs.TemplateVariablePairs) > 0 { + templateVariablesSchema, schemaError := getTemplateVariableSchema(gs.TemplateID) + if schemaError != nil { + return nil, schemaError + } + + variables := map[string]sdk.FromTemplateVariablesValue{} + for _, pair := range gs.TemplateVariablePairs { + name, value, err := parseDefinition(pair, templateVariablesSchema) + if err != nil { + return nil, err + } + + variables[*name] = *value + } + + fromTemplate.SetVariables(variables) + } + + return fromTemplate, nil +} + +func getTemplateVariableSchema(templateID string) ([]sdk.TemplateItemVariablesSchemaInner, error) { + templateItem, err := template.Get(template.NewItemOptions(templateID)) + if err != nil { + return nil, err + } + + return templateItem.GetVariablesSchema(), nil +} + +func parseDefinition( + definition string, + templateVariablesSchema []sdk.TemplateItemVariablesSchemaInner, +) (*string, *sdk.FromTemplateVariablesValue, error) { + parts := strings.SplitN(definition, "=", variableSplitSize) + if len(parts) != variableSplitSize { + return nil, nil, fmt.Errorf("%w: %s", errInvalidVarDefinition, definition) + } + + name := parts[0] + value, err := getVariableValue(name, parts[1], templateVariablesSchema) + + if err != nil { + return nil, nil, fmt.Errorf("%w: %s - %w", errInvalidVarDefinition, definition, err) + } + + return &name, value, nil +} + +func getVariableValue( + name string, + stringValue string, + templateVariablesSchema []sdk.TemplateItemVariablesSchemaInner, +) (*sdk.FromTemplateVariablesValue, error) { + for _, schema := range templateVariablesSchema { + switch { + case schema.BooleanTypeItem != nil && schema.BooleanTypeItem.GetName() == name: + return stringToBoolVariableValue(stringValue), nil + case schema.IntegerTypeItem != nil && schema.IntegerTypeItem.GetName() == name: + return stringToIntVariableValue(stringValue), nil + case schema.FloatTypeItem != nil && schema.FloatTypeItem.GetName() == name: + return stringToFloatVariableValue(stringValue), nil + case schema.StringTypeItem != nil && schema.StringTypeItem.GetName() == name: + return stringVariableValue(stringValue), nil + case schema.EnumTypeItem != nil && schema.EnumTypeItem.GetName() == name: + return stringToEnumValue(stringValue, schema.EnumTypeItem.GetValues()) + } + } + + return nil, fmt.Errorf("%w %s", errUnknownVar, name) +} + +func stringToBoolVariableValue(stringValue string) *sdk.FromTemplateVariablesValue { + boolValue := cast.ToBool(stringValue) + variable := sdk.BoolAsFromTemplateVariablesValue(&boolValue) + + return &variable +} + +func stringToIntVariableValue(stringValue string) *sdk.FromTemplateVariablesValue { + intValue := cast.ToInt32(stringValue) + variable := sdk.Int32AsFromTemplateVariablesValue(&intValue) + + return &variable +} + +func stringToFloatVariableValue(stringValue string) *sdk.FromTemplateVariablesValue { + floatValue := cast.ToFloat32(stringValue) + variable := sdk.Float32AsFromTemplateVariablesValue(&floatValue) + + return &variable +} + +func stringVariableValue(stringValue string) *sdk.FromTemplateVariablesValue { + variable := sdk.StringAsFromTemplateVariablesValue(&stringValue) + + return &variable +} + +func stringToEnumValue(stringValue string, values []sdk.EnumTypeItemValuesInner) (*sdk.FromTemplateVariablesValue, error) { + boolValue := cast.ToBool(stringValue) + int32Value := cast.ToInt32(stringValue) + float32Value := cast.ToFloat32(stringValue) + + for _, item := range values { + switch { + case item.BooleanValueItem != nil && item.BooleanValueItem.GetValue() == boolValue: + variable := sdk.BoolAsFromTemplateVariablesValue(&boolValue) + + return &variable, nil + case item.IntegerValueItem != nil && item.IntegerValueItem.GetValue() == int32Value: + variable := sdk.Int32AsFromTemplateVariablesValue(&int32Value) + + return &variable, nil + case item.FloatValueItem != nil && item.FloatValueItem.GetValue() == float32Value: + variable := sdk.Float32AsFromTemplateVariablesValue(&float32Value) + + return &variable, nil + case item.StringValueItem != nil && item.StringValueItem.GetValue() == stringValue: + variable := sdk.StringAsFromTemplateVariablesValue(&stringValue) + + return &variable, nil + } + } + + return nil, fmt.Errorf("%w: %s", errUnknownEnum, stringValue) +} + +func readFile(fileName string) ([]byte, error) { + file, err := os.Open(fileName) + if err != nil { + return nil, err + } + + defer file.Close() + + return io.ReadAll(file) +} diff --git a/pkg/formatter/stylish.template.go b/pkg/formatter/stylish.template.go index f163194..f9cebc8 100644 --- a/pkg/formatter/stylish.template.go +++ b/pkg/formatter/stylish.template.go @@ -43,4 +43,6 @@ func tabulateTemplateItem(writer *tabwriter.Writer, item *sdk.TemplateItem) { fmt.Fprintf(writer, "\t %v\n", tag) } } + + tabulateTemplateVariableFromItem(writer, item) } diff --git a/pkg/formatter/stylish.template.variable.go b/pkg/formatter/stylish.template.variable.go new file mode 100644 index 0000000..384fe96 --- /dev/null +++ b/pkg/formatter/stylish.template.variable.go @@ -0,0 +1,200 @@ +package formatter + +import ( + "fmt" + "text/tabwriter" + + "bunnyshell.com/sdk" +) + +func tabulateTemplateVariableFromItem(writer *tabwriter.Writer, item *sdk.TemplateItem) { + for index, variable := range item.GetVariablesSchema() { + if index == 0 { + fmt.Fprintf(writer, "\nTemplate Variables:\n") + fmt.Fprintf(writer, "%v\t %v\t %v\t %v\n", "Name", "Default", "Type", "Description") + } + + tabulateTemplateVariable(writer, variable) + } +} + +func tabulateTemplateVariable(writer *tabwriter.Writer, item sdk.TemplateItemVariablesSchemaInner) { + switch { + case item.BooleanTypeItem != nil: + fmt.Fprintf( + writer, + "%v\t %v\t %v\t %v\n", + item.BooleanTypeItem.GetName(), + booleanTypeToString(item.BooleanTypeItem), + "Bool", + item.BooleanTypeItem.GetDescription(), + ) + case item.IntegerTypeItem != nil: + fmt.Fprintf( + writer, + "%v\t %v\t %v\t %v\n", + item.IntegerTypeItem.GetName(), + integerTypeToString(item.IntegerTypeItem), + "Int", + item.IntegerTypeItem.GetDescription(), + ) + case item.FloatTypeItem != nil: + fmt.Fprintf( + writer, + "%v\t %v\t %v\t %v\n", + item.FloatTypeItem.GetName(), + floatTypeToString(item.FloatTypeItem), + "Float", + item.FloatTypeItem.GetDescription(), + ) + case item.StringTypeItem != nil: + fmt.Fprintf( + writer, + "%v\t %v\t %v\t %v\n", + item.StringTypeItem.GetName(), + stringTypeToString(item.StringTypeItem), + "String", + item.StringTypeItem.GetDescription(), + ) + case item.EnumTypeItem != nil: + fmt.Fprintf( + writer, + "%v\t %v\t %v\t %v\n", + item.EnumTypeItem.GetName(), + enumTypeToString(item.EnumTypeItem), + "Enum", + item.EnumTypeItem.GetDescription()+" (choices: "+getEnumChoices(item.EnumTypeItem)+")", + ) + default: + fmt.Fprintf(writer, "Unknown variable type %v\n", item) + } +} + +func getEnumChoices(item *sdk.EnumTypeItem) string { + result := "" + + for index, item := range item.GetValues() { + if index > 0 { + result += ", " + } + + result += enumChoiceToString(item) + } + + return result +} + +func enumChoiceToString(item sdk.EnumTypeItemValuesInner) string { + switch { + case item.BooleanValueItem != nil: + if value, ok := item.BooleanValueItem.GetValueOk(); ok { + return fmt.Sprintf("%t", *value) + } + case item.IntegerValueItem != nil: + if value, ok := item.IntegerValueItem.GetValueOk(); ok { + return fmt.Sprintf("%d", *value) + } + case item.FloatValueItem != nil: + if value, ok := item.FloatValueItem.GetValueOk(); ok { + return fmt.Sprintf("%f", *value) + } + case item.StringValueItem != nil: + if value, ok := item.StringValueItem.GetValueOk(); ok { + return stringOrEmpty(*value) + } + } + + return "" +} + +func enumTypeToString(item *sdk.EnumTypeItem) string { + defaultValue, hasDefaultValue := item.GetDefaultValueOk() + if !hasDefaultValue { + return "" + } + + switch { + case defaultValue.BooleanValueItem != nil: + value, ok := defaultValue.BooleanValueItem.GetValueOk() + if ok { + return fmt.Sprintf("%t", *value) + } + case defaultValue.IntegerValueItem != nil: + value, ok := defaultValue.IntegerValueItem.GetValueOk() + if ok { + return fmt.Sprintf("%d", *value) + } + case defaultValue.FloatValueItem != nil: + value, ok := defaultValue.FloatValueItem.GetValueOk() + if ok { + return fmt.Sprintf("%f", *value) + } + case defaultValue.StringValueItem != nil: + value, ok := defaultValue.StringValueItem.GetValueOk() + if ok { + return stringOrEmpty(*value) + } + } + + return "" +} + +func booleanTypeToString(item *sdk.BooleanTypeItem) string { + if !item.HasDefaultValue() { + return "" + } + + defaultValue := item.GetDefaultValue() + if !defaultValue.HasValue() { + return "" + } + + return fmt.Sprintf("%t", defaultValue.GetValue()) +} + +func integerTypeToString(item *sdk.IntegerTypeItem) string { + if !item.HasDefaultValue() { + return "" + } + + defaultValue := item.GetDefaultValue() + if !defaultValue.HasValue() { + return "" + } + + return fmt.Sprintf("%d", defaultValue.GetValue()) +} + +func floatTypeToString(item *sdk.FloatTypeItem) string { + if !item.HasDefaultValue() { + return "" + } + + defaultValue := item.GetDefaultValue() + if !defaultValue.HasValue() { + return "" + } + + return fmt.Sprintf("%f", defaultValue.GetValue()) +} + +func stringTypeToString(item *sdk.StringTypeItem) string { + if !item.HasDefaultValue() { + return "" + } + + defaultValue := item.GetDefaultValue() + if !defaultValue.HasValue() { + return "" + } + + return stringOrEmpty(defaultValue.GetValue()) +} + +func stringOrEmpty(data string) string { + if data == "" { + return "''" + } + + return data +}