From f41b34aa5c9a7f0af65ce327d8a7f8126fcd56f5 Mon Sep 17 00:00:00 2001 From: Bel LaPointe Date: Wed, 9 Feb 2022 12:30:12 -0700 Subject: [PATCH] almost there --- spike/review/reinvent/ezmded/server/server.go | 129 ++++++++++++++---- .../reinvent/ezmded/server/server_test.go | 32 +++-- spike/review/reinvent/ezmded/server/tree.go | 26 +++- 3 files changed, 142 insertions(+), 45 deletions(-) diff --git a/spike/review/reinvent/ezmded/server/server.go b/spike/review/reinvent/ezmded/server/server.go index d86564a..0cdd2b3 100644 --- a/spike/review/reinvent/ezmded/server/server.go +++ b/spike/review/reinvent/ezmded/server/server.go @@ -10,6 +10,7 @@ import ( "local/router" "local/simpleserve/simpleserve" "net/http" + "net/url" "os" "path" "regexp" @@ -72,26 +73,8 @@ func (server *Server) tryCatchHttpHandler(handler func(http.ResponseWriter, *htt } func (server *Server) apiV0TreeHandler(w http.ResponseWriter, r *http.Request) error { - _, pretty := r.URL.Query()["pretty"] - if pretty { - return server.apiV0TreePrettyHandler(w, r) - } - return server.apiV0TreePlainHandler(w, r) -} - -func (server *Server) apiV0TreePrettyHandler(w http.ResponseWriter, r *http.Request) error { tree := server.tree() - //branches, err := tree.GetPretty() - branches, err := tree.GetRoot() - if err != nil { - return err - } - return json.NewEncoder(w).Encode(branches) -} - -func (server *Server) apiV0TreePlainHandler(w http.ResponseWriter, r *http.Request) error { - tree := server.tree() - branches, err := tree.GetRoot() + branches, err := tree.GetRootMeta() if err != nil { return err } @@ -106,7 +89,8 @@ func ensureAndWrite(p string, b []byte) error { } func (server *Server) apiV0MediaHandler(w http.ResponseWriter, r *http.Request) error { - id, err := server.postContentHandler(path.Dir(server.diskMediaPath("id")), w, r) + id := uuid.New().String() + id, err := server.postContentHandler(server.diskMediaPath(id), w, r) if err != nil { return err } @@ -157,11 +141,10 @@ func (server *Server) getContentHandler(filePath string, w http.ResponseWriter, return nil } -func (server *Server) postContentHandler(fileDir string, w http.ResponseWriter, r *http.Request) (string, error) { +func (server *Server) postContentHandler(filePath string, w http.ResponseWriter, r *http.Request) (string, error) { if r.Method != http.MethodPost { return "", errors.New("not found") } - id := strings.Split(uuid.New().String(), "-")[0] if strings.HasPrefix(r.Header.Get("Content-Type"), "multipart/form-data") { kb := int64(1 << 10) mb := kb << 10 @@ -179,7 +162,7 @@ func (server *Server) postContentHandler(fileDir string, w http.ResponseWriter, if h, ok := infos[0].Header["Content-Type"]; ok { ext = path.Base(h[0]) } - id += "." + ext + filePath += "." + ext f, err := infos[0].Open() if err != nil { return "", err @@ -193,8 +176,7 @@ func (server *Server) postContentHandler(fileDir string, w http.ResponseWriter, } return "", fmt.Errorf("parse form: %+v", r.PostForm) } - filePath := path.Join(fileDir, id) - return id, server.putContentHandler(filePath, w, r) + return filePath, server.putContentHandler(filePath, w, r) } func (server *Server) putContentHandler(filePath string, w http.ResponseWriter, r *http.Request) error { @@ -210,8 +192,8 @@ func (server *Server) rootHandler(w http.ResponseWriter, r *http.Request) error return server.getContentHandler(path.Join(server.root, "index.html"), w, r) } -func (server *Server) tree() *Tree { - return nil +func (server *Server) tree() Tree { + return NewTree(path.Join(server.root, "files")) } func (server *Server) diskMediaPath(id string) string { @@ -223,7 +205,80 @@ func (server *Server) apiV0FilesHandler(w http.ResponseWriter, r *http.Request) } func (server *Server) apiV0FilesIDHandler(w http.ResponseWriter, r *http.Request) error { - return errors.New("not impl") + switch r.Method { + case http.MethodGet: + return server.apiV0FilesIDGetHandler(w, r) + case http.MethodPut: + return server.apiV0FilesIDPutHandler(w, r) + case http.MethodDelete: + return server.apiV0FilesIDDelHandler(w, r) + } + http.NotFound(w, r) + return nil +} + +func (server *Server) apiV0FilesIDGetHandler(w http.ResponseWriter, r *http.Request) error { + id := server.fileId(r) + + leaf, err := server.tree().Get(id) + if os.IsNotExist(err) { + http.NotFound(w, r) + return nil + } else if err != nil { + return err + } + + w.Header().Set("Title", leaf.Title) + _, err = w.Write([]byte(leaf.Content)) + return err +} + +func (server *Server) apiV0FilesIDDelHandler(w http.ResponseWriter, r *http.Request) error { + id := server.fileId(r) + + leaf, err := server.tree().Get(id) + if os.IsNotExist(err) { + return nil + } else if err != nil { + return err + } + leaf.Deleted = true + + return server.tree().Put(id, leaf) +} + +func (server *Server) urlFileId(id []string) string { + if len(id) == 0 { + return "" + } + for i := 1; i < len(id); i++ { + id[0] = strings.Join([]string{id[0], url.PathEscape(id[i])}, "/") + } + return id[0] +} + +func (server *Server) fileId(r *http.Request) []string { + return strings.Split(strings.TrimPrefix(r.URL.Path, "/api/v0/files/"), "/") +} + +func (server *Server) apiV0FilesIDPutHandler(w http.ResponseWriter, r *http.Request) error { + id := server.fileId(r) + + leaf, err := server.tree().Get(id) + if os.IsNotExist(err) { + } else if err != nil { + return err + } + b, err := ioutil.ReadAll(r.Body) + if err != nil { + return err + } + + leaf.Content = string(b) + leaf.Title = r.Header.Get("Title") + leaf.Deleted = false + + return server.tree().Put(id, leaf) } func (server *Server) apiV0SearchHandler(w http.ResponseWriter, r *http.Request) error { @@ -245,5 +300,21 @@ func (server *Server) apiV0SearchHandler(w http.ResponseWriter, r *http.Request) w.Write([]byte(`[]`)) return nil } - return errors.New("not impl") + tree, err := server.tree().GetRoot() + if err != nil { + return err + } + result := []string{} + if err := tree.ForEach(func(id []string, leaf Leaf) error { + for _, pattern := range patterns { + if !pattern.MatchString(leaf.Content) && !pattern.MatchString(fmt.Sprint(id)) { + return nil + } + } + result = append(result, server.urlFileId(id)) + return nil + }); err != nil { + return err + } + return json.NewEncoder(w).Encode(result) } diff --git a/spike/review/reinvent/ezmded/server/server_test.go b/spike/review/reinvent/ezmded/server/server_test.go index a6447a1..1975783 100644 --- a/spike/review/reinvent/ezmded/server/server_test.go +++ b/spike/review/reinvent/ezmded/server/server_test.go @@ -1,6 +1,13 @@ package main -/* +import ( + "net/http" + "net/http/httptest" + "path" + "strings" + "testing" +) + func TestServerRoutes(t *testing.T) { server := NewServer(t.TempDir()) if err := server.Routes(); err != nil { @@ -11,15 +18,16 @@ func TestServerRoutes(t *testing.T) { t.Fatal(err) } ensureAndWrite(server.diskMediaPath("delid"), []byte("hi")) - ensureAndWrite(server.diskFilePath("getfid"), []byte("getfid body")) - ensureAndWrite(server.diskFilePath("putfid"), []byte("initial putfid body")) - ensureAndWrite(server.diskFilePath("delfid"), []byte("delfid body")) - ensureAndWrite(path.Join(server.root, "index.html"), []byte("mom")) - if err := server.tree().Put("putfid", Branch{}); err != nil { + + tree := server.tree() + if err := tree.Put([]string{"getfid"}, Leaf{Title: "", Content: "getfid body"}); err != nil { t.Fatal(err) } - server.tree().Put("delfid", Branch{Title: "delfid title"}) - server.tree().Put("getfid", Branch{Title: "getfid title"}) + tree.Put([]string{"putfid"}, Leaf{Title: "putfid title", Content: "initial putfid body"}) + tree.Put([]string{"delfid"}, Leaf{Title: "delfid title", Content: "delfid body"}) + t.Log(tree.GetRoot()) + + ensureAndWrite(path.Join(server.root, "index.html"), []byte("mom")) cases := map[string]struct { path string @@ -37,15 +45,10 @@ func TestServerRoutes(t *testing.T) { method: http.MethodGet, want: `["getfid"]`, }, - "v0: tree: get pretty": { - path: "/api/v0/tree?pretty", - method: http.MethodGet, - want: `"getfid":{"Children":{`, - }, "v0: tree: get": { path: "/api/v0/tree", method: http.MethodGet, - want: `":{"Title":"getfid title",`, + want: `"Title":"Untitled",`, }, "v0: media: post": { path: "/api/v0/media", @@ -101,6 +104,7 @@ func TestServerRoutes(t *testing.T) { } } +/* func TestServerPutTreeGetFile(t *testing.T) { server := NewServer(t.TempDir()) if err := server.Routes(); err != nil { diff --git a/spike/review/reinvent/ezmded/server/tree.go b/spike/review/reinvent/ezmded/server/tree.go index f9af7c9..4ec8c0f 100644 --- a/spike/review/reinvent/ezmded/server/tree.go +++ b/spike/review/reinvent/ezmded/server/tree.go @@ -9,8 +9,8 @@ import ( ) type Branch struct { - Leaf Leaf - Branches map[string]Branch + Leaf Leaf `json:"Leaf,omitempty"` + Branches map[string]Branch `json:"Branches,omitempty"` } func (branch Branch) IsZero() bool { @@ -29,6 +29,24 @@ func (branch Branch) Find(baseId string) ([]string, bool) { return nil, false } +func (branch Branch) ForEach(foo func([]string, Leaf) error) error { + return branch.forEach([]string{}, foo) +} + +func (branch Branch) forEach(preid []string, foo func([]string, Leaf) error) error { + if err := foo(preid, branch.Leaf); err != nil { + return err + } + postid := append(preid, "") + for id, child := range branch.Branches { + postid[len(postid)-1] = id + if err := child.forEach(postid, foo); err != nil { + return err + } + } + return nil +} + type Leaf struct { Title string Deleted bool @@ -39,6 +57,9 @@ func (base Leaf) Merge(updated Leaf) Leaf { if updated.Title != "" { base.Title = updated.Title } + if base.Title == "" { + base.Title = "Untitled" + } base.Deleted = updated.Deleted base.Content = updated.Content return base @@ -55,6 +76,7 @@ func NewTree(root string) Tree { func (tree Tree) WithRoot(root string) Tree { tree.root = root + tree.cachedRoot = Branch{} return tree }