Skip to content

Commit

Permalink
WithRegionalFlags allow force custom location but warn
Browse files Browse the repository at this point in the history
  • Loading branch information
avirtopeanu-ionos committed Nov 29, 2024
1 parent 3ab8abb commit 96dbee9
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 17 deletions.
2 changes: 1 addition & 1 deletion commands/dns/dns.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ func DNSCommand() *core.Command {
cmd.AddCommand(dnssec.Root())
cmd.AddCommand(secondary_zones.Root())

return cmd
return core.WithRegionalFlags(cmd, "https://dns.%s.ionos.com", []string{"de/fra", "de/txl"})
}
78 changes: 62 additions & 16 deletions internal/core/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,21 +38,64 @@ func (c *Command) Name() string {
}
}

// WithRegionalFlags adds regional flag support to a command, allowing users to specify a location or override the server URL.
//
// # Usage
//
// To use this function, wrap the root command of your API and specify the baseURL and allowed locations.
//
// Example:
//
// ```
//
// func DNSCommand() *core.Command {
// cmd := &core.Command{
// Command: &cobra.Command{
// Use: "dns",
// Short: "The sub-commands of the 'dns' resource help automate DNS Zone and Record management",
// TraverseChildren: true,
// },
// }
//
// // Add regional flags
// return core.WithRegionalFlags(cmd, "https://dns.%s.ionos.com", []string{"de/fra", "de/txl"})
// }
//
// ```
//
// - 'baseURL': The base URL for the API, with an optional '%s' placeholder for the location (e.g., '"https://dns.%s.ionos.com"').
// - 'allowedLocations': A slice of allowed locations (e.g., '[]string{"de/fra", "de/txl"}'). These will populate the '--location' flag completion.
//
// # Notes
//
// - The '--server-url' flag allows users to override the API host URL manually.
// - The '--location' flag allows users to specify a region, which replaces the '%s' placeholder in the 'baseURL'.
// - If '--location' is used and is valid (from 'allowedLocations'), the 'baseURL' is formatted with the normalized location.
// - If '--location' is invalid or unsupported, a warning is logged, but the constructed URL is still attempted.
// - If 'allowedLocations' is empty, the function panics, as this is considered a programming error.//
// - If an unsupported location is provided, a warning is logged:
// 'WARN: <location> is an invalid location. Valid locations are: <allowedLocations>'
// - This also marks '--api-url' and '--location' flags as mutually exclusive.
// - The first location in 'allowedLocations' is used as the default URL if no location is provided.
func WithRegionalFlags(c *Command, baseURL string, allowedLocations []string) *Command {
locationsToUrl := make(map[string]string, len(allowedLocations))
for _, loc := range allowedLocations {
// de/fra -> de-fra
normalizedLoc := strings.ReplaceAll(loc, "/", "-")
locationsToUrl[normalizedLoc] = fmt.Sprintf(baseURL, normalizedLoc)
}
// Generate the default URL using the first provided location, if available
var defaultUrl string
if len(allowedLocations) > 0 {
// if baseURL does not contain a placeholder, throw a panic, as this is a programming error
if !strings.Contains(baseURL, "%s") {
panic(fmt.Errorf("baseURL %s does not contain a placeholder for location", baseURL))
}

// generate the default URL as the first provided location
defaultLocation := allowedLocations[0]
defaultUrl := fmt.Sprintf(baseURL, strings.ReplaceAll(defaultLocation, "/", "-"))
defaultLocation := allowedLocations[0]
defaultUrl = fmt.Sprintf(baseURL, strings.ReplaceAll(defaultLocation, "/", "-"))
} else {
// If no locations are provided, panic, this is a programming error
panic(fmt.Errorf("no allowedLocations provided for %s", c.Command.Name()))
}

// add the server URL flag
// Add the server URL flag
c.Command.PersistentFlags().StringP(
constants.ArgServerUrl, constants.ArgServerUrlShort, defaultUrl, "Override default host url",
constants.ArgServerUrl, constants.ArgServerUrlShort, defaultUrl, "Override default host URL",
)
viper.BindPFlag(constants.ArgServerUrl, c.Command.PersistentFlags().Lookup(constants.ArgServerUrl))

Expand All @@ -67,7 +110,7 @@ func WithRegionalFlags(c *Command, baseURL string, allowedLocations []string) *C
},
)

// wrap the pre-run logic to handle mutually exclusive flags
// Wrap the pre-run logic to handle mutually exclusive flags
originalPreRun := c.Command.PersistentPreRunE
c.Command.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
if originalPreRun != nil {
Expand All @@ -76,15 +119,18 @@ func WithRegionalFlags(c *Command, baseURL string, allowedLocations []string) *C
}
}

// Mark the flags as mutually exclusive
c.Command.MarkFlagsMutuallyExclusive(constants.ArgServerUrl, constants.FlagLocation)

// Handle the location flag
if location, _ := cmd.Flags().GetString(constants.FlagLocation); location != "" {
if url, ok := locationsToUrl[location]; ok {
viper.Set(constants.ArgServerUrl, url)
normalizedLoc := strings.ReplaceAll(location, "/", "-")
if strings.Contains(baseURL, "%s") {
viper.Set(constants.ArgServerUrl, fmt.Sprintf(baseURL, normalizedLoc))
} else {
// Log a warning if trying to use location with a non-placeholder URL
fmt.Fprintf(c.Command.ErrOrStderr(), jsontabwriter.GenerateLogOutput(
"WARN: %s is an invalid location. Valid locations are: %s",
location, allowedLocations))
"WARN: Ignoring location %s because this API does not support region-based URLs", location))
}
}

Expand Down

0 comments on commit 96dbee9

Please sign in to comment.