Compare commits

..

4 Commits

Author SHA1 Message Date
bel
1324376399 also accpt text/tsv 2024-04-13 09:52:53 -06:00
bel
e58fa50656 accept Accept:text/csv 2024-04-13 09:48:28 -06:00
bel
9bfbcf2d70 wip text/csv 2024-04-13 09:29:00 -06:00
bel
847cd83fd5 extract into writeJSON 2024-04-13 09:26:02 -06:00
2 changed files with 115 additions and 8 deletions

74
main.go
View File

@@ -11,6 +11,7 @@ import (
"net" "net"
"net/http" "net/http"
"os/signal" "os/signal"
"sort"
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
@@ -97,7 +98,7 @@ func newHandlerGetAPIV1Messages(cfg Config) http.HandlerFunc {
return return
} }
json.NewEncoder(w).Encode(map[string]any{"messages": messages}) encodeResponse(w, r, map[string]any{"messages": messages})
} }
} }
@@ -119,7 +120,7 @@ func newHandlerGetAPIV1Threads(cfg Config) http.HandlerFunc {
return return
} }
json.NewEncoder(w).Encode(map[string]any{"threads": threads}) encodeResponse(w, r, map[string]any{"threads": threads})
} }
} }
@@ -137,7 +138,7 @@ func newHandlerGetAPIV1ThreadsThread(cfg Config) http.HandlerFunc {
return return
} }
json.NewEncoder(w).Encode(map[string]any{"thread": map[string]any{"messages": messages}}) encodeResponse(w, r, map[string]any{"thread": messages})
} }
} }
@@ -168,7 +169,7 @@ func handlerPostAPIV1EventsSlackInitialize(w http.ResponseWriter, r *http.Reques
return return
} }
json.NewEncoder(w).Encode(map[string]any{"challenge": challenge.Challenge}) encodeResponse(w, r, map[string]any{"challenge": challenge.Challenge})
} }
func _newHandlerPostAPIV1EventsSlack(cfg Config) http.HandlerFunc { func _newHandlerPostAPIV1EventsSlack(cfg Config) http.HandlerFunc {
@@ -243,3 +244,68 @@ func parseSince(s string) (time.Time, error) {
return time.Time{}, fmt.Errorf("failed to parse since=%q", s) return time.Time{}, fmt.Errorf("failed to parse since=%q", s)
} }
func encodeResponse(w http.ResponseWriter, r *http.Request, v interface{}) error {
if strings.Contains(r.Header.Get("Accept"), "text/csv") {
return encodeCSVResponse(w, v)
}
if strings.Contains(r.Header.Get("Accept"), "text/tsv") {
return encodeTSVResponse(w, v)
}
return encodeJSONResponse(w, v)
}
func encodeJSONResponse(w http.ResponseWriter, v interface{}) error {
return json.NewEncoder(w).Encode(v)
}
func encodeTSVResponse(w http.ResponseWriter, v interface{}) error {
return encodeSVResponse(w, v, "\t")
}
func encodeCSVResponse(w http.ResponseWriter, v interface{}) error {
return encodeSVResponse(w, v, ",")
}
func encodeSVResponse(w http.ResponseWriter, v interface{}, delim string) error {
b, err := json.Marshal(v)
if err != nil {
return err
}
var data map[string][]map[string]json.RawMessage
if err := json.Unmarshal(b, &data); err != nil {
return err
}
var objects []map[string]json.RawMessage
for k := range data {
objects = data[k]
}
fields := []string{}
for i := range objects {
for k := range objects[i] {
b, _ := json.Marshal(k)
fields = append(fields, string(b))
}
break
}
sort.Strings(fields)
w.Write([]byte(strings.Join(fields, delim)))
w.Write([]byte("\n"))
for _, object := range objects {
for j, field := range fields {
json.Unmarshal([]byte(field), &field)
if j > 0 {
w.Write([]byte(delim))
}
w.Write(object[field])
}
w.Write([]byte("\n"))
}
return nil
}

View File

@@ -3,6 +3,7 @@ package main
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/csv"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
@@ -139,16 +140,56 @@ func TestRun(t *testing.T) {
} }
var result struct { var result struct {
Thread struct { Thread []Message
Messages []Message
}
} }
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
t.Fatal(err) t.Fatal(err)
} else if len(result.Thread.Messages) != 1 { } else if len(result.Thread) != 1 {
t.Fatal(result.Thread) t.Fatal(result.Thread)
} else { } else {
t.Logf("%+v", result) t.Logf("%+v", result)
} }
}) })
t.Run("CSV GET /api/v1/threads/1712911957.023359", func(t *testing.T) {
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/threads/1712911957.023359", u), nil)
if err != nil {
t.Fatal(err)
}
req.Header.Set("Accept", "text/csv")
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
b, _ := io.ReadAll(resp.Body)
t.Fatalf("(%d) %s", resp.StatusCode, b)
}
dec := csv.NewReader(resp.Body)
var lastLine []string
for {
line, err := dec.Read()
if err == io.EOF {
break
} else if err != nil {
t.Error(err)
}
if lastLine == nil {
} else if len(lastLine) != len(line) {
t.Errorf("last line had %v elements but this line has %v", len(lastLine), len(line))
}
t.Logf("%+v", line)
lastLine = line
}
if lastLine == nil {
t.Error("no lines found")
}
})
} }