carlos / platform Public

carlos analytics: count_from, one PUT for both set flags, gofmt

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Paul Campbell pushed by paul@keymail.dev 19579c8c538a02361f0c9052c5f2fc8725f1e6c5 parent 66a0759
3 files changed, +103 −21
  • analytics_cmd.go +32 −15
  • analytics_cmd_test.go +70 −6
  • apimode.go +1 −0
diff --git a/analytics_cmd.go b/analytics_cmd.go
index f7c1f2d5d..27f781de9 100644
--- a/analytics_cmd.go
+++ b/analytics_cmd.go
@@ -38,25 +38,23 @@ func runAnalytics(args []string) {
app2 := resolvedApp(sess, acct, *app, analyticsUsage)
ctx := context.Background()
- if *setCounting != "" {
- if *setCounting != "edge" && *setCounting != "off" {
- log.Fatalf("analytics: --set-count wants edge or off")
+ if *setCounting != "" || *setDoor != "" {
+ body, err := analyticsSetBody(*setCounting, *setDoor)
+ if err != nil {
+ log.Fatalf("analytics: %v", err)
}
- if err := sess.telemetryPut(ctx, acct, app2, map[string]any{"analytics": map[string]string{"count": *setCounting}}); err != nil {
+ if err := sess.telemetryPut(ctx, acct, app2, map[string]any{"analytics": body}); err != nil {
log.Fatalf("analytics: %v", err)
}
- fmt.Fprintf(stdout, "Analytics counting for %s is now %s.\n", app2, *setCounting)
- return
- }
-
- if *setDoor != "" {
- if *setDoor != "on" && *setDoor != "off" {
- log.Fatalf("analytics: --set-door wants on or off")
+ // Build summary of what was set
+ var parts []string
+ if *setCounting != "" {
+ parts = append(parts, fmt.Sprintf("counting %s", *setCounting))
}
- if err := sess.telemetryPut(ctx, acct, app2, map[string]any{"analytics": map[string]string{"door": *setDoor}}); err != nil {
- log.Fatalf("analytics: %v", err)
+ if *setDoor != "" {
+ parts = append(parts, fmt.Sprintf("door %s", *setDoor))
}
- fmt.Fprintf(stdout, "Analytics door for %s is now %s.\n", app2, *setDoor)
+ fmt.Fprintf(stdout, "Analytics for %s: %s.\n", app2, joinOr(parts, ""))
return
}
@@ -67,8 +65,27 @@ func runAnalytics(args []string) {
}
printAnalytics(stdout, res)
if !res.On {
- mutedf(stdout, "analytics is off for this app — turn it on with `carlos analytics --app %s --set-count edge`", app2)
+ mutedf(stdout, "analytics is off for this app (from the %s) — turn it on with `carlos analytics --app %s --set-count edge`", res.CountFrom, app2)
+ }
+}
+
+// analyticsSetBody builds the analytics settings map from the given flag values,
+// validating them first. Returns error for invalid values.
+func analyticsSetBody(count, door string) (map[string]string, error) {
+ if count != "" && count != "edge" && count != "off" {
+ return nil, fmt.Errorf("--set-count wants edge or off")
+ }
+ if door != "" && door != "on" && door != "off" {
+ return nil, fmt.Errorf("--set-door wants on or off")
+ }
+ body := make(map[string]string)
+ if count != "" {
+ body["count"] = count
+ }
+ if door != "" {
+ body["door"] = door
}
+ return body, nil
}
// printAnalytics renders the analytics summary: a headline line with totals,
diff --git a/analytics_cmd_test.go b/analytics_cmd_test.go
index ac7b9fff7..ada1248b2 100644
--- a/analytics_cmd_test.go
+++ b/analytics_cmd_test.go
@@ -50,12 +50,12 @@ func TestPrintAnalytics(t *testing.T) {
output := buf.String()
checks := []string{
- "5", // PV count
- "4", // Visitors count
- "daily visitors, summed", // text from headline
- "/pricing", // page
- "news.ycombinator.com", // referer
- "signup", // event
+ "5", // PV count
+ "4", // Visitors count
+ "daily visitors, summed", // text from headline
+ "/pricing", // page
+ "news.ycombinator.com", // referer
+ "signup", // event
}
for _, check := range checks {
@@ -64,3 +64,67 @@ func TestPrintAnalytics(t *testing.T) {
}
}
}
+
+func TestAnalyticsSetFlags(t *testing.T) {
+ tests := []struct {
+ name string
+ count string
+ door string
+ wantErr bool
+ want map[string]string
+ }{
+ {
+ name: "both flags",
+ count: "edge",
+ door: "on",
+ want: map[string]string{"count": "edge", "door": "on"},
+ },
+ {
+ name: "count only",
+ count: "off",
+ want: map[string]string{"count": "off"},
+ },
+ {
+ name: "door only",
+ door: "off",
+ want: map[string]string{"door": "off"},
+ },
+ {
+ name: "neither",
+ want: map[string]string{},
+ },
+ {
+ name: "invalid count",
+ count: "invalid",
+ wantErr: true,
+ },
+ {
+ name: "invalid door",
+ door: "maybe",
+ wantErr: true,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := analyticsSetBody(tt.count, tt.door)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("analyticsSetBody() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if err != nil {
+ return
+ }
+ if len(got) != len(tt.want) {
+ t.Errorf("analyticsSetBody() got %v, want %v", got, tt.want)
+ return
+ }
+ for k, v := range tt.want {
+ if got[k] != v {
+ t.Errorf("analyticsSetBody() got %v, want %v", got, tt.want)
+ return
+ }
+ }
+ })
+ }
+}
diff --git a/apimode.go b/apimode.go
index 2608f204f..beb5fa821 100644
--- a/apimode.go
+++ b/apimode.go
@@ -2890,6 +2890,7 @@ type apiAnalyticsResponse struct {
To string `json:"to"`
On bool `json:"on"`
Count string `json:"count"`
+ CountFrom string `json:"count_from"`
PV int64 `json:"pv"`
Visitors int64 `json:"visitors"`
Bytes int64 `json:"bytes"`