Skip to content

Commit d19480c

Browse files
fix(cmd): validate --format before any network call (CONTRACT §4 spirit) (#39)
Each data-producing subcommand (activity, intraday, measurements, sleep, workouts) was instantiating client.New() and hitting the Withings API before calling validateFormat() on --format. An invalid value like --format obviously-not-a-format would only fail after the HTTP round-trip, so the compat/formats.flagValidationIsHermetic subtest was passing for the wrong reason (network error masking the parse failure under the no-net proxy env). Move validateFormat() to the top of each RunE, before client.New(). Now an unknown --format exits non-zero with an empty stdout and the real "unknown --format" message on stderr, with no auth load and no dial attempt — verified per-subcommand under HTTP_PROXY=127.0.0.1:1. Refs: quantcli/common QUA-30, CONTRACT §4 (output format). Co-authored-by: Terastar-Paperclip <terastar-paperclip@users.noreply.github.com> Co-authored-by: Paperclip <noreply@paperclip.ing>
1 parent 3e6bcfd commit d19480c

5 files changed

Lines changed: 30 additions & 20 deletions

File tree

‎cmd/activity.go‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ var activityCmd = &cobra.Command{
4949
Use: "activity",
5050
Short: "Export daily activity (steps, distance, calories, HR zones)",
5151
RunE: func(cmd *cobra.Command, args []string) error {
52+
// Parse-level flag validation runs before any network call so an
53+
// invalid --format exits non-zero hermetically (CONTRACT §4).
54+
format, err := validateFormat(activityFormatFlag)
55+
if err != nil {
56+
return err
57+
}
5258
since, err := sinceOrDefault(activitySinceFlag, 30)
5359
if err != nil {
5460
return err
@@ -83,10 +89,6 @@ var activityCmd = &cobra.Command{
8389

8490
sort.Slice(all, func(i, j int) bool { return all[i].Date < all[j].Date })
8591

86-
format, err := validateFormat(activityFormatFlag)
87-
if err != nil {
88-
return err
89-
}
9092
switch format {
9193
case "json":
9294
return printJSON(all)

‎cmd/intraday.go‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ spo2_auto, steps and distance; native Withings trackers report steps and HR.
6969
7070
Default window is the last 24h — intraday is dense; wider ranges are slow.`,
7171
RunE: func(cmd *cobra.Command, args []string) error {
72+
// Parse-level flag validation runs before any network call so an
73+
// invalid --format exits non-zero hermetically (CONTRACT §4).
74+
format, err := validateFormat(intradayFormatFlag)
75+
if err != nil {
76+
return err
77+
}
7278
since, err := sinceOrDefault(intradaySinceFlag, 1)
7379
if err != nil {
7480
return err
@@ -124,10 +130,6 @@ Default window is the last 24h — intraday is dense; wider ranges are slow.`,
124130

125131
sort.Slice(all, func(i, j int) bool { return all[i].Timestamp < all[j].Timestamp })
126132

127-
format, err := validateFormat(intradayFormatFlag)
128-
if err != nil {
129-
return err
130-
}
131133
switch format {
132134
case "json":
133135
return printJSON(all)

‎cmd/measurements.go‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ var measurementsCmd = &cobra.Command{
8080
Use: "measurements",
8181
Short: "Export body measurements (weight, body fat, BP, etc.)",
8282
RunE: func(cmd *cobra.Command, args []string) error {
83+
// Parse-level flag validation runs before any network call so an
84+
// invalid --format exits non-zero hermetically (CONTRACT §4).
85+
format, err := validateFormat(measurementsFormatFlag)
86+
if err != nil {
87+
return err
88+
}
8389
since, err := sinceOrDefault(measurementsSinceFlag, 30)
8490
if err != nil {
8591
return err
@@ -131,10 +137,6 @@ var measurementsCmd = &cobra.Command{
131137

132138
sort.Slice(rows, func(i, j int) bool { return rows[i].Date.Before(rows[j].Date) })
133139

134-
format, err := validateFormat(measurementsFormatFlag)
135-
if err != nil {
136-
return err
137-
}
138140
switch format {
139141
case "json":
140142
return printJSON(rows)

‎cmd/sleep.go‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ var sleepCmd = &cobra.Command{
4444
Use: "sleep",
4545
Short: "Export nightly sleep summaries",
4646
RunE: func(cmd *cobra.Command, args []string) error {
47+
// Parse-level flag validation runs before any network call so an
48+
// invalid --format exits non-zero hermetically (CONTRACT §4).
49+
format, err := validateFormat(sleepFormatFlag)
50+
if err != nil {
51+
return err
52+
}
4753
since, err := sinceOrDefault(sleepSinceFlag, 30)
4854
if err != nil {
4955
return err
@@ -110,10 +116,6 @@ var sleepCmd = &cobra.Command{
110116

111117
sort.Slice(all, func(i, j int) bool { return all[i].StartDate < all[j].StartDate })
112118

113-
format, err := validateFormat(sleepFormatFlag)
114-
if err != nil {
115-
return err
116-
}
117119
switch format {
118120
case "json":
119121
return printJSON(all)

‎cmd/workouts.go‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,12 @@ var workoutsCmd = &cobra.Command{
9595
Use: "workouts",
9696
Short: "Export workouts (runs, walks, bikes, etc.)",
9797
RunE: func(cmd *cobra.Command, args []string) error {
98+
// Parse-level flag validation runs before any network call so an
99+
// invalid --format exits non-zero hermetically (CONTRACT §4).
100+
format, err := validateFormat(workoutsFormatFlag)
101+
if err != nil {
102+
return err
103+
}
98104
since, err := sinceOrDefault(workoutsSinceFlag, 90)
99105
if err != nil {
100106
return err
@@ -131,10 +137,6 @@ var workoutsCmd = &cobra.Command{
131137

132138
sort.Slice(all, func(i, j int) bool { return all[i].StartDate < all[j].StartDate })
133139

134-
format, err := validateFormat(workoutsFormatFlag)
135-
if err != nil {
136-
return err
137-
}
138140
switch format {
139141
case "json":
140142
return printJSON(all)

0 commit comments

Comments
 (0)