From a4698e179a4696f06c4e92dc018dc36ba1ba20bb Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 28 May 2023 16:19:42 +0200 Subject: [PATCH] feat: add DELETE /users/@me/flags/{id} --- backend/db/flags.go | 30 +++++++++++++++++++++++++++++- backend/routes/user/flags.go | 30 ++++++++++++++++++++++++++++++ backend/routes/user/routes.go | 2 +- 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/backend/db/flags.go b/backend/db/flags.go index 1393a5a..6f3458b 100644 --- a/backend/db/flags.go +++ b/backend/db/flags.go @@ -55,6 +55,7 @@ const ( const ( ErrInvalidFlagID = errors.Sentinel("invalid flag ID") + ErrFlagNotFound = errors.Sentinel("flag not found") ) func (db *DB) AccountFlags(ctx context.Context, userID xid.ID) (fs []PrideFlag, err error) { @@ -70,6 +71,23 @@ func (db *DB) AccountFlags(ctx context.Context, userID xid.ID) (fs []PrideFlag, return NotNull(fs), nil } +func (db *DB) UserFlag(ctx context.Context, flagID xid.ID) (f PrideFlag, err error) { + sql, args, err := sq.Select("*").From("pride_flags").Where("id = ?", flagID).ToSql() + if err != nil { + return f, errors.Wrap(err, "building query") + } + + err = pgxscan.Get(ctx, db, &f, sql, args...) + if err != nil { + if errors.Cause(err) == pgx.ErrNoRows { + return f, ErrFlagNotFound + } + + return f, errors.Wrap(err, "executing query") + } + return f, nil +} + func (db *DB) UserFlags(ctx context.Context, userID xid.ID) (fs []UserFlag, err error) { sql, args, err := sq.Select("u.id", "u.flag_id", "f.user_id", "f.hash", "f.name", "f.description"). From("user_flags AS u"). @@ -241,7 +259,17 @@ func (db *DB) WriteFlag(ctx context.Context, flagID xid.ID, flag *bytes.Buffer) } func (db *DB) DeleteFlag(ctx context.Context, flagID xid.ID, hash string) error { - err := db.minio.RemoveObject(ctx, db.minioBucket, "/flags/"+flagID.String()+"/"+hash+".webp", minio.RemoveObjectOptions{}) + sql, args, err := sq.Delete("pride_flags").Where("id = ?", flagID).ToSql() + if err != nil { + return errors.Wrap(err, "building sql") + } + + _, err = db.Exec(ctx, sql, args...) + if err != nil { + return errors.Wrap(err, "executing query") + } + + err = db.minio.RemoveObject(ctx, db.minioBucket, "/flags/"+flagID.String()+"/"+hash+".webp", minio.RemoveObjectOptions{}) if err != nil { return errors.Wrap(err, "deleting flag") } diff --git a/backend/routes/user/flags.go b/backend/routes/user/flags.go index 2ad6ebd..77654f3 100644 --- a/backend/routes/user/flags.go +++ b/backend/routes/user/flags.go @@ -203,5 +203,35 @@ func (s *Server) patchUserFlag(w http.ResponseWriter, r *http.Request) error { } func (s *Server) deleteUserFlag(w http.ResponseWriter, r *http.Request) error { + ctx := r.Context() + claims, _ := server.ClaimsFromContext(ctx) + + if !claims.TokenWrite { + return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"} + } + + flagID, err := xid.FromString(chi.URLParam(r, "flagID")) + if err != nil { + return server.APIError{Code: server.ErrNotFound, Details: "Invalid flag ID"} + } + + flag, err := s.DB.UserFlag(ctx, flagID) + if err != nil { + if err == db.ErrFlagNotFound { + return server.APIError{Code: server.ErrNotFound, Details: "Flag not found"} + } + + return errors.Wrap(err, "getting flag object") + } + if flag.UserID != claims.UserID { + return server.APIError{Code: server.ErrNotFound, Details: "Flag not found"} + } + + err = s.DB.DeleteFlag(ctx, flag.ID, flag.Hash) + if err != nil { + return errors.Wrap(err, "deleting flag") + } + + render.NoContent(w, r) return nil } diff --git a/backend/routes/user/routes.go b/backend/routes/user/routes.go index c5998f1..fd6e9a2 100644 --- a/backend/routes/user/routes.go +++ b/backend/routes/user/routes.go @@ -33,7 +33,7 @@ func Mount(srv *server.Server, r chi.Router) { r.Get("/@me/flags", server.WrapHandler(s.getUserFlags)) r.Post("/@me/flags", server.WrapHandler(s.postUserFlag)) r.Patch("/@me/flags/{flagID}", server.WrapHandler(s.patchUserFlag)) - r.Delete("/@me/flags", server.WrapHandler(s.deleteUserFlag)) + r.Delete("/@me/flags/{flagID}", server.WrapHandler(s.deleteUserFlag)) }) }) }