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
68 changes: 17 additions & 51 deletions cli/cmd/customer_create.go
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just commenting here, because there's no good place for it. I noticed in the output of your screenshots the column header still said "CHANNELS" (plural). That needs to be updated here. There might be other places too, but I'm not familiar with this repo, but it looks like maybe this customer type too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i saw that CHANNELS too! But I looked up the git history for that line of code and it has always been plural (since 2019/ before multi-channel). I'd be down to change it to singular but since its always been plural i just left it.

Also that customer type is the API contract which still technically is an array.

Thanks for reviewing all this so well!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point! 😉 I don't see any reason not to change it to singular though, especially since we're removing this functionality now.

Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package cmd

import (
"fmt"
"os"
"time"

"github.com/pkg/errors"
Expand All @@ -15,8 +13,7 @@ import (
type createCustomerOpts struct {
Name string
CustomID string
ChannelNames []string
DefaultChannel string
ChannelName string
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent field naming between create and update opts

Low Severity

The createCustomerOpts struct uses ChannelName while updateCustomerOpts uses Channel for the same concept. The previous code was consistently plural (ChannelNames and Channels). Using inconsistent naming for equivalent fields across related structs reduces code readability and can cause confusion when working with both commands.

Additional Locations (1)

Fix in Cursor Fix in Web

ExpiryDuration time.Duration
EnsureChannel bool
IsAirgapEnabled bool
Expand Down Expand Up @@ -54,9 +51,6 @@ The --app flag must be set to specify the target application.`,
Example: `# Create a basic customer with a name and assigned to a channel
replicated customer create --app myapp --name "Acme Inc" --channel stable

# Create a customer with multiple channels and a custom ID
replicated customer create --app myapp --name "Beta Corp" --custom-id "BETA123" --channel beta --channel stable

# Create a paid customer with specific features enabled
replicated customer create --app myapp --name "Enterprise Ltd" --type paid --channel enterprise --airgap --snapshot

Expand All @@ -65,8 +59,7 @@ replicated customer create --app myapp --name "Trial User" --type trial --channe

# Create a customer with all available options
replicated customer create --app myapp --name "Full Options Inc" --custom-id "FULL001" \
--channel stable --channel beta --default-channel stable --type paid \
--email "contact@fulloptions.com" --expires-in 8760h \
--channel stable --type paid --email "contact@fulloptions.com" --expires-in 8760h \
--airgap --snapshot --kots-install --embedded-cluster-download \
--support-bundle-upload --ensure-channel`,
RunE: func(cmd *cobra.Command, args []string) error {
Expand All @@ -79,8 +72,7 @@ replicated customer create --app myapp --name "Full Options Inc" --custom-id "FU
parent.AddCommand(cmd)
cmd.Flags().StringVar(&opts.Name, "name", "", "Name of the customer")
cmd.Flags().StringVar(&opts.CustomID, "custom-id", "", "Set a custom customer ID to more easily tie this customer record to your external data systems")
cmd.Flags().StringArrayVar(&opts.ChannelNames, "channel", []string{}, "Release channel to which the customer should be assigned (can be specified multiple times)")
cmd.Flags().StringVar(&opts.DefaultChannel, "default-channel", "", "Which of the specified channels should be the default channel. if not set, the first channel specified will be the default channel.")
cmd.Flags().StringVar(&opts.ChannelName, "channel", "", "Release channel to which the customer should be assigned")
cmd.Flags().DurationVar(&opts.ExpiryDuration, "expires-in", 0, "If set, an expiration date will be set on the license. Supports Go durations like '72h' or '3600m'")
cmd.Flags().BoolVar(&opts.EnsureChannel, "ensure-channel", false, "If set, channel will be created if it does not exist.")
cmd.Flags().BoolVar(&opts.IsAirgapEnabled, "airgap", false, "If set, the license will allow airgap installs.")
Expand Down Expand Up @@ -126,50 +118,24 @@ func (r *runners) createCustomer(cmd *cobra.Command, opts createCustomerOpts, ou
opts.CustomerType = "prod"
}

channels := []kotsclient.CustomerChannel{}

foundDefaultChannel := false
for _, requestedChannel := range opts.ChannelNames {
getOrCreateChannelOptions := client.GetOrCreateChannelOptions{
AppID: r.appID,
AppType: r.appType,
NameOrID: requestedChannel,
Description: "",
CreateIfAbsent: opts.EnsureChannel,
}

channel, err := r.api.GetOrCreateChannelByName(getOrCreateChannelOptions)
if err != nil {
return errors.Wrap(err, "get channel")
}

customerChannel := kotsclient.CustomerChannel{
ID: channel.ID,
}

if opts.DefaultChannel == requestedChannel {
customerChannel.IsDefault = true
foundDefaultChannel = true
}

channels = append(channels, customerChannel)
}

if len(channels) == 0 {
return errors.New("no channels found")
getOrCreateChannelOptions := client.GetOrCreateChannelOptions{
AppID: r.appID,
AppType: r.appType,
NameOrID: opts.ChannelName,
Description: "",
CreateIfAbsent: opts.EnsureChannel,
}

if opts.DefaultChannel != "" && !foundDefaultChannel {
return errors.New("default channel not found in specified channels")
channel, err := r.api.GetOrCreateChannelByName(getOrCreateChannelOptions)
if err != nil {
return errors.Wrap(err, "get channel")
}

if !foundDefaultChannel {
if len(channels) > 1 {
fmt.Fprintln(os.Stderr, "No default channel specified, defaulting to the first channel specified.")
}
firstChannel := channels[0]
firstChannel.IsDefault = true
channels[0] = firstChannel
channels := []kotsclient.CustomerChannel{
{
ID: channel.ID,
IsDefault: true,
},
}

createOpts := kotsclient.CreateCustomerOpts{
Expand Down
66 changes: 18 additions & 48 deletions cli/cmd/customer_update.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package cmd

import (
"fmt"
"os"
"time"

"github.com/pkg/errors"
Expand All @@ -16,8 +14,7 @@ type updateCustomerOpts struct {
CustomerID string
Name string
CustomID string
Channels []string
DefaultChannel string
Channel string
ExpiryDuration time.Duration
EnsureChannel bool
IsAirgapEnabled bool
Expand Down Expand Up @@ -53,8 +50,8 @@ func (r *runners) InitCustomerUpdateCommand(parent *cobra.Command) *cobra.Comman
Example: `# Update a customer's name
replicated customer update --customer cus_abcdef123456 --name "New Company Name"

# Change a customer's channel and make it the default
replicated customer update --customer cus_abcdef123456 --channel stable --default-channel stable
# Change a customer's channel
replicated customer update --customer cus_abcdef123456 --channel stable

# Enable airgap installations for a customer
replicated customer update --customer cus_abcdef123456 --airgap
Expand All @@ -79,8 +76,7 @@ replicated customer update --customer cus_abcdef123456 --name "JSON Corp" --outp
cmd.Flags().StringVar(&opts.CustomerID, "customer", "", "The ID of the customer to update")
cmd.Flags().StringVar(&opts.Name, "name", "", "Name of the customer")
cmd.Flags().StringVar(&opts.CustomID, "custom-id", "", "Set a custom customer ID to more easily tie this customer record to your external data systems")
cmd.Flags().StringArrayVar(&opts.Channels, "channel", []string{}, "Release channel to which the customer should be assigned (can be specified multiple times)")
cmd.Flags().StringVar(&opts.DefaultChannel, "default-channel", "", "Which of the specified channels should be the default channel. if not set, the first channel specified will be the default channel.")
cmd.Flags().StringVar(&opts.Channel, "channel", "", "Release channel to which the customer should be assigned")
cmd.Flags().DurationVar(&opts.ExpiryDuration, "expires-in", 0, "If set, an expiration date will be set on the license. Supports Go durations like '72h' or '3600m'")
cmd.Flags().BoolVar(&opts.EnsureChannel, "ensure-channel", false, "If set, channel will be created if it does not exist.")
cmd.Flags().BoolVar(&opts.IsAirgapEnabled, "airgap", false, "If set, the license will allow airgap installs.")
Expand Down Expand Up @@ -132,50 +128,24 @@ func (r *runners) updateCustomer(cmd *cobra.Command, opts updateCustomerOpts) (e
opts.Type = "prod"
}

channels := []kotsclient.CustomerChannel{}

foundDefaultChannel := false
for _, requestedChannel := range opts.Channels {
getOrCreateChannelOptions := client.GetOrCreateChannelOptions{
AppID: r.appID,
AppType: r.appType,
NameOrID: requestedChannel,
Description: "",
CreateIfAbsent: opts.EnsureChannel,
}

channel, err := r.api.GetOrCreateChannelByName(getOrCreateChannelOptions)
if err != nil {
return errors.Wrap(err, "get channel")
}

customerChannel := kotsclient.CustomerChannel{
ID: channel.ID,
}

if opts.DefaultChannel == requestedChannel {
customerChannel.IsDefault = true
foundDefaultChannel = true
}

channels = append(channels, customerChannel)
getOrCreateChannelOptions := client.GetOrCreateChannelOptions{
AppID: r.appID,
AppType: r.appType,
NameOrID: opts.Channel,
Description: "",
CreateIfAbsent: opts.EnsureChannel,
}

if len(channels) == 0 {
return errors.New("no channels found")
}

if opts.DefaultChannel != "" && !foundDefaultChannel {
return errors.New("default channel not found in specified channels")
channel, err := r.api.GetOrCreateChannelByName(getOrCreateChannelOptions)
if err != nil {
return errors.Wrap(err, "get channel")
}

if !foundDefaultChannel {
if len(channels) > 1 {
fmt.Fprintln(os.Stderr, "No default channel specified, defaulting to the first channel specified.")
}
firstChannel := channels[0]
firstChannel.IsDefault = true
channels[0] = firstChannel
channels := []kotsclient.CustomerChannel{
{
ID: channel.ID,
IsDefault: true,
},
}

updateOpts := kotsclient.UpdateCustomerOpts{
Expand Down
2 changes: 1 addition & 1 deletion cli/print/customers.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
"github.com/replicatedhq/replicated/pkg/types"
)

var customersTmplSrc = `ID NAME CHANNELS EXPIRES TYPE CUSTOM_ID
var customersTmplSrc = `ID NAME CHANNEL EXPIRES TYPE CUSTOM_ID
{{ range . -}}
{{ .ID }} {{ .Name }} {{range .Channels}} {{.Name}}{{end}} {{if not .Expires}}Never{{else}}{{.Expires}}{{end}} {{.Type}} {{if not .CustomID}}Not Set{{else}}{{.CustomID}}{{end}}
{{ end }}`
Expand Down
Loading