summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-14 10:01:43 +0300
committerPaul Buetow <paul@buetow.org>2026-04-14 10:01:43 +0300
commit4d9004ab31a6064af741fbb73758bd8844964c6f (patch)
tree38a7f09840b6cd8774ba5b4ce24b373d4cc1de26
parent385cc685ab8f5312225342c66d29aaa3bf008a45 (diff)
z2: HTTP report query aliases and Gemtext Content-Type
Accept Category, Metric, and OutputFormat as alternate query keys alongside category, metric, output-format. Serve Gemtext reports with text/gemini; assert Content-Type in daemon tests. Builds on y2 daemon GET /report reusing ParseReportQuery and WriteReports. Made-with: Cursor
-rw-r--r--internal/daemon/daemon.go2
-rw-r--r--internal/daemon/daemon_test.go42
-rw-r--r--internal/goprecords/report.go18
-rw-r--r--internal/goprecords/report_test.go13
4 files changed, 71 insertions, 4 deletions
diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go
index 4e2402f..a4b1a3e 100644
--- a/internal/daemon/daemon.go
+++ b/internal/daemon/daemon.go
@@ -101,6 +101,8 @@ func reportContentType(f goprecords.OutputFormat) string {
switch f {
case goprecords.FormatMarkdown:
return "text/markdown; charset=utf-8"
+ case goprecords.FormatGemtext:
+ return "text/gemini; charset=utf-8"
default:
return "text/plain; charset=utf-8"
}
diff --git a/internal/daemon/daemon_test.go b/internal/daemon/daemon_test.go
index 38878fa..b140c9e 100644
--- a/internal/daemon/daemon_test.go
+++ b/internal/daemon/daemon_test.go
@@ -54,6 +54,9 @@ func TestReport(t *testing.T) {
if res.StatusCode != http.StatusOK {
t.Fatalf("status %d", res.StatusCode)
}
+ if ct := res.Header.Get("Content-Type"); ct != "text/plain; charset=utf-8" {
+ t.Fatalf("Content-Type %q", ct)
+ }
b, err := io.ReadAll(res.Body)
if err != nil {
t.Fatal(err)
@@ -63,6 +66,45 @@ func TestReport(t *testing.T) {
}
}
+func TestReportQueryAliases(t *testing.T) {
+ fixtures := filepath.Join("..", "..", "fixtures")
+ srv := httptest.NewServer(Handler(fixtures))
+ defer srv.Close()
+ res, err := http.Get(srv.URL + "/report?Category=Host&Metric=Uptime&limit=2&OutputFormat=Markdown")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer res.Body.Close()
+ if res.StatusCode != http.StatusOK {
+ t.Fatalf("status %d", res.StatusCode)
+ }
+ if ct := res.Header.Get("Content-Type"); ct != "text/markdown; charset=utf-8" {
+ t.Fatalf("Content-Type %q", ct)
+ }
+ b, _ := io.ReadAll(res.Body)
+ body := string(b)
+ if !strings.Contains(body, "# Top") || !strings.Contains(body, "```") {
+ t.Fatalf("expected markdown heading and fence, got %q", b)
+ }
+}
+
+func TestReportGemtextContentType(t *testing.T) {
+ fixtures := filepath.Join("..", "..", "fixtures")
+ srv := httptest.NewServer(Handler(fixtures))
+ defer srv.Close()
+ res, err := http.Get(srv.URL + "/report?output-format=Gemtext&limit=2")
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer res.Body.Close()
+ if res.StatusCode != http.StatusOK {
+ t.Fatalf("status %d", res.StatusCode)
+ }
+ if ct := res.Header.Get("Content-Type"); ct != "text/gemini; charset=utf-8" {
+ t.Fatalf("Content-Type %q", ct)
+ }
+}
+
func TestReportBadQuery(t *testing.T) {
srv := httptest.NewServer(Handler(t.TempDir()))
defer srv.Close()
diff --git a/internal/goprecords/report.go b/internal/goprecords/report.go
index 939f0e9..b0403e7 100644
--- a/internal/goprecords/report.go
+++ b/internal/goprecords/report.go
@@ -72,9 +72,10 @@ func (rf *ReportFlags) Parse() (ReportConfig, error) {
// ParseReportQuery builds a ReportConfig from URL query parameters using the
// same names and defaults as RegisterReportFlags (category, metric, limit,
-// output-format, all, include-kernel, stats-order).
+// output-format, all, include-kernel, stats-order). It also accepts Category,
+// Metric, and OutputFormat as alternate keys (same values as the CLI).
func ParseReportQuery(q url.Values) (ReportConfig, error) {
- catStr := q.Get("category")
+ catStr := firstQuery(q, "category", "Category")
if catStr == "" {
catStr = "Host"
}
@@ -82,7 +83,7 @@ func ParseReportQuery(q url.Values) (ReportConfig, error) {
if err != nil {
return ReportConfig{}, err
}
- metStr := q.Get("metric")
+ metStr := firstQuery(q, "metric", "Metric")
if metStr == "" {
metStr = "Uptime"
}
@@ -98,7 +99,7 @@ func ParseReportQuery(q url.Values) (ReportConfig, error) {
}
limit = uint(v)
}
- outStr := q.Get("output-format")
+ outStr := firstQuery(q, "output-format", "OutputFormat")
if outStr == "" {
outStr = "Plaintext"
}
@@ -131,6 +132,15 @@ func ParseReportQuery(q url.Values) (ReportConfig, error) {
}, nil
}
+func firstQuery(q url.Values, keys ...string) string {
+ for _, k := range keys {
+ if v := q.Get(k); v != "" {
+ return v
+ }
+ }
+ return ""
+}
+
func parseQueryBool(s string) (bool, error) {
switch strings.ToLower(strings.TrimSpace(s)) {
case "true", "1", "yes":
diff --git a/internal/goprecords/report_test.go b/internal/goprecords/report_test.go
index c068ce9..d6b884d 100644
--- a/internal/goprecords/report_test.go
+++ b/internal/goprecords/report_test.go
@@ -346,6 +346,19 @@ func TestParseReportQuery(t *testing.T) {
if err == nil {
t.Fatal("expected error")
}
+
+ q = url.Values{
+ "Category": []string{"KernelName"},
+ "Metric": []string{"Score"},
+ "OutputFormat": []string{"Gemtext"},
+ }
+ cfg, err = ParseReportQuery(q)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if cfg.Category != CategoryKernelName || cfg.Metric != MetricScore || cfg.OutputFormat != FormatGemtext {
+ t.Fatalf("PascalCase keys: %+v", cfg)
+ }
}
func hostName(i int) string {