Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 161 additions & 0 deletions backend/api/internal/database/comment_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"database/sql"
"fmt"
"net/http"
"strconv"
"time"

"backend/api/internal/types"
Expand Down Expand Up @@ -463,3 +464,163 @@ func QueryUpdateCommentContent(id int, newContent string) (int16, error) {

return 200, nil
}

// CreateCommentLike creates a like relationship between a user and a comment.
//
// Parameters:
// - username: The username of the user creating the like.
// - strCommentID: The ID of the comment to like (as a string, converted internally).
//
// Returns:
// - int: HTTP-like status code indicating the result of the operation.
// - error: An error if the operation fails or the user is not liking the comment.
func CreateCommentLike(username string, strCommentId string) (int, error) {
// get user ID from username, implicitly checks if user exists
user_id, err := GetUserIdByUsername(username)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred getting id for username: %v", err)
}

// parse comment ID
commentId, err := strconv.Atoi(strCommentId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred parsing user id: %v", err)
}

// verify comment exists
_, err = QueryComment(commentId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred verifying the comment exists: %v", err)
}

// check if the like already exists
var exists bool
query := `SELECT EXISTS (
SELECT 1 FROM CommentLikes WHERE user_id = ? AND comment_id = ?
)`
err = DB.QueryRow(query, user_id, commentId).Scan(&exists)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred checking like existence: %v", err)
}
if exists {
// like already exists, but we return success to keep it idempotent
return http.StatusOK, nil
}

// insert the like
insertQuery := `INSERT INTO CommentLikes (user_id, comment_id) VALUES (?, ?)`
_, err = DB.Exec(insertQuery, user_id, commentId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("Failed to insert comment like: %v", err)
}

// update the likes column
updateQuery := `UPDATE Comments SET likes = likes + 1 WHERE id = ?`
_, err = DB.Exec(updateQuery, commentId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("Failed to update likes count: %v", err)
}

return http.StatusCreated, nil
}

// RemoveCommentLike deletes a like relationship between a user and a comment.
//
// Parameters:
// - username: The username of the user removing the like.
// - strPostId: The ID of the comment to unlike (as a string, converted internally).
//
// Returns:
// - int: HTTP-like status code indicating the result of the operation.
// - error: An error if the operation fails or the user is not liking the comment.
func RemoveCommentLike(username string, strCommentId string) (int, error) {
// get user ID
user_id, err := GetUserIdByUsername(username)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred getting id for username: %v", err)
}

// parse post ID
commentId, err := strconv.Atoi(strCommentId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred parsing username id: %v", err)
}

// verify post exists
_, err = QueryPost(commentId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred verifying the comment exists: %v", err)
}

// perform the delete operation
deleteQuery := `DELETE FROM CommentLikes WHERE user_id = ? AND comment_id = ?`
result, err := DB.Exec(deleteQuery, user_id, commentId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("Failed to delete comment like: %v", err)
}

// check if any rows were actually deleted
rowsAffected, err := result.RowsAffected()
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("Failed to check rows affected: %v", err)
}

if rowsAffected == 0 {
// if no rows were deleted, return success to keep idempotency
return http.StatusNoContent, nil
}

// update the likes column
updateQuery := `UPDATE Comments SET likes = likes - 1 WHERE id = ?`
_, err = DB.Exec(updateQuery, commentId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("Failed to update likes count: %v", err)
}

return http.StatusOK, nil
}

// QueryCommentLike queries for a like relationship between a user and a post.
//
// Parameters:
// - username: The username of the user removing the like.
// - postID: The ID of the post to unlike (as a string, converted internally).
//
// Returns:
// - int: HTTP-like status code indicating the result of the operation.
// - error: An error if the operation fails or.
func QueryCommentLike(username string, strCommId string) (int, bool, error) {
// get user ID from username, implicitly checks if user exists
user_id, err := GetUserIdByUsername(username)
if err != nil {
return http.StatusInternalServerError, false, fmt.Errorf("An error occurred getting id for username: %v", err)
}

// parse post ID
commId, err := strconv.Atoi(strCommId)
if err != nil {
return http.StatusInternalServerError, false, fmt.Errorf("An error occurred parsing comment_id: %v", err)
}

// verify post exists
_, err = QueryComment(commId)
if err != nil {
return http.StatusInternalServerError, false, fmt.Errorf("An error occurred verifying the comment exists: %v", err)
}

// check if the like already exists
var exists bool
query := `SELECT EXISTS (
SELECT 1 FROM CommentLikes WHERE user_id = ? AND comment_id = ?
)`
err = DB.QueryRow(query, user_id, commId).Scan(&exists)
if err != nil {
return http.StatusInternalServerError, false, fmt.Errorf("An error occurred checking like existence: %v", err)
}
if exists {
return http.StatusOK, true, nil
} else {
return http.StatusOK, false, nil
}

}
2 changes: 1 addition & 1 deletion backend/api/internal/database/create_tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ CREATE TABLE Comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL,
parent_comment_id INTEGER,
likes INTEGER NOT NULL,
likes INTEGER DEFAULT 0,
creation_date TIMESTAMP NOT NULL,
user_id INTEGER NOT NULL,
FOREIGN KEY (parent_comment_id) REFERENCES Comments(id) ON DELETE CASCADE,
Expand Down
Binary file modified backend/api/internal/database/dev.sqlite3
Binary file not shown.
167 changes: 165 additions & 2 deletions backend/api/internal/database/post_queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"database/sql"
"fmt"
"net/http"
"strconv"
"time"

