mirror of
https://github.com/notherealmarco/WASAPhoto.git
synced 2025-03-14 06:06:15 +01:00
Add get followers
This commit is contained in:
parent
b89296c249
commit
e8047c77a0
10 changed files with 162 additions and 17 deletions
|
@ -11,6 +11,8 @@ func (rt *_router) Handler() http.Handler {
|
|||
|
||||
rt.router.PUT("/users/:user_id/username", rt.wrap(rt.UpdateUsername))
|
||||
|
||||
rt.router.GET("/users/:user_id/followers", rt.wrap(rt.GetFollowers))
|
||||
|
||||
rt.router.GET("/", rt.getHelloWorld)
|
||||
rt.router.GET("/context", rt.wrap(rt.getContextReply))
|
||||
|
||||
|
|
|
@ -33,17 +33,32 @@ func (b *BearerAuth) GetToken() string {
|
|||
return b.token
|
||||
}
|
||||
|
||||
func (b *BearerAuth) Authorized(db database.AppDatabase) (bool, error) {
|
||||
func (b *BearerAuth) Authorized(db database.AppDatabase) (reqcontext.AuthStatus, error) {
|
||||
// this is the way we manage authorization, the bearer token is the user id
|
||||
state, err := db.UserExists(b.token)
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
return reqcontext.UNAUTHORIZED, err
|
||||
}
|
||||
return state, nil
|
||||
|
||||
if state {
|
||||
return reqcontext.AUTHORIZED, nil
|
||||
}
|
||||
return reqcontext.UNAUTHORIZED, nil
|
||||
}
|
||||
|
||||
func (b *BearerAuth) UserAuthorized(db database.AppDatabase, uid string) (reqcontext.AuthStatus, error) {
|
||||
|
||||
// If uid is not a valid user, return USER_NOT_FOUND
|
||||
user_exists, err := db.UserExists(uid)
|
||||
|
||||
if err != nil {
|
||||
return reqcontext.UNAUTHORIZED, err
|
||||
}
|
||||
if !user_exists {
|
||||
return reqcontext.USER_NOT_FOUND, nil
|
||||
}
|
||||
|
||||
if b.token == uid {
|
||||
auth, err := b.Authorized(db)
|
||||
|
||||
|
@ -51,11 +66,7 @@ func (b *BearerAuth) UserAuthorized(db database.AppDatabase, uid string) (reqcon
|
|||
return -1, err
|
||||
}
|
||||
|
||||
if auth {
|
||||
return reqcontext.AUTHORIZED, nil
|
||||
} else {
|
||||
return reqcontext.UNAUTHORIZED, nil
|
||||
}
|
||||
return auth, nil
|
||||
}
|
||||
return reqcontext.FORBIDDEN, nil
|
||||
}
|
||||
|
|
|
@ -34,5 +34,10 @@ func SendAuthorizationError(f func(db database.AppDatabase, uid string) (reqcont
|
|||
w.WriteHeader(http.StatusForbidden)
|
||||
return false
|
||||
}
|
||||
// requested user is not found -> 404 as the resource is not found
|
||||
if auth == reqcontext.USER_NOT_FOUND {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
|
49
service/api/followers.go
Normal file
49
service/api/followers.go
Normal file
|
@ -0,0 +1,49 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/notherealmarco/WASAPhoto/service/api/helpers"
|
||||
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
|
||||
)
|
||||
|
||||
func (rt *_router) GetFollowers(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||
|
||||
uid := ps.ByName("user_id")
|
||||
|
||||
if !helpers.VerifyUserOrNotFound(rt.db, uid, w) {
|
||||
return
|
||||
}
|
||||
|
||||
followers, err := rt.db.GetUserFollowers(uid)
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError) // todo: is not ok, maybe let's use a helper
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("content-type", "application/json")
|
||||
err = json.NewEncoder(w).Encode(&followers)
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError) // todo: is not ok
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (rt *_router) PutFollow(w http.ResponseWriter, r *http.Request, ps httprouter.Params, ctx reqcontext.RequestContext) {
|
||||
|
||||
uid := ps.ByName("user_id")
|
||||
followed := ps.ByName("follower_uid")
|
||||
|
||||
err := rt.db.FollowUser(uid, followed)
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError) // todo: is not ok, maybe let's use a helper
|
||||
return
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusNoContent) // todo: change to 204 also in API spec
|
||||
}
|
42
service/api/helpers/api-helpers.go
Normal file
42
service/api/helpers/api-helpers.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package helpers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"github.com/notherealmarco/WASAPhoto/service/database"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func DecodeJsonOrBadRequest(r io.Reader, w http.ResponseWriter, v interface{}, l logrus.FieldLogger) bool {
|
||||
|
||||
err := json.NewDecoder(r).Decode(v)
|
||||
if err != nil {
|
||||
SendInternalError(err, "Error decoding json", w, l)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func VerifyUserOrNotFound(db database.AppDatabase, uid string, w http.ResponseWriter) bool {
|
||||
|
||||
user_exists, err := db.UserExists(uid)
|
||||
|
||||
if err != nil {
|
||||
SendInternalError(err, "Error verifying user existence", w, nil)
|
||||
return false
|
||||
}
|
||||
|
||||
if !user_exists {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func SendInternalError(err error, description string, w http.ResponseWriter, l logrus.FieldLogger) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
l.WithError(err).Error(description)
|
||||
w.Write([]byte(description)) // todo: maybe in json. But it's not important to send the full error to the client
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/notherealmarco/WASAPhoto/service/api/authorization"
|
||||
"github.com/notherealmarco/WASAPhoto/service/api/helpers"
|
||||
"github.com/notherealmarco/WASAPhoto/service/api/reqcontext"
|
||||
"github.com/notherealmarco/WASAPhoto/service/structures"
|
||||
)
|
||||
|
@ -17,14 +17,18 @@ func (rt *_router) UpdateUsername(w http.ResponseWriter, r *http.Request, ps htt
|
|||
return
|
||||
}
|
||||
var req structures.UserDetails
|
||||
err := json.NewDecoder(r.Body).Decode(&req) //todo: capire se serve close
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusBadRequest) // todo: move to DecodeOrBadRequest helper
|
||||
if !helpers.DecodeJsonOrBadRequest(r.Body, w, &req, rt.baseLogger) {
|
||||
return
|
||||
}
|
||||
|
||||
err = rt.db.UpdateUsername(uid, req.Name)
|
||||
//err := json.NewDecoder(r.Body).Decode(&req) //todo: capire se serve close
|
||||
|
||||
//if err != nil {
|
||||
// w.WriteHeader(http.StatusBadRequest) // todo: move to DecodeOrBadRequest helper
|
||||
// return
|
||||
//}
|
||||
|
||||
err := rt.db.UpdateUsername(uid, req.Name)
|
||||
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError) // todo: is not ok, maybe let's use a helper
|
||||
|
|
|
@ -18,6 +18,7 @@ const (
|
|||
AUTHORIZED = 0
|
||||
UNAUTHORIZED = 1
|
||||
FORBIDDEN = 2
|
||||
USER_NOT_FOUND = 3
|
||||
)
|
||||
|
||||
// RequestContext is the context of the request, for request-dependent parameters
|
||||
|
|
|
@ -34,6 +34,8 @@ import (
|
|||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/notherealmarco/WASAPhoto/service/structures"
|
||||
)
|
||||
|
||||
// AppDatabase is the high level interface for the DB
|
||||
|
@ -42,6 +44,7 @@ type AppDatabase interface {
|
|||
GetUserID(name string) (string, error)
|
||||
UpdateUsername(uid, name string) error
|
||||
CreateUser(name string) (string, error)
|
||||
GetUserFollowers(uid string) ([]structures.UIDName, error)
|
||||
FollowUser(uid string, follow string) error
|
||||
UnfollowUser(uid string, unfollow string) error
|
||||
BanUser(uid string, ban string) error
|
||||
|
|
|
@ -3,6 +3,7 @@ package database
|
|||
import (
|
||||
"github.com/gofrs/uuid"
|
||||
"github.com/notherealmarco/WASAPhoto/service/database/db_errors"
|
||||
"github.com/notherealmarco/WASAPhoto/service/structures"
|
||||
)
|
||||
|
||||
//Check if user exists and if exists return the user id by username
|
||||
|
@ -44,6 +45,28 @@ func (db *appdbimpl) UpdateUsername(uid string, name string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// Get user followers
|
||||
func (db *appdbimpl) GetUserFollowers(uid string) ([]structures.UIDName, error) {
|
||||
rows, err := db.c.Query(`SELECT "follower", "user.name" FROM "follows", "users"
|
||||
WHERE "follows.follower" = "users.uid"
|
||||
AND "followed" = ?`, uid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var followers []structures.UIDName = make([]structures.UIDName, 0)
|
||||
for rows.Next() {
|
||||
var uid string
|
||||
var name string
|
||||
err = rows.Scan(&uid, &name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
followers = append(followers, structures.UIDName{UID: uid, Name: name})
|
||||
}
|
||||
return followers, nil
|
||||
}
|
||||
|
||||
// Follow a user
|
||||
func (db *appdbimpl) FollowUser(uid string, follow string) error {
|
||||
_, err := db.c.Exec(`INSERT INTO "follows" ("follower", "followed") VALUES (?, ?)`, uid, follow)
|
||||
|
|
|
@ -3,3 +3,8 @@ package structures
|
|||
type UserDetails struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type UIDName struct {
|
||||
UID string `json:"user_id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue