Skip to content

Commit

Permalink
add delete content api
Browse files Browse the repository at this point in the history
  • Loading branch information
sunwei committed Jan 18, 2025
1 parent 1619a50 commit ab8c84c
Show file tree
Hide file tree
Showing 12 changed files with 310 additions and 18 deletions.
19 changes: 15 additions & 4 deletions internal/domain/content/entity/content.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ func (c *Content) GetContent(contentType, id, status string) ([]byte, error) {
return c.Repo.GetContent(GetNamespace(contentType, status), id)
}

func (c *Content) GetContentByHash(contentType, hash, status string) ([]byte, error) {
idb, err := c.Repo.GetIdByHash(GetNamespace(contentType, status), hash)
if err != nil {
return nil, err
}
id := string(idb)
return c.GetContent(contentType, id, status)
}

func (c *Content) DeleteContent(contentType, id, status string) error {
data, err := c.GetContent(contentType, id, status)
if err != nil {
Expand All @@ -62,10 +71,12 @@ func (c *Content) DeleteContent(contentType, id, status string) error {
}

ns := GetNamespace(contentType, status)
if err := c.Repo.DeleteContent(
ns,
id,
cti.(content.Sluggable).ItemSlug()); err != nil {

hash := ""
if ctiHash, ok := cti.(content.Hashable); ok {
hash = ctiHash.ItemHash()
}
if err := c.Repo.DeleteContent(ns, id, cti.(content.Sluggable).ItemSlug(), hash); err != nil {
return err
}

Expand Down
3 changes: 2 additions & 1 deletion internal/domain/content/repository/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ type Repository interface {
AllContent(namespace string) [][]byte
ContentByPrefix(namespace, prefix string) ([][]byte, error)
GetContent(namespace string, id string) ([]byte, error)
DeleteContent(namespace string, id string, slug string) error
DeleteContent(namespace string, id string, slug string, hash string) error

NextContentId(ns string) (uint64, error)
CheckSlugForDuplicate(namespace string, slug string) (string, error)
GetIdByHash(namespace string, hash string) ([]byte, error)

PutSortedContent(namespace string, m map[string][]byte) error

Expand Down
4 changes: 4 additions & 0 deletions internal/domain/content/valueobject/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ func (s *Post) MarshalEditor() ([]byte, error) {
// String defines the display name of a Song in the CMS list-view
func (s *Post) String() string { return s.Title }

func (s *Post) SetHash() {
s.Hash = Hash([]string{s.Content})
}

// Create implements api.Createable, and allows external POST requests from clients
// to add content as long as the request contains the json tag names of the Song
// struct fields, and is multipart encoded
Expand Down
11 changes: 11 additions & 0 deletions internal/domain/content/valueobject/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ type Resource struct {

Name string `json:"name"`
Asset string `json:"asset"`
Size string `json:"size"`
}

// MarshalEditor writes a buffer of html to edit a Song within the CMS
Expand All @@ -30,6 +31,12 @@ func (s *Resource) MarshalEditor() ([]byte, error) {
"placeholder": "Upload the asset here",
}),
},
editor.Field{
View: editor.File("Size", s, map[string]string{
"label": "Size",
"placeholder": "Upload the size here",
}),
},
)

if err != nil {
Expand All @@ -42,6 +49,10 @@ func (s *Resource) MarshalEditor() ([]byte, error) {
// String defines the display name of a Song in the CMS list-view
func (s *Resource) String() string { return s.Name }

func (s *Resource) SetHash() {
s.Hash = Hash([]string{s.Name, s.Size})
}

// Create implements api.Createable, and allows external POST requests from clients
// to add content as long as the request contains the json tag names of the Song
// struct fields, and is multipart encoded
Expand Down
18 changes: 17 additions & 1 deletion internal/interfaces/api/database/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ func (d *Database) GetContent(namespace string, id string) ([]byte, error) {
})
}

func (d *Database) DeleteContent(namespace string, id string, slug string) error {
func (d *Database) DeleteContent(namespace string, id string, slug string, hash string) error {
if err := d.getStore(namespace).Delete(&item{bucket: namespace, key: id}); err != nil {
return err
}
Expand All @@ -111,6 +111,12 @@ func (d *Database) DeleteContent(namespace string, id string, slug string) error
return err
}

if hash != "" {
if err := d.getStore(namespace).RemoveIndex(fmt.Sprintf("%s:%s", namespace, hash)); err != nil {
return err
}
}

return nil
}

Expand Down Expand Up @@ -176,6 +182,12 @@ func (d *Database) NewContent(ci any, data []byte) error {
if err := d.getStore(ns).SetIndex(newKeyValueItem(ciSlug.ItemSlug(), fmt.Sprintf("%s:%d", ns, id))); err != nil {
return err
}
ciHash, ok := ci.(content.Hashable)
if ok {
if err := d.getStore(ns).SetIndex(newKeyValueItem(fmt.Sprintf("%s:%s", ns, ciHash.ItemHash()), fmt.Sprintf("%d", id))); err != nil {
return err
}
}

return nil
}
Expand Down Expand Up @@ -241,6 +253,10 @@ func (d *Database) CheckSlugForDuplicate(namespace string, slug string) (string,
return d.getStore(namespace).CheckSlugForDuplicate(slug)
}

func (d *Database) GetIdByHash(namespace string, hash string) ([]byte, error) {
return d.getStore(namespace).GetIndex(fmt.Sprintf("%s:%s", namespace, hash))
}

func (d *Database) Query(namespace string, opts db.QueryOptions) (int, [][]byte) {
return d.getStore(namespace).Query(namespace, opts)
}
159 changes: 151 additions & 8 deletions internal/interfaces/api/handler/handlecontent.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,35 @@ func (s *Handler) postContent(res http.ResponseWriter, req *http.Request) {

post := p()

cid := req.PostForm.Get("id")
isUpdating := cid != "-1"
isCreating := !isUpdating

if isUpdating {
ep := p()
data, err := s.contentApp.GetContent(t, cid, "")
if err != nil {
s.log.Errorf("Error getting content: %v with id %s", err, cid)
res.WriteHeader(http.StatusNotFound)
}
err = json.Unmarshal(data, ep)
if err != nil {
if err := s.res.err500(res); err != nil {
s.log.Errorf("Error response err 500: %s", err)
}
return
}
if sort, ok := ep.(content.Sortable); ok {
req.PostForm.Set("timestamp", timestamp.TimeToString(sort.Time()))
}
if identifier, ok := ep.(content.Identifiable); ok {
req.PostForm.Set("uuid", identifier.UniqueID().String())
}
if slug, ok := ep.(content.Sluggable); ok {
req.PostForm.Set("slug", slug.ItemSlug())
}
}

ext, ok := post.(content.Createable)
if !ok {
s.log.Printf("Attempt to create non-createable type: %s from %s", t, req.RemoteAddr)
Expand All @@ -232,7 +261,9 @@ func (s *Handler) postContent(res http.ResponseWriter, req *http.Request) {
}

ts := timestamp.Now()
req.PostForm.Set("timestamp", ts)
if isCreating {
req.PostForm.Set("timestamp", ts)
}
req.PostForm.Set("updated", ts)

urlPaths, err := s.StoreFiles(req)
Expand Down Expand Up @@ -310,15 +341,24 @@ func (s *Handler) postContent(res http.ResponseWriter, req *http.Request) {

req.PostForm.Set("namespace", t)

id, err := s.contentApp.NewContent(t, req.PostForm)
if err != nil {
s.log.Errorf("Error calling SetContent: %v", err)
res.WriteHeader(http.StatusInternalServerError)
return
if isCreating {
id, err := s.contentApp.NewContent(t, req.PostForm)
if err != nil {
s.log.Errorf("Error calling SetContent: %v", err)
res.WriteHeader(http.StatusInternalServerError)
return
}
cid = id
} else {
if err = s.contentApp.UpdateContent(t, req.PostForm); err != nil {
s.log.Errorf("Error updating content: %s", err)
res.WriteHeader(http.StatusInternalServerError)
return
}
}

// set the target in the context so user can get saved value from db in hook
ctx := context.WithValue(req.Context(), "target", fmt.Sprintf("%s:%s", t, id))
ctx := context.WithValue(req.Context(), "target", fmt.Sprintf("%s:%s", t, cid))
req = req.WithContext(ctx)

err = hook.AfterSave(res, req)
Expand All @@ -344,7 +384,7 @@ func (s *Handler) postContent(res http.ResponseWriter, req *http.Request) {
} else {
spec = "public"
data = map[string]interface{}{
"id": id,
"id": cid,
"status": spec,
"type": t,
}
Expand All @@ -370,3 +410,106 @@ func (s *Handler) postContent(res http.ResponseWriter, req *http.Request) {
return
}
}

func (s *Handler) DeleteContentHandler(res http.ResponseWriter, req *http.Request) {
if req.Method != http.MethodPost {
res.WriteHeader(http.StatusMethodNotAllowed)
return
}

id := req.PostForm.Get("id")
t := req.PostForm.Get("type")
status := req.PostForm.Get("status")
ct := t

if id == "" || t == "" {
res.WriteHeader(http.StatusBadRequest)
return
}

p, ok := s.contentApp.AllContentTypes()[ct]
if !ok {
s.log.Printf("Type %s not supported", t)
res.WriteHeader(http.StatusBadRequest)
return
}

post := p()
hook, ok := post.(content.Hookable)
if !ok {
s.log.Printf("Type %s does not implement item.Hookable or embed item.Item.", t)
res.WriteHeader(http.StatusBadRequest)
return
}

data, err := s.contentApp.GetContent(t, id, status)
if err != nil {
res.WriteHeader(http.StatusInternalServerError)
s.log.Printf("Error in db.Content %s:%s: %s", t, id, err)
return
}
if data == nil {
res.WriteHeader(http.StatusNotFound)
s.log.Printf("Content not found: %s %s %s", t, id, status)
return
}

err = json.Unmarshal(data, post)
if err != nil {
log.Println("Error unmarshalling ", t, "=", id, err, " Hooks will be called on a zero-value.")
}

reject := req.URL.Query().Get("reject")
if reject == "true" {
err = hook.BeforeReject(res, req)
if err != nil {
log.Println("Error running BeforeReject method in deleteHandler for:", t, err)
return
}
}

err = hook.BeforeAdminDelete(res, req)
if err != nil {
log.Println("Error running BeforeAdminDelete method in deleteHandler for:", t, err)
return
}

err = hook.BeforeDelete(res, req)
if err != nil {
log.Println("Error running BeforeDelete method in deleteHandler for:", t, err)
return
}

err = s.contentApp.DeleteContent(t, id, status)
if err != nil {
s.log.Errorf("Error in db.Content %s:%s: %s", t, id, err)
res.WriteHeader(http.StatusInternalServerError)
return
}

if err := s.adminApp.InvalidateCache(); err != nil {
s.log.Errorf("Error invalidating cache: %s", err)
}

err = hook.AfterDelete(res, req)
if err != nil {
s.log.Errorln("Error running AfterDelete method in deleteHandler for:", t, err)
return
}

err = hook.AfterAdminDelete(res, req)
if err != nil {
s.log.Errorln("Error running AfterAdminDelete method in deleteHandler for:", t, err)
return
}

if reject == "true" {
err = hook.AfterReject(res, req)
if err != nil {
s.log.Errorln("Error running AfterReject method in deleteHandler for:", t, err)
return
}
}

res.WriteHeader(http.StatusOK)
}
Loading

0 comments on commit ab8c84c

Please sign in to comment.