diff --git a/service/api/api-handler.go b/service/api/api-handler.go index 60741cc..ebcd0b6 100644 --- a/service/api/api-handler.go +++ b/service/api/api-handler.go @@ -18,6 +18,7 @@ func (rt *_router) Handler() http.Handler { rt.router.DELETE("/users/:user_id/followers/:follower_uid", rt.wrap(rt.DeleteFollow)) rt.router.GET("/users/:user_id/following", rt.wrap(rt.GetFollowersFollowing)) + rt.router.GET("/users/:user_id/bans", rt.wrap(rt.GetUserBans)) rt.router.PUT("/users/:user_id/bans/:ban_uid", rt.wrap(rt.PutBan)) rt.router.DELETE("/users/:user_id/bans/:ban_uid", rt.wrap(rt.DeleteBan)) diff --git a/service/api/bans.go b/service/api/bans.go index 6bc8d11..e613b2d 100644 --- a/service/api/bans.go +++ b/service/api/bans.go @@ -1,6 +1,7 @@ package api import ( + "encoding/json" "net/http" "github.com/julienschmidt/httprouter" @@ -10,6 +11,45 @@ import ( "github.com/notherealmarco/WASAPhoto/service/database" ) +func (rt *_router) GetUserBans(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) { + + // Get user id + uid := ps.ByName("user_id") + + if !authorization.SendAuthorizationError(ctx.Auth.UserAuthorized, uid, rt.db, w, rt.baseLogger, http.StatusNotFound) { + // A user should not be able to see other users' bans + return + } + + // Get limits, or use default values + start_index, limit, err := helpers.GetLimits(r.URL.Query()) + + if err != nil { + // Send error if the limits are specified but invalid + helpers.SendBadRequest(w, "Invalid start_index or limit value", rt.baseLogger) + return + } + + // Get bans + // We don't need to check if the user exists, because the authorization middleware already did that + bans, err := rt.db.GetUserBans(uid, start_index, limit) + + if err != nil { + helpers.SendInternalError(err, "Database error: GetUserBans", w, rt.baseLogger) + return + } + + // Return ban list + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) // is it needed? + json.NewEncoder(w).Encode(bans) + + if err != nil { + helpers.SendInternalError(err, "Error encoding json", w, rt.baseLogger) + return + } +} + func (rt *_router) PutBan(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) { uid := ps.ByName("user_id") diff --git a/service/api/get-userprofile.go b/service/api/get-userprofile.go index 848a33b..b0c393e 100644 --- a/service/api/get-userprofile.go +++ b/service/api/get-userprofile.go @@ -44,3 +44,40 @@ func (rt *_router) GetUserProfile(w http.ResponseWriter, r *http.Request, ps htt return } } + +func (rt *_router) GetUserPhotos(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) { + + // Get user id + uid := ps.ByName("user_id") + + if !authorization.SendErrorIfNotLoggedIn(ctx.Auth.Authorized, rt.db, w, rt.baseLogger) || + !helpers.SendNotFoundIfBanned(rt.db, ctx.Auth.GetUserID(), uid, w, rt.baseLogger) { + return + } + + // Get limits, or use default values + start_index, limit, err := helpers.GetLimits(r.URL.Query()) + + if err != nil { + helpers.SendBadRequest(w, "Invalid start_index or limit value", rt.baseLogger) + return + } + + // Get user photos + photos, err := rt.db.GetUserPhotos(uid, start_index, limit) + + if err != nil { + helpers.SendInternalError(err, "Database error: GetUserPhotos", w, rt.baseLogger) + return + } + + // Return user photos + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + err = json.NewEncoder(w).Encode(photos) + + if err != nil { + helpers.SendInternalError(err, "Error encoding json", w, rt.baseLogger) + return + } +} diff --git a/service/database/database.go b/service/database/database.go index 09b4e2b..688d3f3 100644 --- a/service/database/database.go +++ b/service/database/database.go @@ -56,6 +56,7 @@ type AppDatabase interface { BanUser(uid string, ban string) (QueryResult, error) UnbanUser(uid string, unban string) (QueryResult, error) IsBanned(uid string, banner string) (bool, error) + GetUserBans(uid string, start_index int, limit int) (*[]structures.UIDName, error) PostPhoto(uid string) (DBTransaction, int64, error) DeletePhoto(uid string, photo int64) (bool, error) @@ -64,7 +65,8 @@ type AppDatabase interface { LikePhoto(uid string, photo int64, liker_uid string) (QueryResult, error) UnlikePhoto(uid string, photo int64, liker_uid string) (QueryResult, error) - GetUserProfile(uid string) (QueryResult, *structures.UserProfile, error) // should support limits + GetUserProfile(uid string) (QueryResult, *structures.UserProfile, error) + GetUserPhotos(uid string, start_index int, limit int) (*[]structures.UserPhoto, error) GetUserStream(uid string, start_index int, limit int) (*[]structures.Photo, error) GetComments(uid string, photo_id int64, requesting_uid string, start_index int, offset int) (QueryResult, *[]structures.Comment, error) diff --git a/service/database/db-profile.go b/service/database/db-profile.go index 35acb98..b1e34e5 100644 --- a/service/database/db-profile.go +++ b/service/database/db-profile.go @@ -39,7 +39,8 @@ func (db *appdbimpl) GetUserProfile(uid string) (QueryResult, *structures.UserPr return ERR_INTERNAL, nil, err } - photos, err := db.getUserPhotos(uid) + var photos int64 + err = db.c.QueryRow(`SELECT COUNT(*) FROM "photos" WHERE "photos"."user" = ?`, uid).Scan(&following) if err != nil { return ERR_INTERNAL, nil, err @@ -54,7 +55,7 @@ func (db *appdbimpl) GetUserProfile(uid string) (QueryResult, *structures.UserPr }, nil } -func (db *appdbimpl) getUserPhotos(uid string) (*[]structures.UserPhoto, error) { +func (db *appdbimpl) GetUserPhotos(uid string, start_index int, limit int) (*[]structures.UserPhoto, error) { // Get photos rows, err := db.c.Query(`SELECT "p"."id", "p"."date", @@ -65,9 +66,16 @@ func (db *appdbimpl) getUserPhotos(uid string) (*[]structures.UserPhoto, error) ( SELECT COUNT(*) AS "comments" FROM "comments" AS "c" WHERE "c"."photo" = "p"."id" + ), + EXISTS ( + SELECT * FROM "likes" AS "l" + WHERE "l"."photo_id" = "p"."id" + AND "l"."user" = ? ) FROM "photos" AS "p" - WHERE "p"."user" = ?`, uid) + WHERE "p"."user" = ? + OFFSET ? + LIMIT ?`, uid, uid, start_index, limit) if err != nil { // Return the error return nil, err @@ -78,7 +86,7 @@ func (db *appdbimpl) getUserPhotos(uid string) (*[]structures.UserPhoto, error) for rows.Next() { // If there is a next row, we create an instance of Photo and add it to the slice var photo structures.UserPhoto - err = rows.Scan(&photo.ID, &photo.Date, &photo.Likes, &photo.Comments) + err = rows.Scan(&photo.ID, &photo.Date, &photo.Likes, &photo.Comments, &photo.Liked) if err != nil { // Return the error return nil, err diff --git a/service/database/db-users.go b/service/database/db-users.go index cda6867..a87768b 100644 --- a/service/database/db-users.go +++ b/service/database/db-users.go @@ -235,6 +235,23 @@ func (db *appdbimpl) IsBanned(uid string, banner string) (bool, error) { return cnt > 0, nil } +func (db *appdbimpl) GetUserBans(uid string, start_index int, limit int) (*[]structures.UIDName, error) { + + rows, err := db.c.Query(`SELECT "ban", "user"."name" FROM "bans", "users" + WHERE "bans"."ban" = "users"."uid" + AND "bans"."user" = ? + OFFSET ? + LIMIT ?`, uid, start_index, limit) + + bans, err := db.uidNameQuery(rows, err) + + if err != nil { + return nil, err + } + + return bans, nil +} + // Search by name func (db *appdbimpl) SearchByName(name string, requesting_uid string, start_index int, limit int) (*[]structures.UIDName, error) { diff --git a/service/structures/api-structures.go b/service/structures/api-structures.go index 594d44a..e255fa1 100644 --- a/service/structures/api-structures.go +++ b/service/structures/api-structures.go @@ -27,6 +27,7 @@ type Photo struct { //todo: move to structures Likes int64 `json:"likes"` Comments int64 `json:"comments"` Date string `json:"date"` + Liked bool `json:"liked"` } type UserPhoto struct { @@ -34,12 +35,13 @@ type UserPhoto struct { Likes int64 `json:"likes"` Comments int64 `json:"comments"` Date string `json:"date"` + Liked bool `json:"liked"` } type UserProfile struct { - UID string `json:"user_id"` - Name string `json:"name"` - Following int64 `json:"following"` - Followers int64 `json:"followers"` - Photos *[]UserPhoto `json:"photos"` //todo: check json names + UID string `json:"user_id"` + Name string `json:"name"` + Following int64 `json:"following"` + Followers int64 `json:"followers"` + Photos int64 `json:"photos"` //todo: check json names }