feat(api): serve the category content field on GET /_journal/api/v1/categories
- add the nullable TEXT column golem15_journal_categories.content (migration 202610090001) - map it as models.Category.Content and emit category content on every categories row - document the categories row shape and the stored-as-is HTML semantics
This commit is contained in:
62
controllers/api/categories_test.go
Normal file
62
controllers/api/categories_test.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"git.golem15.com/golem15/sm-journal-plugin/models"
|
||||
)
|
||||
|
||||
func sortedCategoryKeys(row map[string]any) []string {
|
||||
keys := make([]string, 0, len(row))
|
||||
for k := range row {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
func TestSerializeCategoryNodeContent(t *testing.T) {
|
||||
html := "<h2>Acme</h2>\n<p>Pillar copy</p>\n"
|
||||
var parentID uint = 1
|
||||
root := models.Category{ID: 1, Name: "News", Slug: "news", Content: &html}
|
||||
child := models.Category{ID: 2, Name: "Quiet", Slug: "quiet", ParentID: &parentID}
|
||||
byParent := map[uint][]models.Category{1: {child}}
|
||||
counts := map[uint]int64{1: 2}
|
||||
|
||||
row := serializeCategoryNode(&root, byParent, counts, true)
|
||||
if row == nil {
|
||||
t.Fatal("root row is nil")
|
||||
}
|
||||
if got, ok := row["content"].(string); !ok || got != html {
|
||||
t.Fatalf("root content = %#v, want %q", row["content"], html)
|
||||
}
|
||||
wantRoot := []string{"children", "content", "description", "id", "name", "nest_depth", "parent_id", "post_count", "slug"}
|
||||
if got := sortedCategoryKeys(row); !reflect.DeepEqual(got, wantRoot) {
|
||||
t.Fatalf("root keys = %v, want %v", got, wantRoot)
|
||||
}
|
||||
kids, ok := row["children"].([]map[string]any)
|
||||
if !ok || len(kids) != 1 {
|
||||
t.Fatalf("children = %#v, want one row", row["children"])
|
||||
}
|
||||
v, present := kids[0]["content"]
|
||||
if !present || v != nil {
|
||||
t.Fatalf("child content = %#v (present %v), want nil", v, present)
|
||||
}
|
||||
wantChild := []string{"content", "description", "id", "name", "nest_depth", "parent_id", "post_count", "slug"}
|
||||
if got := sortedCategoryKeys(kids[0]); !reflect.DeepEqual(got, wantChild) {
|
||||
t.Fatalf("child keys = %v, want %v", got, wantChild)
|
||||
}
|
||||
|
||||
listed := serializeCategoryNode(&root, byParent, counts, false)
|
||||
if listed == nil {
|
||||
t.Fatal("root omitted without include_empty")
|
||||
}
|
||||
if _, has := listed["children"]; has {
|
||||
t.Fatalf("empty child listed without include_empty: %#v", listed["children"])
|
||||
}
|
||||
if got, ok := listed["content"].(string); !ok || got != html {
|
||||
t.Fatalf("root content without include_empty = %#v, want %q", listed["content"], html)
|
||||
}
|
||||
}
|
||||
@@ -195,6 +195,10 @@ func serializeCategoryNode(c *models.Category, byParent map[uint][]models.Catego
|
||||
if c.Description != nil {
|
||||
desc = *c.Description
|
||||
}
|
||||
var content any
|
||||
if c.Content != nil {
|
||||
content = *c.Content
|
||||
}
|
||||
var parent any
|
||||
if c.ParentID != nil {
|
||||
parent = *c.ParentID
|
||||
@@ -208,6 +212,7 @@ func serializeCategoryNode(c *models.Category, byParent map[uint][]models.Catego
|
||||
"name": c.Name,
|
||||
"slug": c.Slug,
|
||||
"description": desc,
|
||||
"content": content,
|
||||
"parent_id": parent,
|
||||
"nest_depth": depth,
|
||||
"post_count": postCount,
|
||||
|
||||
Reference in New Issue
Block a user