-
Notifications
You must be signed in to change notification settings - Fork 363
/
create.go
226 lines (188 loc) · 5.99 KB
/
create.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package samples
import (
"errors"
"fmt"
"os"
"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
"github.com/stripe/stripe-cli/pkg/ansi"
"github.com/stripe/stripe-cli/pkg/config"
"github.com/stripe/stripe-cli/pkg/samples"
"github.com/stripe/stripe-cli/pkg/validators"
"github.com/stripe/stripe-cli/pkg/version"
)
// CreateCmd wraps the `create` command for samples which generates a new
// project
type CreateCmd struct {
cfg *config.Config
Cmd *cobra.Command
forceRefresh bool
}
// NewCreateCmd creates and returns a create command for samples
func NewCreateCmd(config *config.Config) *CreateCmd {
createCmd := &CreateCmd{
cfg: config,
forceRefresh: false,
}
createCmd.Cmd = &cobra.Command{
Use: "create <sample> [destination]",
Args: validators.MaximumNArgs(2),
Short: "Setup and bootstrap a Stripe Sample",
Long: `The create command will locally clone a sample, let you select which integration,
client, and server you want to run. It then automatically bootstraps the
local configuration to let you get started faster.`,
Example: `stripe samples create accept-a-payment
stripe samples create react-elements-card-payment my-payments-form`,
RunE: createCmd.runCreateCmd,
}
createCmd.Cmd.Flags().BoolVar(&createCmd.forceRefresh, "force-refresh", false, "Forcefully refresh the local samples cache")
return createCmd
}
func (cc *CreateCmd) runCreateCmd(cmd *cobra.Command, args []string) error {
version.CheckLatestVersion()
if len(args) == 0 {
cmd.Help()
return nil
}
selectedSample := args[0]
destination := selectedSample
if len(args) > 1 {
destination = args[1]
}
color := ansi.Color(os.Stdout)
spinner := ansi.StartNewSpinner(fmt.Sprintf("Downloading %s", selectedSample), os.Stdout)
sampleManager, err := samples.NewSampleManager(cc.cfg)
if err != nil {
ansi.StopSpinner(spinner, "", os.Stdout)
return err
}
sampleConfig, err := sampleManager.GetSampleConfig(selectedSample, cc.forceRefresh)
if err != nil {
ansi.StopSpinner(spinner, "", os.Stdout)
return err
}
ansi.StopSpinner(spinner, "", os.Stdout)
fmt.Printf("%s %s\n", color.Green("✔"), ansi.Faint("Finished downloading"))
// Once we've initialized the sample in the local cache
// directory, the user needs to select which integration they
// want to work with (if selectedSamplelicable) and which language they
// want to copy
selectedConfig, err := promptSampleConfig(sampleConfig)
if err != nil {
return err
}
resultChan := make(chan samples.CreationResult)
go sampleManager.Create(
cmd.Context(),
selectedSample,
selectedConfig,
destination,
cc.forceRefresh,
resultChan,
)
for res := range resultChan {
if res.Err != nil {
ansi.StopSpinner(spinner, "", os.Stdout)
return res.Err
}
switch res.State {
case samples.WillInitialize:
case samples.DidInitialize:
case samples.WillCopy:
spinner = ansi.StartNewSpinner(fmt.Sprintf("Copying files over... %s", destination), os.Stdout)
case samples.DidCopy:
ansi.StopSpinner(spinner, "", os.Stdout)
fmt.Printf("%s %s\n", color.Green("✔"), ansi.Faint("Files copied"))
case samples.WillConfigure:
spinner = ansi.StartNewSpinner(fmt.Sprintf("Configuring your code... %s", selectedSample), os.Stdout)
case samples.DidConfigure:
ansi.StopSpinner(spinner, "", os.Stdout)
fmt.Printf("%s %s\n", color.Green("✔"), ansi.Faint("Project configured"))
case samples.Done:
fmt.Println("You're all set. To get started: cd", destination)
if res.PostInstall != "" {
fmt.Println(res.PostInstall)
}
default:
return errors.New("an unknown error occurred during sample creation")
}
}
return nil
}
// promptSampleConfig prompts the user to select the integration they want to use
// (if available) and the language they want the integration to be.
func promptSampleConfig(sampleConfig *samples.SampleConfig) (*samples.SelectedConfig, error) {
var selectedConfig samples.SelectedConfig
if sampleConfig.HasIntegrations() {
integration, err := integrationSelectPrompt(sampleConfig)
if err != nil {
return nil, err
}
selectedConfig.Integration = integration
} else {
selectedConfig.Integration = &sampleConfig.Integrations[0]
}
if selectedConfig.Integration.HasMultipleClients() {
client, err := clientSelectPrompt(selectedConfig.Integration.Clients)
if err != nil {
return nil, err
}
selectedConfig.Client = client
} else {
selectedConfig.Client = ""
}
if selectedConfig.Integration.HasMultipleServers() {
server, err := serverSelectPrompt(selectedConfig.Integration.Servers)
if err != nil {
return nil, err
}
selectedConfig.Server = server
} else {
selectedConfig.Server = ""
}
return &selectedConfig, nil
}
func selectOptions(template, label string, options []string) (string, error) {
color := ansi.Color(os.Stdout)
templates := &promptui.SelectTemplates{
Selected: color.Green("✔").String() + ansi.Faint(fmt.Sprintf(" Selected %s: {{ . | bold }} ", template)),
}
prompt := promptui.Select{
Label: label,
Items: options,
Templates: templates,
Size: 7,
}
_, result, err := prompt.Run()
if err != nil {
return "", err
}
return result, nil
}
func clientSelectPrompt(clients []string) (string, error) {
selected, err := selectOptions("client", "Which client would you like to use", clients)
if err != nil {
return "", err
}
return selected, nil
}
func integrationSelectPrompt(sc *samples.SampleConfig) (*samples.SampleConfigIntegration, error) {
selected, err := selectOptions("integration", "What type of integration would you like to use", sc.IntegrationNames())
if err != nil {
return nil, err
}
var selectedIntegration *samples.SampleConfigIntegration
for i, integration := range sc.Integrations {
if integration.Name == selected {
selectedIntegration = &sc.Integrations[i]
}
}
return selectedIntegration, nil
}
func serverSelectPrompt(servers []string) (string, error) {
selected, err := selectOptions("server", "What server would you like to use", servers)
if err != nil {
return "", err
}
return selected, nil
}