Skip to content

Commit 448ce88

Browse files
author
Katrina Owen
authored
Merge pull request #821 from jdsutherland/refactor-download
Extract download type from download command
2 parents c3525cf + b336b6a commit 448ce88

File tree

1 file changed

+152
-99
lines changed

1 file changed

+152
-99
lines changed

cmd/download.go

Lines changed: 152 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -54,66 +54,29 @@ func runDownload(cfg config.Config, flags *pflag.FlagSet, args []string) error {
5454
return err
5555
}
5656

57-
identifier, err := downloadIdentifier(flags)
57+
download, err := newDownload(flags, usrCfg)
5858
if err != nil {
5959
return err
6060
}
61-
url := fmt.Sprintf("%s/solutions/%s", usrCfg.GetString("apibaseurl"), identifier)
6261

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()
7764

78-
res, err := client.Do(req)
79-
if err != nil {
65+
if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
8066
return err
8167
}
8268

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 {
10770
return err
10871
}
10972

110-
err = metadata.Write(dir)
73+
client, err := api.NewClient(usrCfg.GetString("token"), usrCfg.GetString("apibaseurl"))
11174
if err != nil {
11275
return err
11376
}
11477

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)
11780
parsedURL, err := netURL.ParseRequestURI(unparsedURL)
11881

11982
if err != nil {
@@ -176,6 +139,150 @@ func runDownload(cfg config.Config, flags *pflag.FlagSet, args []string) error {
176139
return nil
177140
}
178141

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+
179286
type downloadPayload struct {
180287
Solution struct {
181288
ID string `json:"id"`
@@ -223,60 +330,6 @@ func (dp downloadPayload) metadata() workspace.ExerciseMetadata {
223330
}
224331
}
225332

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-
280333
func setupDownloadFlags(flags *pflag.FlagSet) {
281334
flags.StringP("uuid", "u", "", "the solution UUID")
282335
flags.StringP("track", "t", "", "the track ID")

0 commit comments

Comments
 (0)