feat(api): add file listing endpoint

- Add GET /api endpoint to list all markdown files
- Filter to only include .md files
- Return JSON response with files array
- Add comprehensive tests for file listing functionality
This commit is contained in:
2026-02-06 21:23:59 -05:00
parent 2a9e793971
commit a074f5a854
4 changed files with 224 additions and 193 deletions

View File

@@ -38,10 +38,36 @@ func (h *APIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
}
func (h *APIHandler) handleListFiles(w http.ResponseWriter, r *http.Request) {
h.log.Info("GET request for file listing")
files, err := os.ReadDir(h.dataDir)
if err != nil {
h.log.Errorf("Error reading data directory: %v", err)
h.writeError(w, http.StatusInternalServerError, "failed to list files")
return
}
filenames := []string{}
for _, file := range files {
if !file.IsDir() && filepath.Ext(file.Name()) == ".md" {
filenames = append(filenames, file.Name())
}
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string][]string{"files": filenames})
}
func (h *APIHandler) handleGet(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
filename := vars["filename"]
if filename == "" {
h.handleListFiles(w, r)
return
}
h.log.Infof("GET request for file: %s", filename)
filepath := filepath.Join(h.dataDir, filename)

View File

@@ -31,6 +31,7 @@ func NewServer(host string, port int, handler http.Handler, log *logrus.Logger)
func (s *Server) Start() error {
router := mux.NewRouter()
router.Handle("/api", s.handler).Methods("GET")
router.Handle("/api/{filename:.+.md}", s.handler)
router.PathPrefix("/").Handler(http.FileServer(http.Dir("frontend/dist")))

View File

@@ -0,0 +1,141 @@
package main
import (
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"github.com/evanreichard/markdown-editor/internal/api"
"github.com/evanreichard/markdown-editor/internal/logger"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
)
func TestFileListing(t *testing.T) {
dataDir, err := setupTestDir()
if err != nil {
t.Fatalf("Failed to create test dir: %v", err)
}
defer cleanupTestDir(dataDir)
// Create some test files
testFiles := []string{
"file1.md",
"file2.md",
"file3.md",
}
for _, filename := range testFiles {
filepath := filepath.Join(dataDir, filename)
content := "# " + filename + "\n\nContent of " + filename
if err := os.WriteFile(filepath, []byte(content), 0644); err != nil {
t.Fatalf("Failed to create test file %s: %v", filename, err)
}
}
log := logger.NewLogger()
handler := api.NewAPIHandler(dataDir, log)
router := mux.NewRouter()
router.Handle("/api", handler).Methods("GET")
router.Handle("/api/{filename:.+\\.md}", handler)
// Test GET /api (list files)
req := httptest.NewRequest("GET", "/api", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response struct {
Files []string `json:"files"`
}
body, _ := io.ReadAll(w.Body)
assert.NoError(t, json.Unmarshal(body, &response))
// Verify all test files are returned
assert.Len(t, response.Files, 3)
assert.Contains(t, response.Files, "file1.md")
assert.Contains(t, response.Files, "file2.md")
assert.Contains(t, response.Files, "file3.md")
}
func TestFileListingWithNonMarkdownFiles(t *testing.T) {
dataDir, err := setupTestDir()
if err != nil {
t.Fatalf("Failed to create test dir: %v", err)
}
defer cleanupTestDir(dataDir)
// Create test files including non-markdown files
testFiles := []string{
"file1.md",
"file2.txt",
"file3.md",
"file4.log",
}
for _, filename := range testFiles {
filepath := filepath.Join(dataDir, filename)
content := "Content of " + filename
if err := os.WriteFile(filepath, []byte(content), 0644); err != nil {
t.Fatalf("Failed to create test file %s: %v", filename, err)
}
}
log := logger.NewLogger()
handler := api.NewAPIHandler(dataDir, log)
router := mux.NewRouter()
router.Handle("/api", handler).Methods("GET")
router.Handle("/api/{filename:.+\\.md}", handler)
// Test GET /api (list files)
req := httptest.NewRequest("GET", "/api", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response struct {
Files []string `json:"files"`
}
body, _ := io.ReadAll(w.Body)
assert.NoError(t, json.Unmarshal(body, &response))
// Verify only markdown files are returned
assert.Len(t, response.Files, 2)
assert.Contains(t, response.Files, "file1.md")
assert.Contains(t, response.Files, "file3.md")
assert.NotContains(t, response.Files, "file2.txt")
assert.NotContains(t, response.Files, "file4.log")
}
func TestFileListingEmptyDirectory(t *testing.T) {
dataDir, err := setupTestDir()
if err != nil {
t.Fatalf("Failed to create test dir: %v", err)
}
defer cleanupTestDir(dataDir)
log := logger.NewLogger()
handler := api.NewAPIHandler(dataDir, log)
router := mux.NewRouter()
router.Handle("/api", handler).Methods("GET")
router.Handle("/api/{filename:.+\\.md}", handler)
// Test GET /api (list files in empty directory)
req := httptest.NewRequest("GET", "/api", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
assert.Equal(t, http.StatusOK, w.Code)
var response struct {
Files []string `json:"files"`
}
body, _ := io.ReadAll(w.Body)
assert.NoError(t, json.Unmarshal(body, &response))
// Verify empty array is returned
assert.Len(t, response.Files, 0)
}