// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 package selector import ( "fmt" "sort" "strings" "github.com/aws/copilot-cli/internal/pkg/template" "github.com/aws/copilot-cli/internal/pkg/term/prompt" "gopkg.in/yaml.v3" ) // CFNSelector represents a selector for a CloudFormation template. type CFNSelector struct { prompt Prompter } // NewCFNSelector initializes a CFNSelector. func NewCFNSelector(prompt Prompter) *CFNSelector { return &CFNSelector{ prompt: prompt, } } // Resources prompts the user to multiselect resources from a CloudFormation template body. // By default, the prompt filters out any custom resource in the template. func (sel *CFNSelector) Resources(msg, finalMsg, help, body string) ([]template.CFNResource, error) { tpl := struct { Resources map[string]struct { Type string `yaml:"Type"` } `yaml:"Resources"` }{} if err := yaml.Unmarshal([]byte(body), &tpl); err != nil { return nil, fmt.Errorf("unmarshal CloudFormation template: %v", err) } // Prompt for a selection. var options []prompt.Option for name, resource := range tpl.Resources { if resource.Type == "AWS::Lambda::Function" || strings.HasPrefix(resource.Type, "Custom::") { continue } options = append(options, prompt.Option{ Value: name, Hint: resource.Type, }) } sort.Slice(options, func(i, j int) bool { // Sort options by resource type, if they're the same resource type then sort by logicalID. if options[i].Hint == options[j].Hint { return options[i].Value < options[j].Value } return options[i].Hint < options[j].Hint }) logicalIDs, err := sel.prompt.MultiSelectOptions(msg, help, options, prompt.WithFinalMessage(finalMsg)) if err != nil { return nil, fmt.Errorf("select CloudFormation resources: %v", err) } // Transform to template.CFNResource out := make([]template.CFNResource, len(logicalIDs)) for i, logicalID := range logicalIDs { out[i] = template.CFNResource{ Type: template.CFNType(tpl.Resources[logicalID].Type), LogicalID: logicalID, } } return out, nil }