"backend/api/internal/types"
Expand Down Expand Up @@ -159,7 +160,7 @@ func QueryPostsByUserId(userId int) ([]types.Post, int, error) {
}
return nil, http.StatusInternalServerError, err
}
posts = append(posts, post)
posts = append(posts, post)
}

return posts, http.StatusOK, nil
Expand Down Expand Up @@ -201,7 +202,169 @@ func QueryPostsByProjectId(projId int) ([]types.Post, int, error) {
}
return nil, http.StatusInternalServerError, err
}
posts = append(posts, post)
posts = append(posts, post)
}
return posts, http.StatusOK, nil
}

// CreatePostLike creates a like relationship between a user and a post.
//
// Parameters:
// - username: The username of the user creating the like.
// - strPostID: The ID of the project to like (as a string, converted internally).
//
// Returns:
// - int: HTTP-like status code indicating the result of the operation.
// - error: An error if the operation fails or the user is not liking the post.
func CreatePostLike(username string, strPostId string) (int, error) {
// get user ID from username, implicitly checks if user exists
user_id, err := GetUserIdByUsername(username)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred getting id for username: %v", err)
}

// parse post ID
postId, err := strconv.Atoi(strPostId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred parsing post_id id: %v", err)
}

// verify post exists
post, err := QueryPost(postId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred verifying the post exists: %v", err)
} else if post == nil {
return http.StatusNotFound, fmt.Errorf("Post ID %d does not exist", postId)
}

// check if the like already exists
var exists bool
query := `SELECT EXISTS (
SELECT 1 FROM PostLikes WHERE user_id = ? AND post_id = ?
)`
err = DB.QueryRow(query, user_id, postId).Scan(&exists)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred checking like existence: %v", err)
}
if exists {
// like already exists, but we return success to keep it idempotent
return http.StatusOK, nil
}

// insert the like
insertQuery := `INSERT INTO PostLikes (user_id, post_id) VALUES (?, ?)`
_, err = DB.Exec(insertQuery, user_id, postId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("Failed to insert post like: %v", err)
}

// update the likes column
updateQuery := `UPDATE Posts SET likes = likes + 1 WHERE id = ?`
_, err = DB.Exec(updateQuery, postId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("Failed to update likes count: %v", err)
}

return http.StatusCreated, nil
}

// RemovePostLike deletes a like relationship between a user and a post.
//
// Parameters:
// - username: The username of the user removing the like.
// - strPostID: The ID of the post to unlike (as a string, converted internally).
//
// Returns:
// - int: HTTP-like status code indicating the result of the operation.
// - error: An error if the operation fails or the user is not liking the post.
func RemovePostLike(username string, strPostId string) (int, error) {
// get user ID
user_id, err := GetUserIdByUsername(username)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred getting id for username: %v", err)
}

// parse post ID
postId, err := strconv.Atoi(strPostId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred parsing username id: %v", err)
}

// verify post exists
_, err = QueryPost(postId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("An error occurred verifying the post exists: %v", err)
}

// perform the delete operation
deleteQuery := `DELETE FROM PostLikes WHERE user_id = ? AND post_id = ?`
result, err := DB.Exec(deleteQuery, user_id, postId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("Failed to delete post like: %v", err)
}

// check if any rows were actually deleted
rowsAffected, err := result.RowsAffected()
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("Failed to check rows affected: %v", err)
}

if rowsAffected == 0 {
// if no rows were deleted, return success to keep idempotency
return http.StatusNoContent, nil
}

// update the likes column
updateQuery := `UPDATE Posts SET likes = likes - 1 WHERE id = ?`
_, err = DB.Exec(updateQuery, postId)
if err != nil {
return http.StatusInternalServerError, fmt.Errorf("Failed to update likes count: %v", err)
}

return http.StatusOK, nil
}

// QueryPostLike queries for a like relationship between a user and a post.
//
// Parameters:
// - username: The username of the user removing the like.
// - postID: The ID of the post to unlike (as a string, converted internally).
//
// Returns:
// - int: HTTP-like status code indicating the result of the operation.
// - error: An error if the operation fails or.
func QueryPostLike(username string, strPostId string) (int, bool, error) {
// get user ID from username, implicitly checks if user exists
user_id, err := GetUserIdByUsername(username)
if err != nil {
return http.StatusInternalServerError, false, fmt.Errorf("An error occurred getting id for username: %v", err)
}

// parse post ID
postId, err := strconv.Atoi(strPostId)
if err != nil {
return http.StatusInternalServerError, false, fmt.Errorf("An error occurred parsing post_id: %v", err)
}

// verify post exists
_, err = QueryPost(postId)
if err != nil {
return http.StatusInternalServerError, false, fmt.Errorf("An error occurred verifying the post exists: %v", err)
}

// check if the like already exists
var exists bool
query := `SELECT EXISTS (
SELECT 1 FROM PostLikes WHERE user_id = ? AND post_id = ?
)`
err = DB.QueryRow(query, user_id, postId).Scan(&exists)
if err != nil {
return http.StatusInternalServerError, false, fmt.Errorf("An error occurred checking like existence: %v", err)
}
if exists {
return http.StatusOK, true, nil
} else {
return http.StatusOK, false, nil
}

}
Loading