Skip to content

Commit e0fe8ae

Browse files
authored
feat(gateway): add command to list plans (#314)
1 parent 6abf606 commit e0fe8ae

6 files changed

Lines changed: 100 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## Added
11+
12+
- Add `gateway plans` command for listing gateway plans.
13+
1014
## [3.8.1] - 2024-05-24
1115

1216
### Changed

internal/commands/all/all.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ func BuildCommands(rootCmd *cobra.Command, conf *config.Config) {
202202
gatewayCommand := commands.BuildCommand(gateway.BaseGatewayCommand(), rootCmd, conf)
203203
commands.BuildCommand(gateway.DeleteCommand(), gatewayCommand.Cobra(), conf)
204204
commands.BuildCommand(gateway.ListCommand(), gatewayCommand.Cobra(), conf)
205+
commands.BuildCommand(gateway.PlansCommand(), gatewayCommand.Cobra(), conf)
205206

206207
// Host operations
207208
hostCommand := commands.BuildCommand(host.BaseHostCommand(), rootCmd, conf)

internal/commands/database/properties/show.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@ func (s *showCommand) Execute(exec commands.Executor, key string) (output.Output
5353
{Title: "Description:", Key: "description", Value: details.Description},
5454
{Title: "Help message:", Key: "user_error", Value: details.UserError},
5555
{Title: "Create only:", Key: "createOnly", Value: details.CreateOnly, Format: format.Boolean},
56-
{Title: "Type:", Key: "type", Value: details.Type, Format: formatAlternatives},
56+
{Title: "Type:", Key: "type", Value: details.Type, Format: format.StringSliceOr},
5757
{Title: "Default:", Key: "default", Value: details.Default},
58-
{Title: "Possible values:", Key: "enum", Value: details.Enum, Format: formatAlternatives},
58+
{Title: "Possible values:", Key: "enum", Value: details.Enum, Format: format.StringSliceOr},
5959
{Title: "Pattern:", Key: "pattern", Value: details.Pattern},
6060
{Title: "Min length:", Key: "minLength", Value: details.MinLength},
6161
{Title: "Minimum:", Key: "minimum", Value: details.Minimum, Format: format.Dereference[float64]},
6262
{Title: "Max length:", Key: "maxLength", Value: details.MaxLength},
6363
{Title: "Maximum:", Key: "maximum", Value: details.Maximum, Format: format.Dereference[float64]},
64-
{Title: "Properties:", Key: "properties", Value: childProperties, Format: formatProperties},
64+
{Title: "Properties:", Key: "properties", Value: childProperties, Format: format.StringSliceAnd},
6565
}
6666

6767
return output.MarshaledWithHumanOutput{

internal/commands/database/properties/type.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,8 @@ func (s *dbTypeCommand) ExecuteWithoutArguments(exec commands.Executor) (output.
6060
Columns: []output.TableColumn{
6161
{Key: "property", Header: "Property"},
6262
{Key: "createOnly", Header: "Create only", Format: format.Boolean},
63-
{Key: "type", Header: "Type", Format: formatAlternatives},
64-
{Key: "example", Header: "Example", Format: formatAlternatives},
63+
{Key: "type", Header: "Type", Format: format.StringSliceOr},
64+
{Key: "example", Header: "Example", Format: format.StringSliceOr},
6565
},
6666
Rows: rows,
6767
},

internal/commands/gateway/plans.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package gateway
2+
3+
import (
4+
"sort"
5+
6+
"github.com/UpCloudLtd/upcloud-cli/v3/internal/commands"
7+
"github.com/UpCloudLtd/upcloud-cli/v3/internal/format"
8+
"github.com/UpCloudLtd/upcloud-cli/v3/internal/output"
9+
)
10+
11+
// PlansCommand creates the "server plans" command
12+
func PlansCommand() commands.Command {
13+
return &plansCommand{
14+
BaseCommand: commands.New("plans", "List gateway plans", "upctl gateway plans"),
15+
}
16+
}
17+
18+
type plansCommand struct {
19+
*commands.BaseCommand
20+
}
21+
22+
// ExecuteWithoutArguments implements commands.NoArgumentCommand
23+
func (s *plansCommand) ExecuteWithoutArguments(exec commands.Executor) (output.Output, error) {
24+
plans, err := exec.All().GetGatewayPlans(exec.Context())
25+
if err != nil {
26+
return nil, err
27+
}
28+
29+
sort.Slice(plans, func(i, j int) bool {
30+
if plans[i].ServerNumber != plans[j].ServerNumber {
31+
return plans[i].ServerNumber < plans[j].ServerNumber
32+
}
33+
34+
if plans[i].PerGatewayBandwidthMbps != plans[j].PerGatewayBandwidthMbps {
35+
return plans[i].PerGatewayBandwidthMbps < plans[j].PerGatewayBandwidthMbps
36+
}
37+
38+
if plans[i].PerGatewayMaxConnections != plans[j].PerGatewayMaxConnections {
39+
return plans[i].PerGatewayMaxConnections < plans[j].PerGatewayMaxConnections
40+
}
41+
42+
return plans[i].VPNTunnelAmount < plans[j].VPNTunnelAmount
43+
})
44+
45+
rows := []output.TableRow{}
46+
for _, p := range plans {
47+
rows = append(rows, output.TableRow{
48+
p.Name,
49+
p.ServerNumber,
50+
p.SupportedFeatures,
51+
p.PerGatewayBandwidthMbps,
52+
p.PerGatewayMaxConnections,
53+
p.VPNTunnelAmount,
54+
})
55+
}
56+
57+
return output.MarshaledWithHumanOutput{
58+
Value: plans,
59+
Output: output.Table{
60+
Columns: []output.TableColumn{
61+
{Key: "name", Header: "Name"},
62+
{Key: "server_number", Header: "Server count"},
63+
{Key: "supported_features", Header: "Features", Format: format.StringSliceAnd},
64+
{Key: "per_gateway_bandwidth_mbps", Header: "Max bandwidth (Mbps)"},
65+
{Key: "per_gateway_max_connections", Header: "Max connections"},
66+
{Key: "vpn_tunnel_amount", Header: "VPN tunnel amount"},
67+
},
68+
Rows: rows,
69+
},
70+
}, nil
71+
}

internal/commands/database/properties/format.go renamed to internal/format/stringslice.go

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
package databaseproperties
1+
package format
22

33
import (
44
"fmt"
5+
"reflect"
56
"strings"
67

78
"github.com/jedib0t/go-pretty/v6/text"
89
)
910

10-
func formatAlternatives(val interface{}) (text.Colors, string, error) {
11+
func StringSliceOr(val interface{}) (text.Colors, string, error) {
1112
return formatStringSlice(val, "or")
1213
}
1314

14-
func formatProperties(val interface{}) (text.Colors, string, error) {
15+
func StringSliceAnd(val interface{}) (text.Colors, string, error) {
1516
return formatStringSlice(val, "and")
1617
}
1718

@@ -24,19 +25,23 @@ func formatStringSlice(val interface{}, andOrOr string) (text.Colors, string, er
2425
return nil, stringVal, nil
2526
}
2627

27-
if ifaceSliceVal, ok := val.([]interface{}); ok {
28-
return nil, alternativesString(ifaceSliceVal, andOrOr), nil
28+
if ifaceSliceVal, ok := toIfaceSlice(val); ok {
29+
return nil, stringSliceString(ifaceSliceVal, andOrOr), nil
2930
}
3031

31-
if stringSliceVal, ok := val.([]string); ok {
32+
return nil, fmt.Sprintf("%+v", val), nil
33+
}
34+
35+
func toIfaceSlice(val interface{}) ([]interface{}, bool) {
36+
if reflect.TypeOf(val).Kind() == reflect.Slice {
3237
ifaceSliceVal := []interface{}{}
33-
for _, i := range stringSliceVal {
34-
ifaceSliceVal = append(ifaceSliceVal, i)
38+
reflectedVal := reflect.ValueOf(val)
39+
for i := 0; i < reflectedVal.Len(); i++ {
40+
ifaceSliceVal = append(ifaceSliceVal, reflectedVal.Index(i).Interface())
3541
}
36-
return nil, alternativesString(ifaceSliceVal, andOrOr), nil
42+
return ifaceSliceVal, true
3743
}
38-
39-
return nil, fmt.Sprintf("%+v", val), nil
44+
return nil, false
4045
}
4146

4247
func maxStringLen(strings []string) int {
@@ -49,7 +54,7 @@ func maxStringLen(strings []string) int {
4954
return max
5055
}
5156

52-
func alternativesString(values []interface{}, andOrOr string) string {
57+
func stringSliceString(values []interface{}, andOrOr string) string {
5358
if len(values) == 0 {
5459
return ""
5560
}
@@ -68,6 +73,6 @@ func alternativesString(values []interface{}, andOrOr string) string {
6873
whitespace = "\n"
6974
}
7075

71-
str := strings.Join(strs[:len(strs)-1], ","+whitespace)
72-
return str + fmt.Sprintf(" %s%s%s", andOrOr, whitespace, strs[len(values)-1])
76+
str := strings.Join(strs[:len(strs)-1], text.FgHiBlack.Sprint(",")+whitespace)
77+
return str + fmt.Sprintf(" %s%s%s", text.FgHiBlack.Sprint(andOrOr), whitespace, strs[len(values)-1])
7378
}

0 commit comments

Comments
 (0)