@@ -54,66 +54,29 @@ func runDownload(cfg config.Config, flags *pflag.FlagSet, args []string) error {
54
54
return err
55
55
}
56
56
57
- identifier , err := downloadIdentifier (flags )
57
+ download , err := newDownload (flags , usrCfg )
58
58
if err != nil {
59
59
return err
60
60
}
61
- url := fmt .Sprintf ("%s/solutions/%s" , usrCfg .GetString ("apibaseurl" ), identifier )
62
61
63
- client , err := api .NewClient (usrCfg .GetString ("token" ), usrCfg .GetString ("apibaseurl" ))
64
- if err != nil {
65
- return err
66
- }
67
-
68
- req , err := client .NewRequest ("GET" , url , nil )
69
- if err != nil {
70
- return err
71
- }
72
-
73
- req , err = addQueryToDownloadRequest (flags , req )
74
- if err != nil {
75
- return err
76
- }
62
+ metadata := download .payload .metadata ()
63
+ dir := metadata .Exercise (usrCfg .GetString ("workspace" )).MetadataDir ()
77
64
78
- res , err := client .Do (req )
79
- if err != nil {
65
+ if err := os .MkdirAll (dir , os .FileMode (0755 )); err != nil {
80
66
return err
81
67
}
82
68
83
- var payload downloadPayload
84
- defer res .Body .Close ()
85
- if err := json .NewDecoder (res .Body ).Decode (& payload ); err != nil {
86
- return fmt .Errorf ("unable to parse API response - %s" , err )
87
- }
88
-
89
- if res .StatusCode == http .StatusUnauthorized {
90
- siteURL := config .InferSiteURL (usrCfg .GetString ("apibaseurl" ))
91
- return fmt .Errorf ("unauthorized request. Please run the configure command. You can find your API token at %s/my/settings" , siteURL )
92
- }
93
-
94
- if res .StatusCode != http .StatusOK {
95
- switch payload .Error .Type {
96
- case "track_ambiguous" :
97
- return fmt .Errorf ("%s: %s" , payload .Error .Message , strings .Join (payload .Error .PossibleTrackIDs , ", " ))
98
- default :
99
- return errors .New (payload .Error .Message )
100
- }
101
- }
102
-
103
- metadata := payload .metadata ()
104
- dir := metadata .Exercise (usrCfg .GetString ("workspace" )).MetadataDir ()
105
-
106
- if err := os .MkdirAll (dir , os .FileMode (0755 )); err != nil {
69
+ if err := metadata .Write (dir ); err != nil {
107
70
return err
108
71
}
109
72
110
- err = metadata . Write ( dir )
73
+ client , err := api . NewClient ( usrCfg . GetString ( "token" ), usrCfg . GetString ( "apibaseurl" ) )
111
74
if err != nil {
112
75
return err
113
76
}
114
77
115
- for _ , file := range payload .Solution .Files {
116
- unparsedURL := fmt .Sprintf ("%s%s" , payload .Solution .FileDownloadBaseURL , file )
78
+ for _ , file := range download . payload .Solution .Files {
79
+ unparsedURL := fmt .Sprintf ("%s%s" , download . payload .Solution .FileDownloadBaseURL , file )
117
80
parsedURL , err := netURL .ParseRequestURI (unparsedURL )
118
81
119
82
if err != nil {
@@ -176,6 +139,150 @@ func runDownload(cfg config.Config, flags *pflag.FlagSet, args []string) error {
176
139
return nil
177
140
}
178
141
142
+ type download struct {
143
+ // either/or
144
+ slug , uuid string
145
+
146
+ // user config
147
+ token , apibaseurl , workspace string
148
+
149
+ // optional
150
+ track , team string
151
+
152
+ payload * downloadPayload
153
+ }
154
+
155
+ func newDownload (flags * pflag.FlagSet , usrCfg * viper.Viper ) (* download , error ) {
156
+ var err error
157
+ d := & download {}
158
+ d .uuid , err = flags .GetString ("uuid" )
159
+ if err != nil {
160
+ return nil , err
161
+ }
162
+ d .slug , err = flags .GetString ("exercise" )
163
+ if err != nil {
164
+ return nil , err
165
+ }
166
+ d .track , err = flags .GetString ("track" )
167
+ if err != nil {
168
+ return nil , err
169
+ }
170
+ d .team , err = flags .GetString ("team" )
171
+ if err != nil {
172
+ return nil , err
173
+ }
174
+
175
+ d .token = usrCfg .GetString ("token" )
176
+ d .apibaseurl = usrCfg .GetString ("apibaseurl" )
177
+ d .workspace = usrCfg .GetString ("workspace" )
178
+
179
+ if err = d .needsSlugXorUUID (); err != nil {
180
+ return nil , err
181
+ }
182
+ if err = d .needsUserConfigValues (); err != nil {
183
+ return nil , err
184
+ }
185
+ if err = d .needsSlugWhenGivenTrackOrTeam (); err != nil {
186
+ return nil , err
187
+ }
188
+
189
+ client , err := api .NewClient (d .token , d .apibaseurl )
190
+ if err != nil {
191
+ return nil , err
192
+ }
193
+
194
+ req , err := client .NewRequest ("GET" , d .url (), nil )
195
+ if err != nil {
196
+ return nil , err
197
+ }
198
+ d .buildQueryParams (req .URL )
199
+
200
+ res , err := client .Do (req )
201
+ if err != nil {
202
+ return nil , err
203
+ }
204
+ defer res .Body .Close ()
205
+
206
+ if err := json .NewDecoder (res .Body ).Decode (& d .payload ); err != nil {
207
+ return nil , fmt .Errorf ("unable to parse API response - %s" , err )
208
+ }
209
+
210
+ if res .StatusCode == http .StatusUnauthorized {
211
+ return nil , fmt .Errorf (
212
+ "unauthorized request. Please run the configure command. You can find your API token at %s/my/settings" ,
213
+ config .InferSiteURL (d .apibaseurl ),
214
+ )
215
+ }
216
+ if res .StatusCode != http .StatusOK {
217
+ switch d .payload .Error .Type {
218
+ case "track_ambiguous" :
219
+ return nil , fmt .Errorf (
220
+ "%s: %s" ,
221
+ d .payload .Error .Message ,
222
+ strings .Join (d .payload .Error .PossibleTrackIDs , ", " ),
223
+ )
224
+ default :
225
+ return nil , errors .New (d .payload .Error .Message )
226
+ }
227
+ }
228
+
229
+ return d , nil
230
+ }
231
+
232
+ func (d download ) url () string {
233
+ id := "latest"
234
+ if d .uuid != "" {
235
+ id = d .uuid
236
+ }
237
+ return fmt .Sprintf ("%s/solutions/%s" , d .apibaseurl , id )
238
+ }
239
+
240
+ func (d download ) buildQueryParams (url * netURL.URL ) {
241
+ query := url .Query ()
242
+ if d .slug != "" {
243
+ query .Add ("exercise_id" , d .slug )
244
+ if d .track != "" {
245
+ query .Add ("track_id" , d .track )
246
+ }
247
+ if d .team != "" {
248
+ query .Add ("team_id" , d .team )
249
+ }
250
+ }
251
+ url .RawQuery = query .Encode ()
252
+ }
253
+
254
+ // needsSlugXorUUID checks the presence of slug XOR uuid.
255
+ func (d download ) needsSlugXorUUID () error {
256
+ if d .slug != "" && d .uuid != "" || d .uuid == d .slug {
257
+ return errors .New ("need an --exercise name or a solution --uuid" )
258
+ }
259
+ return nil
260
+ }
261
+
262
+ // needsUserConfigValues checks the presence of required values from the user config.
263
+ func (d download ) needsUserConfigValues () error {
264
+ errMsg := "missing required user config: '%s'"
265
+ if d .token == "" {
266
+ return fmt .Errorf (errMsg , "token" )
267
+ }
268
+ if d .apibaseurl == "" {
269
+ return fmt .Errorf (errMsg , "apibaseurl" )
270
+ }
271
+ if d .workspace == "" {
272
+ return fmt .Errorf (errMsg , "workspace" )
273
+ }
274
+ return nil
275
+ }
276
+
277
+ // needsSlugWhenGivenTrackOrTeam ensures that track/team arguments are also given with a slug.
278
+ // (track/team meaningless when given a uuid).
279
+ func (d download ) needsSlugWhenGivenTrackOrTeam () error {
280
+ if (d .team != "" || d .track != "" ) && d .slug == "" {
281
+ return errors .New ("--track or --team requires --exercise (not --uuid)" )
282
+ }
283
+ return nil
284
+ }
285
+
179
286
type downloadPayload struct {
180
287
Solution struct {
181
288
ID string `json:"id"`
@@ -223,60 +330,6 @@ func (dp downloadPayload) metadata() workspace.ExerciseMetadata {
223
330
}
224
331
}
225
332
226
- // downloadIdentifier is the variable for the URI to initiate an exercise download.
227
- func downloadIdentifier (flags * pflag.FlagSet ) (string , error ) {
228
- uuid , err := flags .GetString ("uuid" )
229
- if err != nil {
230
- return "" , err
231
- }
232
- slug , err := flags .GetString ("exercise" )
233
- if err != nil {
234
- return "" , err
235
- }
236
- if uuid != "" && slug != "" || uuid == slug {
237
- return "" , errors .New ("need an --exercise name or a solution --uuid" )
238
- }
239
-
240
- identifier := "latest"
241
- if uuid != "" {
242
- identifier = uuid
243
- }
244
- return identifier , nil
245
- }
246
-
247
- func addQueryToDownloadRequest (flags * pflag.FlagSet , req * http.Request ) (* http.Request , error ) {
248
- uuid , err := flags .GetString ("uuid" )
249
- if err != nil {
250
- return req , err
251
- }
252
- slug , err := flags .GetString ("exercise" )
253
- if err != nil {
254
- return req , err
255
- }
256
- track , err := flags .GetString ("track" )
257
- if err != nil {
258
- return req , err
259
- }
260
-
261
- team , err := flags .GetString ("team" )
262
- if err != nil {
263
- return req , err
264
- }
265
-
266
- if uuid == "" {
267
- q := req .URL .Query ()
268
- q .Add ("exercise_id" , slug )
269
- if track != "" {
270
- q .Add ("track_id" , track )
271
- }
272
- if team != "" {
273
- q .Add ("team_id" , team )
274
- }
275
- req .URL .RawQuery = q .Encode ()
276
- }
277
- return req , nil
278
- }
279
-
280
333
func setupDownloadFlags (flags * pflag.FlagSet ) {
281
334
flags .StringP ("uuid" , "u" , "" , "the solution UUID" )
282
335
flags .StringP ("track" , "t" , "" , "the track ID" )
0 commit comments