Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/config/list_keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func Example_listKeysValue() {
// Valid Keys:
// - activeProfile
// - description
// - detailedExitCode
// - export.format
// - export.outputDirectory
// - export.overwrite
Expand Down
5 changes: 4 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ func NewRootCommand(version string, commit string) *cobra.Command {
// --config, -C
cmd.PersistentFlags().AddFlag(options.RootConfigOption.Flag)

// --detailed-exitcode, -D
cmd.PersistentFlags().AddFlag(options.RootDetailedExitCodeOption.Flag)

// --profile, -P
cmd.PersistentFlags().AddFlag(options.RootProfileOption.Flag)
// auto-completion
Expand Down Expand Up @@ -131,7 +134,7 @@ func checkCfgFileLocation(cfgFile string) {
if os.IsNotExist(err) {
// Only create a new configuration file if it is the default configuration file location
if cfgFile == options.RootConfigOption.DefaultValue.String() {
output.Warn(fmt.Sprintf("Ping CLI configuration file '%s' does not exist.", cfgFile), nil)
output.Message(fmt.Sprintf("Ping CLI configuration file '%s' does not exist.", cfgFile), nil)

createConfigFile(options.RootConfigOption.DefaultValue.String())
} else {
Expand Down
49 changes: 37 additions & 12 deletions cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@ package cmd_test
import (
"testing"

"github.com/pingidentity/pingcli/internal/configuration/options"
"github.com/pingidentity/pingcli/internal/customtypes"
"github.com/pingidentity/pingcli/internal/output"
"github.com/pingidentity/pingcli/internal/testing/testutils"
"github.com/pingidentity/pingcli/internal/testing/testutils_cobra"
"github.com/pingidentity/pingcli/internal/testing/testutils_viper"
)

// Test Root Command Executes without issue
Expand Down Expand Up @@ -51,31 +54,31 @@ func TestRootCmd_VersionFlag(t *testing.T) {
// Test Root Command Executes when provided the --output-format flag
func TestRootCmd_OutputFormatFlag(t *testing.T) {
for _, outputFormat := range customtypes.OutputFormatValidValues() {
err := testutils_cobra.ExecutePingcli(t, "--output-format", outputFormat)
err := testutils_cobra.ExecutePingcli(t, "--"+options.RootOutputFormatOption.CobraParamName, outputFormat)
testutils.CheckExpectedError(t, err, nil)
}
}

// Test Root Command fails when provided an invalid value for the --output-format flag
func TestRootCmd_InvalidOutputFlag(t *testing.T) {
expectedErrorPattern := `^invalid argument "invalid" for "-O, --output-format" flag: unrecognized Output Format: 'invalid'\. Must be one of: [a-z\s,]+$`
err := testutils_cobra.ExecutePingcli(t, "--output-format", "invalid")
err := testutils_cobra.ExecutePingcli(t, "--"+options.RootOutputFormatOption.CobraParamName, "invalid")
testutils.CheckExpectedError(t, err, &expectedErrorPattern)
}

// Test Root Command fails when provided no value for the --output-format flag
func TestRootCmd_NoValueOutputFlag(t *testing.T) {
expectedErrorPattern := `^flag needs an argument: --output-format$`
err := testutils_cobra.ExecutePingcli(t, "--output-format")
err := testutils_cobra.ExecutePingcli(t, "--"+options.RootOutputFormatOption.CobraParamName)
testutils.CheckExpectedError(t, err, &expectedErrorPattern)
}

// Test Root Command Executes output does not change with output-format=text vs output-format=json
func TestRootCmd_OutputFlagTextVsJSON(t *testing.T) {
textOutput, err := testutils_cobra.ExecutePingcliCaptureCobraOutput(t, "--output-format", "text")
textOutput, err := testutils_cobra.ExecutePingcliCaptureCobraOutput(t, "--"+options.RootOutputFormatOption.CobraParamName, "text")
testutils.CheckExpectedError(t, err, nil)

jsonOutput, err := testutils_cobra.ExecutePingcliCaptureCobraOutput(t, "--output-format", "json")
jsonOutput, err := testutils_cobra.ExecutePingcliCaptureCobraOutput(t, "--"+options.RootOutputFormatOption.CobraParamName, "json")
testutils.CheckExpectedError(t, err, nil)

if textOutput != jsonOutput {
Expand All @@ -85,42 +88,64 @@ func TestRootCmd_OutputFlagTextVsJSON(t *testing.T) {

// Test Root Command Executes when provided the --no-color flag
func TestRootCmd_ColorFlag(t *testing.T) {
err := testutils_cobra.ExecutePingcli(t, "--no-color")
err := testutils_cobra.ExecutePingcli(t, "--"+options.RootColorOption.CobraParamName)
testutils.CheckExpectedError(t, err, nil)

err = testutils_cobra.ExecutePingcli(t, "--no-color=false")
err = testutils_cobra.ExecutePingcli(t, "--"+options.RootColorOption.CobraParamName+"=false")
testutils.CheckExpectedError(t, err, nil)
}

// Test Root Command fails when provided an invalid value for the --no-color flag
func TestRootCmd_InvalidColorFlag(t *testing.T) {
expectedErrorPattern := `^invalid argument "invalid" for ".*" flag: strconv\.ParseBool: parsing "invalid": invalid syntax$`
err := testutils_cobra.ExecutePingcli(t, "--no-color=invalid")
err := testutils_cobra.ExecutePingcli(t, "--"+options.RootColorOption.CobraParamName+"=invalid")
testutils.CheckExpectedError(t, err, &expectedErrorPattern)
}

// Test Root Command Executes when provided the --config flag
func TestRootCmd_ConfigFlag(t *testing.T) {
err := testutils_cobra.ExecutePingcli(t, "--config", "config.yaml")
err := testutils_cobra.ExecutePingcli(t, "--"+options.RootConfigOption.CobraParamName, "config.yaml")
testutils.CheckExpectedError(t, err, nil)
}

// Test Root Command fails when provided no value for the --config flag
func TestRootCmd_NoValueConfigFlag(t *testing.T) {
expectedErrorPattern := `^flag needs an argument: --config$`
err := testutils_cobra.ExecutePingcli(t, "--config")
err := testutils_cobra.ExecutePingcli(t, "--"+options.RootConfigOption.CobraParamName)
testutils.CheckExpectedError(t, err, &expectedErrorPattern)
}

// Test Root Command Executes when provided the --profile flag
func TestRootCmd_ProfileFlag(t *testing.T) {
err := testutils_cobra.ExecutePingcli(t, "--profile", "default")
err := testutils_cobra.ExecutePingcli(t, "--"+options.RootProfileOption.CobraParamName, "default")
testutils.CheckExpectedError(t, err, nil)
}

// Test Root Command fails when provided no value for the --profile flag
func TestRootCmd_NoValueProfileFlag(t *testing.T) {
expectedErrorPattern := `^flag needs an argument: --profile$`
err := testutils_cobra.ExecutePingcli(t, "--profile")
err := testutils_cobra.ExecutePingcli(t, "--"+options.RootProfileOption.CobraParamName)
testutils.CheckExpectedError(t, err, &expectedErrorPattern)
}

// // Test Root Command Detailed Exit Code Flag
func TestRootCmd_DetailedExitCodeFlag(t *testing.T) {
err := testutils_cobra.ExecutePingcli(t, "--"+options.RootDetailedExitCodeOption.CobraParamName)
testutils.CheckExpectedError(t, err, nil)

err = testutils_cobra.ExecutePingcli(t, "-"+options.RootDetailedExitCodeOption.Flag.Shorthand)
testutils.CheckExpectedError(t, err, nil)
}

// // Test Root Command Detailed Exit Code Flag with output Warn
func TestRootCmd_DetailedExitCodeWarnLoggedFunc(t *testing.T) {
testutils_viper.InitVipers(t)
t.Setenv(options.RootDetailedExitCodeOption.EnvVar, "true")
output.Warn("test warning", nil)

warnLogged, err := output.DetailedExitCodeWarnLogged()
testutils.CheckExpectedError(t, err, nil)
if !warnLogged {
t.Errorf("Expected DetailedExitCodeWarnLogged to return true")
}
}
4 changes: 2 additions & 2 deletions internal/commands/platform/export_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,7 +398,7 @@ func createOrValidateOutputDir(outputDir string, overwriteExport bool) (resolved
l.Debug().Msgf("Validating export output directory '%s'", outputDir)
_, err = os.Stat(outputDir)
if err != nil {
output.Warn(fmt.Sprintf("Output directory does not exist. Creating the directory at filepath '%s'", outputDir), nil)
output.Message(fmt.Sprintf("Output directory does not exist. Creating the directory at filepath '%s'", outputDir), nil)

err = os.MkdirAll(outputDir, os.ModePerm)
if err != nil {
Expand Down Expand Up @@ -440,7 +440,7 @@ func getPingOneExportEnvID() (err error) {
return fmt.Errorf("failed to determine pingone export environment ID")
}

output.Warn("No target PingOne export environment ID specified. Defaulting export environment ID to the Worker App environment ID.", nil)
output.Message("No target PingOne export environment ID specified. Defaulting export environment ID to the Worker App environment ID.", nil)
}

return nil
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/request/request_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ func pingoneAccessToken() (accessToken string, err error) {
}
}

output.Warn("PingOne access token does not exist or is expired, requesting a new token...", nil)
output.Message("PingOne access token does not exist or is expired, requesting a new token...", nil)

// If no valid access token is available, login and get a new one
return pingoneAuth()
Expand Down
12 changes: 7 additions & 5 deletions internal/configuration/options/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func Options() []Option {
RootProfileOption,
RootColorOption,
RootConfigOption,
RootDetailedExitCodeOption,
RootOutputFormatOption,

ProfileDescriptionOption,
Expand Down Expand Up @@ -157,11 +158,12 @@ var (

// Root Command Options
var (
RootActiveProfileOption Option
RootProfileOption Option
RootColorOption Option
RootConfigOption Option
RootOutputFormatOption Option
RootActiveProfileOption Option
RootDetailedExitCodeOption Option
RootProfileOption Option
RootColorOption Option
RootConfigOption Option
RootOutputFormatOption Option
)

// 'pingcli request' command options
Expand Down
27 changes: 27 additions & 0 deletions internal/configuration/root/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func InitRootOptions() {
initProfileOption()
initColorOption()
initConfigOption()
initDetailedExitCodeOption()
initOutputFormatOption()
initUnmaskSecretValuesOption()
}
Expand Down Expand Up @@ -104,6 +105,32 @@ func initConfigOption() {
}
}

func initDetailedExitCodeOption() {
cobraParamName := "detailed-exitcode"
cobraValue := new(customtypes.Bool)
defaultValue := customtypes.Bool(false)

options.RootDetailedExitCodeOption = options.Option{
CobraParamName: cobraParamName,
CobraParamValue: cobraValue,
DefaultValue: &defaultValue,
EnvVar: "PINGCLI_DETAILED_EXITCODE",
Flag: &pflag.Flag{
Name: cobraParamName,
Shorthand: "D",
Usage: "Enable detailed exit code output. (default false)" +
"\n0 - pingcli command succeeded with no errors or warnings." +
"\n1 - pingcli command failed with errors." +
"\n2 - pingcli command succeeded with warnings.",
Value: cobraValue,
NoOptDefVal: "true", // Make this flag a boolean flag
},
Sensitive: false,
Type: options.ENUM_BOOL,
ViperKey: "detailedExitCode",
}
}

func initOutputFormatOption() {
cobraParamName := "output-format"
cobraValue := new(customtypes.OutputFormat)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ func TestCustomDomainExport(t *testing.T) {
expectedImportBlocks := []connector.ImportBlock{
{
ResourceType: "pingone_custom_domain",
ResourceName: "pioneerpalaceband.com",
ResourceID: fmt.Sprintf("%s/5eb2548d-fdb2-45f6-85bc-7adfd856cbd9", clientInfo.PingOneExportEnvironmentID),
ResourceName: "mycustomdomain.com",
ResourceID: fmt.Sprintf("%s/2f478666-ce6d-48b8-aef8-043ad3093109", clientInfo.PingOneExportEnvironmentID),
},
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@ func TestTrustedEmailDomainExport(t *testing.T) {
ResourceName: "demo.bxretail.org",
ResourceID: fmt.Sprintf("%s/49f94864-f9c7-4778-ae37-839c2c546d1c", clientInfo.PingOneExportEnvironmentID),
},
{
ResourceType: "pingone_trusted_email_domain",
ResourceName: "pioneerpalaceband.com",
ResourceID: fmt.Sprintf("%s/63d645d1-046a-4d53-a267-513cfc1d4213", clientInfo.PingOneExportEnvironmentID),
},
}

testutils.ValidateImportBlocks(t, resource, &expectedImportBlocks)
Expand Down
4 changes: 4 additions & 0 deletions internal/customtypes/export_services.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ func (es *ExportServices) SetServicesByServiceGroup(serviceGroup *ExportServiceG
return fmt.Errorf("failed to set ExportServices value: %s. ExportServices is nil", serviceGroup)
}

if serviceGroup.String() == "" {
return nil
}

switch {
case strings.EqualFold(ENUM_EXPORT_SERVICE_GROUP_PINGONE, serviceGroup.String()):
return es.Set(strings.Join(ExportServicesPingOneValidValues(), ","))
Expand Down
26 changes: 20 additions & 6 deletions internal/output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ import (
)

var (
boldRed = color.New(color.FgRed).Add(color.Bold).SprintfFunc()
cyan = color.New(color.FgCyan).SprintfFunc()
green = color.New(color.FgGreen).SprintfFunc()
red = color.New(color.FgRed).SprintfFunc()
white = color.New(color.FgWhite).SprintfFunc()
yellow = color.New(color.FgYellow).SprintfFunc()
boldRed = color.New(color.FgRed).Add(color.Bold).SprintfFunc()
cyan = color.New(color.FgCyan).SprintfFunc()
green = color.New(color.FgGreen).SprintfFunc()
red = color.New(color.FgRed).SprintfFunc()
white = color.New(color.FgWhite).SprintfFunc()
yellow = color.New(color.FgYellow).SprintfFunc()
detailedExitCodeWarnLogged = false
)

// Set the faith color option based on user configuration
Expand Down Expand Up @@ -56,10 +57,23 @@ func Success(message string, fields map[string]interface{}) {
// This function outputs yellow text to inform the user of a warning
func Warn(message string, fields map[string]interface{}) {
l := logger.Get()
detailedExitCodeWarnLogged = true

print(fmt.Sprintf("WARNING: %s", message), fields, yellow, l.Warn)
}

func DetailedExitCodeWarnLogged() (bool, error) {
detailedExitCodeEnabled, err := profiles.GetOptionValue(options.RootDetailedExitCodeOption)
if err != nil {
return false, err
}

if detailedExitCodeEnabled == "true" {
return detailedExitCodeWarnLogged, nil
}
return false, nil
}

// This functions is used to inform the user their configuration
// or input to pingcli has caused an error.
func UserError(message string, fields map[string]interface{}) {
Expand Down
11 changes: 11 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,15 @@ func main() {
output.UserError(fmt.Sprintf("Failed to execute pingcli: %v", err), nil)
os.Exit(1)
}

detailedExitCodeWarnLogged, err := output.DetailedExitCodeWarnLogged()
if err != nil {
output.UserError(fmt.Sprintf("Failed to execute pingcli: %v", err), nil)
os.Exit(1)
}
if detailedExitCodeWarnLogged {
os.Exit(2)
} else {
os.Exit(0)
}
}