package add import ( "context" "errors" "fmt" "edge-infra.dev/pkg/edge/api/graph/model" "edge-infra.dev/pkg/edge/edgeadmin/commands/operatorintervention/edgeextension" "edge-infra.dev/pkg/edge/edgeadmin/commands/operatorintervention/format" "edge-infra.dev/pkg/edge/edgecli" "edge-infra.dev/pkg/edge/edgecli/constructors" "edge-infra.dev/pkg/lib/cli/command" "edge-infra.dev/pkg/lib/cli/rags" ) type SubCommand interface { // Returns the short usage string for a subcommand ShortUsageString() string // Returns the short help string for a subcommand ShortHelpString() string // Returns the long help string for a subcommand LongHelpString() string // Returns list of associated flags for a subcommand Flags() []*rags.Rag // Gets all values from supported flags for a subcommand and // creates a GraphQL variables map from them Variables() map[string]interface{} // Return the mutation struct for a subcommand's graphQL mutation Mutation() OperatorInterventionMutation // Return the input type used to generate a relevant error display string ResponseType() string } type OperatorInterventionMutation interface { Errors() []*model.OperatorInterventionErrorResponse } func NewCmd(cfg *edgecli.Config) *command.Command { return &command.Command{ ShortUsage: "edgeadmin operatorintervention add", ShortHelp: "subcommands to add operator intervention configurations", Commands: []*command.Command{ newSubcommand(&Command{}, cfg), newSubcommand(&Privilege{}, cfg), newSubcommand(&RoleMapping{}, cfg), newSubcommand(&Rule{}, cfg), }, } } // There is a lot of repeated boilerplate between different Add subcommands, so an // interface is passed in to cut down on duplication and to have a fixed Add subcommand // structure func newSubcommand(subcommand SubCommand, cfg *edgecli.Config) *command.Command { var ( // Extension that validates connection flags and builds // a BFF client on initialization edge = &edgeextension.Ext{Cfg: cfg} ) var cmd *command.Command cmd = &command.Command{ ShortUsage: subcommand.ShortUsageString(), ShortHelp: subcommand.ShortHelpString(), LongHelp: subcommand.LongHelpString(), Flags: subcommand.Flags(), Extensions: []command.Extension{ edge, }, Exec: func(ctx context.Context, _ []string) error { if err := validateRequiredFlags(cmd.Rags); err != nil { return err } registrar, err := constructors.BuildRegistrar(cmd.Rags) if err != nil { return err } mutation := subcommand.Mutation() if err := registrar.GetBFFClient().Mutate(ctx, mutation, subcommand.Variables()); err != nil { return err } return handleResponse(subcommand.ResponseType(), mutation.Errors()) }, } return cmd } // Current implementation of flagutil.ValidateRequiredFlags() does not validate list // flags so custom implementation is needed. This function checks only for string and // string collection flags func validateRequiredFlags(rags *rags.RagSet) (err error) { for _, rag := range rags.Rags() { if rag.Required { val := rag.Value.String() if val == "" { err = errors.Join(err, fmt.Errorf("Flag '%s' is required", rag.Name)) } } } return err } func handleResponse(inputType string, errs []*model.OperatorInterventionErrorResponse) error { fmt.Println(format.GenerateApplyOutput(inputType, errs)) if len(errs) != 0 { // Return empty error to guarantee binary non-zero exit code. // Error details have already been printed, so return an empty // error. This does unfortunately print an extra blank line on // the output return fmt.Errorf("") } return nil } const ( longHelpSingleFlagFormatString = ` This adds new %[1]ss to the operator intervention configuration database. To add a %[1]s, run the following: $ edgeadmin operatorintervention add %[1]s --%[1]s example If the %[1]s is valid, it will then be added to the database. For a %[1]s to be considered valid, it must contain at least one character and must not have any whitespace. The database query is best-effort and no errors are expected to be returned as long as all %[1]ss are valid inputs. Multiple %[1]ss can be added at once by using the --%[1]s flag multiple times: $ edgeadmin operatorintervention add %[1]s --%[1]s example1 --%[1]s example2 --%[1]s example3 ` longHelpTwoFlagsFormatStr = ` This adds new %[1]ss to the operator intervention configuration database. To add a %[1]s, run the following: $ edgeadmin operatorintervention add %[1]s --%[2]s example --%[3]s val1 If the %[1]s is valid, it will then be added to the database. For a %[1]s to be considered valid, it must contain ONLY one valid %[2]s and at least one %[3]s (all of which must be valid). The database query is all-or-nothing, so a response with potential errors is expected. This may be because of an incomplete payload, or because one or more input values were not found in the database. Multiple %[3]ss can be added to the same %[1]s by using the --%[3]s flag multiple times: $ edgeadmin operatorintervention add %[1]s --%[2]s example --%[3]s val1 --%[3]s val2 --%[3]s val3 ` )