package api import ( "encoding/csv" "fmt" "net/http" "sort" "strconv" "github.com/gorilla/mux" "github.com/rs/zerolog/log" ) type FlatMaps []map[string]any func (maps FlatMaps) GetHeaders() (res []string) { keys := map[string]bool{} for _, m := range maps { for k, _ := range m { if _, ok := keys[k]; !ok { keys[k] = true res = append(res, k) } } } sort.Strings(res) return } func (maps FlatMaps) GetValues() (res [][]string) { headers := maps.GetHeaders() for _, m := range maps { line := []string{} for _, k := range headers { if v, ok := m[k]; ok && v != nil { line = append(line, fmt.Sprint(v)) } else { line = append(line, "") } } res = append(res, line) } return } func (h APIHandler) CacheExport(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) cacheid := vars["cacheid"] d, err := h.cache.Get(cacheid) if err != nil { log.Error().Err(err).Msg("Error getting cache") w.WriteHeader(http.StatusNotFound) return } if data, ok := d.([]any); ok { flatmaps := FlatMaps{} for _, v := range data { fm := map[string]any{} flatten("", v.(map[string]any), fm) flatmaps = append(flatmaps, fm) } w.Header().Set("Content-Type", "text/csv") w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=export-%s.csv", cacheid)) c := csv.NewWriter(w) c.Write(flatmaps.GetHeaders()) c.WriteAll(flatmaps.GetValues()) return } w.WriteHeader(http.StatusNotFound) } func flatten(prefix string, src map[string]any, dest map[string]any) { if len(prefix) > 0 { prefix += "." } for k, v := range src { switch child := v.(type) { case map[string]any: flatten(prefix+k, child, dest) case []any: for i := 0; i < len(child); i++ { dest[prefix+k+"."+strconv.Itoa(i)] = child[i] } default: // log.Trace().Str("key", prefix+k).Any("value", v).Msg("") dest[prefix+k] = v } } }