-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #23 from GenerateNU/following_crud
Following crud completed
- Loading branch information
Showing
8 changed files
with
384 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,4 +71,4 @@ backend-ngrok: | |
# Test the backend | ||
.PHONY: backend-test | ||
backend-test: | ||
cd backend | ||
cd backend |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
package controllers | ||
|
||
import ( | ||
"backend/src/models" | ||
"backend/src/services" | ||
"net/http" | ||
"strconv" | ||
|
||
"github.com/gin-gonic/gin" | ||
) | ||
|
||
////////////////////////////// Types + Constructors///////////////////////// | ||
|
||
type FollowingController struct { | ||
followingService *services.FollowingService | ||
} | ||
|
||
func NewFollowingController(followingService *services.FollowingService) *FollowingController { | ||
return &FollowingController{ | ||
followingService: followingService, | ||
} | ||
} | ||
|
||
///////////////////////Read////////////////////////////////////////////////// | ||
|
||
// GetAllFollowings godoc | ||
// | ||
// @Summary Gets all followings relations | ||
// @Description Returns all followings relations as objects | ||
// @ID get-all-followings | ||
// @Tags followings | ||
// @Produce json | ||
// @Success 200 {object} []models.Followings | ||
// @Failure 404 {string} string "Failed to fetch followers: 404 Error" | ||
// @Router /api/following/ [get] | ||
func (fol *FollowingController) GetAllFollowings(c *gin.Context) { | ||
followings, err := fol.followingService.GetAllFollowings() | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch users", "msg": err}) | ||
return | ||
} | ||
|
||
c.JSON(http.StatusOK, followings) | ||
} | ||
|
||
// GetTimeline godoc | ||
// | ||
// @Summary Gets all users followed by an input user | ||
// @ID get-all-user-followings | ||
// @Tags user-followings | ||
// @Produce json | ||
// @Success 200 {object} []models.Users | ||
// @Failure 404 {string} string "Failed to fetch followers: 404 Error" | ||
// @Router /api/following/ [get] | ||
func (fol *FollowingController) GetTimeline(c *gin.Context) { | ||
user, err := strconv.ParseUint(c.Param("follower_user_id"), 10, 64) | ||
if err != nil { | ||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid follower_user_id"}) | ||
return | ||
} | ||
followings, err := fol.followingService.GetTimeline(uint(user)) | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch users"}) | ||
return | ||
} | ||
|
||
c.JSON(http.StatusOK, followings) | ||
} | ||
|
||
// GetFollowers godoc | ||
// | ||
// @Summary Gets all users following an input user | ||
// @ID get-all-user-followers | ||
// @Tags user-followings | ||
// @Produce json | ||
// @Param followed_user_id path uint true "ID of the user being followed" | ||
// @Success 200 {object} []models.Users | ||
// @Failure 404 {string} string "Failed to fetch followers: 404 Error" | ||
// @Router /api/followers/{followed_user_id} [get] | ||
func (fol *FollowingController) GetFollowers(c *gin.Context) { | ||
user, err := strconv.ParseUint(c.Param("following_user_id"), 10, 64) | ||
if err != nil { | ||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid following_user_id"}) | ||
return | ||
} | ||
followings, err := fol.followingService.GetFollowers(uint(user)) | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch users"}) | ||
return | ||
} | ||
|
||
c.JSON(http.StatusOK, followings) | ||
} | ||
|
||
//////////////////////////////////////////Create//////////////////////////////////////// | ||
|
||
// CreateFollowings godoc | ||
// | ||
// @Summary Creates a Followings relation | ||
// @ID create-followings | ||
// @Tags followings | ||
// @Accept json | ||
// @Produce json | ||
// @Param request body CreateFollowingsRequest true "Request body for creating a Followings relation" | ||
// @Success 200 {string} string "Following created successfully" | ||
// @Failure 400 {string} string "Bad Request" | ||
// @Failure 500 {string} string "Internal Server Error" | ||
// @Router /api/followings/ [post] | ||
func (fol *FollowingController) CreateFollowings(c *gin.Context) { | ||
|
||
// Define Struct for JSON binding | ||
var requestBody struct { | ||
Follower uint `json:"follower_user_id"` | ||
Followed uint `json:"following_user_id"` | ||
} | ||
// Bind the JSON body to the struct | ||
if err := c.ShouldBindJSON(&requestBody); err != nil { | ||
// If there was an error parsing the JSON, return a Bad Request | ||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error(), "type": "json encoding"}) | ||
return | ||
} | ||
|
||
// Create a new Followings instance | ||
following := models.NewFollowings(requestBody.Follower, requestBody.Followed) | ||
|
||
// Call following service | ||
var err = fol.followingService.CreateFollowings(following) | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create following Here", "extra info": err}) | ||
return | ||
} | ||
//Success | ||
c.JSON(http.StatusOK, gin.H{"msg": "Following created successfully"}) | ||
} | ||
|
||
///////////////////////////////Delete//////////////////////////////// | ||
|
||
// UnfollowUser godoc | ||
// | ||
// @Summary Unfollows a user | ||
// @ID unfollowUser | ||
// @Tags followings | ||
// @Produce json | ||
// @Param follower_user_id path uint true "ID of the follower user" | ||
// @Param followed_user_id path uint true "ID of the user to unfollow" | ||
// @Success 200 {string} string "User unfollowed successfully" | ||
// @Failure 404 {string} string "Failed to unfollow user: 404 Error" | ||
// @Router /api/followings/{follower_user_id}/{followed_user_id} [delete] | ||
func (fol *FollowingController) UnfollowUser(c *gin.Context) { | ||
|
||
// Convert follower_user_id and followed_user_id to uint | ||
follower, err := strconv.ParseUint(c.Param("follower_user_id"), 10, 64) | ||
if err != nil { | ||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid follower_user_id"}) | ||
return | ||
} | ||
|
||
followed, err := strconv.ParseUint(c.Param("following_user_id"), 10, 64) | ||
if err != nil { | ||
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid following_user_id"}) | ||
return | ||
} | ||
|
||
err = fol.followingService.DeleteFollowing(uint(follower), uint(followed)) | ||
if err != nil { | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to unfollow user", "error info": err}) | ||
return | ||
} | ||
|
||
c.JSON(http.StatusOK, gin.H{"msg": "User unfollowed successfully"}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,20 @@ | ||
-- Create Following table | ||
DROP TABLE IF EXISTS following; | ||
DROP TABLE IF EXISTS followings; | ||
|
||
CREATE TABLE IF NOT EXISTS following ( | ||
following_id SERIAL PRIMARY KEY, | ||
CREATE TABLE IF NOT EXISTS followings ( | ||
id SERIAL PRIMARY KEY, | ||
follower_user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, | ||
following_user_id INT NOT NULL REFERENCES users(id) ON DELETE CASCADE, | ||
follow_date TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, | ||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, | ||
CONSTRAINT unique_following_pair UNIQUE (follower_user_id, following_user_id), | ||
CONSTRAINT no_self_follow CHECK (follower_user_id != following_user_id) | ||
); | ||
|
||
CREATE INDEX IF NOT EXISTS idx_follower_user_id ON following(follower_user_id); | ||
CREATE INDEX IF NOT EXISTS idx_following_user_id ON following(following_user_id); | ||
CREATE INDEX IF NOT EXISTS idx_follower_user_id ON followings(follower_user_id); | ||
CREATE INDEX IF NOT EXISTS idx_following_user_id ON followings(following_user_id); | ||
|
||
-- Insert sample data into "following" table | ||
INSERT INTO following (follower_user_id, following_user_id) | ||
INSERT INTO followings (follower_user_id, following_user_id) | ||
VALUES | ||
(1, 2), | ||
(2, 1); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package models | ||
|
||
import "time" | ||
|
||
// Followings represents a following relationship between two users. | ||
// It defines the structure of a following entry in the database. | ||
type Followings struct { | ||
FollowerUserID uint `gorm:"type:int;" json:"follower_user_id" validate:"required"` | ||
FollowedUserID uint `gorm:"type:int;" json:"following_user_id" validate:"required"` | ||
CreatedAt time.Time `gorm:"type:timestamp" json:"created_at" validate:"required"` | ||
|
||
//User objects: User must be preloaded for gorm reads | ||
FollowerUser User `gorm:"foreignKey:FollowerUserID;references:ID"` | ||
FollowedUser User `gorm:"foreignKey:FollowedUserID;references:ID"` | ||
} | ||
|
||
// NewFollowings creates a new instance of Followings with the provided follower and followed user IDs. | ||
// It initializes the struct fields and returns a pointer to the newly created Followings object. | ||
func NewFollowings(followerID, followedID uint) *Followings { | ||
return &Followings{ | ||
|
||
FollowerUserID: followerID, | ||
FollowedUserID: followedID, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package routes | ||
|
||
import ( | ||
"backend/src/controllers" | ||
"backend/src/services" | ||
|
||
"github.com/gin-gonic/gin" | ||
"gorm.io/gorm" | ||
) | ||
|
||
// SetupFollowingRoutes sets up routes related to followings, timelines, and followers. | ||
// | ||
// Parameters: | ||
// - router: A pointer to a Gin Engine instance where routes will be set up. | ||
// - db: A pointer to algo db instance | ||
// | ||
// Returns: None. | ||
func SetupFollowingRoutes(router *gin.Engine, db *gorm.DB) { | ||
followingService := services.NewFollowingService(db) | ||
followingController := controllers.NewFollowingController(followingService) | ||
|
||
followingRoutes := router.Group("/followings") | ||
{ | ||
//Get all following relations: Done | ||
followingRoutes.GET("/", followingController.GetAllFollowings) | ||
// Create a following relation: Done | ||
followingRoutes.POST("", followingController.CreateFollowings) | ||
//Delete a following relation | ||
followingRoutes.DELETE("/:follower_user_id/:following_user_id", followingController.UnfollowUser) | ||
|
||
} | ||
timelineRoutes := router.Group("/timelines") | ||
{ | ||
// Get a user timeline | ||
timelineRoutes.GET("/:follower_user_id", followingController.GetTimeline) | ||
} | ||
|
||
followersRoutes := router.Group("/followers") | ||
{ | ||
// Get all of a users followers | ||
followersRoutes.GET("/:following_user_id", followingController.GetFollowers) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.