From e2be07c3ad6b1ae1e80c71dfc1b794b11d2db5fc Mon Sep 17 00:00:00 2001 From: Derek Corniello Date: Mon, 16 Dec 2024 18:12:15 -0500 Subject: [PATCH 1/4] added documentation to endpoints --- backend/api/internal/handlers/user_routes.go | 4 +- backend/api/internal/spec/project-api.yml | 303 +++++++++++++++++-- backend/api/internal/spec/user-api.yml | 188 +++++++++++- backend/api/main.go | 18 +- 4 files changed, 471 insertions(+), 42 deletions(-) diff --git a/backend/api/internal/handlers/user_routes.go b/backend/api/internal/handlers/user_routes.go index c56e7ff..8ed4f22 100644 --- a/backend/api/internal/handlers/user_routes.go +++ b/backend/api/internal/handlers/user_routes.go @@ -172,7 +172,7 @@ func GetUsersFollowersUsernames(context *gin.Context) { return } - context.JSON(http.StatusOK, followers) + context.JSON(http.StatusOK, gin.H{"message": "Successfully got followers", "followers":followers}) } func GetUsersFollowingUsernames(context *gin.Context) { @@ -184,7 +184,7 @@ func GetUsersFollowingUsernames(context *gin.Context) { return } - context.JSON(http.StatusOK, following) + context.JSON(http.StatusOK, gin.H{"message": "Successfully got following", "following": following}) } func FollowUser(context *gin.Context) { diff --git a/backend/api/internal/spec/project-api.yml b/backend/api/internal/spec/project-api.yml index 11e7870..cad25fb 100644 --- a/backend/api/internal/spec/project-api.yml +++ b/backend/api/internal/spec/project-api.yml @@ -4,12 +4,45 @@ info: description: API for managing project records, including fetching, creating, updating, and deleting projects. version: 1.0.0 paths: - /projects/{id}: + /projects: + post: + summary: Create a new project + description: Creates a new project record. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Project' + responses: + 201: + description: Project created successfully + content: + application/json: + schema: + type: object + properties: + message: + type: string + 400: + description: Invalid request payload + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 500: + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /projects/{project_id}: get: summary: Get a project by ID description: Retrieve project details by their ID. parameters: - - name: id + - name: project_id in: path required: true schema: @@ -45,7 +78,7 @@ paths: summary: Delete a project by ID description: Deletes a project by its ID. parameters: - - name: id + - name: project_id in: path required: true schema: @@ -84,7 +117,7 @@ paths: summary: Update project information description: Updates specific fields of a project by its ID. parameters: - - name: id + - name: project_id in: path required: true schema: @@ -128,19 +161,20 @@ paths: schema: $ref: '#/components/schemas/ErrorResponse' - /projects: - post: - summary: Create a new project - description: Creates a new project record. - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Project' + /projects/{project_id}/followers: + get: + summary: Get the project's followers' ids + description: Returns all of the user ids of the followers of the project specified + parameters: + - name: project_id + in: path + required: true + schema: + type: integer + format: int64 responses: - 201: - description: Project created successfully + 200: + description: Successfully retrieved list of ids content: application/json: schema: @@ -148,8 +182,243 @@ paths: properties: message: type: string + followers: + type: array + items: + type: string 400: - description: Invalid request payload + description: Invalid project ID + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 404: + description: Project not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 500: + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /projects/{project_id}/followers/usernames: + get: + summary: Get the project's followers' names + description: Returns all of the usernames of the followers of the project specified + parameters: + - name: project_id + in: path + required: true + schema: + type: integer + format: int64 + responses: + 200: + description: Successfully retrieved list of usernames + content: + application/json: + schema: + type: object + properties: + message: + type: string + followers: + type: array + items: + type: string + + 400: + description: Invalid project ID + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 404: + description: Project not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 500: + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /projects/follows/{username}/: + get: + summary: Get the ids of the projects a user is following + description: Returns all of the project ids of the projects the user follows + parameters: + - name: project_id + in: path + required: true + schema: + type: string + responses: + 200: + description: Successfully retrieved list of ids + content: + application/json: + schema: + type: object + properties: + message: + type: string + followers: + type: array + items: + type: integer + format: int64 + 400: + description: Invalid username + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 404: + description: Username not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 500: + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /projects/follows/{username}/names: + get: + summary: Get the names of the projects a user is following + description: Returns all of the names of the projects the user follows + parameters: + - name: username + in: path + required: true + schema: + type: string + responses: + 200: + description: Successfully retrieved list of project names + content: + application/json: + schema: + type: object + properties: + message: + type: string + followers: + type: array + items: + type: string + + 400: + description: Invalid project ID + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 404: + description: Project not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 500: + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /projects/{username}/follow/{project_id}: + get: + summary: Allows user to follow a project + description: Creates a new following from a user to a project based on username and project id + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: project_id + in: path + required: true + schema: + type: integer + format: int64 + + responses: + 200: + description: Successfully followed project + content: + application/json: + schema: + type: object + properties: + message: + type: string + 400: + description: Invalid username or project id + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 404: + description: Project or User not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 500: + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /projects/{username}/unfollow/{project_id}: + get: + summary: Allows user to unfollow a project + description: Deletes a current following from a user to a project based on username and project id + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: project_id + in: path + required: true + schema: + type: integer + format: int64 + + responses: + 200: + description: Successfully unfollowed project + content: + application/json: + schema: + type: object + properties: + message: + type: string + 400: + description: Invalid username or project id + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 404: + description: Project or User not found content: application/json: schema: diff --git a/backend/api/internal/spec/user-api.yml b/backend/api/internal/spec/user-api.yml index 6af1a70..1920ca6 100644 --- a/backend/api/internal/spec/user-api.yml +++ b/backend/api/internal/spec/user-api.yml @@ -4,6 +4,36 @@ info: description: API for managing user records, including fetching, creating, updating, and deleting users. version: 1.0.0 paths: + /users: + post: + summary: Create a new user + description: Creates a new user record. + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/User' + responses: + 201: + description: User created successfully + content: + application/json: + schema: + $ref: '#/components/schemas/User' + 400: + description: Invalid request payload + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 500: + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + /users/{username}: get: summary: Get a user by username @@ -126,6 +156,74 @@ paths: $ref: '#/components/schemas/ErrorResponse' /users/{username}/followers: + get: + summary: Gets followers of a username by their username + description: Returns a list of user ids of the users that follow the given user + parameters: + - name: username + in: path + required: true + schema: + type: string + responses: + 200: + description: Successfully retrieved the followers list + content: + application/json: + schema: + type: array + items: + format: string + 400: + description: Invalid username format or missing parameter + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 404: + description: User not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 500: + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + /users/{username}/follows: + get: + summary: Retrieves a user's list of following + description: Returns a list of user ids of the users that a given user follows. + parameters: + - name: username + in: path + required: true + schema: + type: array + items: + type: string + responses: + 200: + description: Successfully retrieved the following list + content: + application/json: + schema: + type: array + items: + type: string + 400: + description: Invalid username format or missing parameter + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 404: + description: User not found + + /users/{username}/followers/usernames: get: summary: Gets followers of a username by their username description: Returns a list of usernames of the users that follow the given user @@ -192,25 +290,41 @@ paths: $ref: '#/components/schemas/ErrorResponse' 404: description: User not found - /users: - post: - summary: Create a new user - description: Creates a new user record. - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/User' + + /users/{username}/follow/{new_follow}: + get: + summary: Allows user to follow another user + description: Creates a new following from a user to another user based on two usernames + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: new_follow + in: path + required: true + schema: + type: string + responses: - 201: - description: User created successfully + 200: + description: Successfully followed user content: application/json: schema: - $ref: '#/components/schemas/User' + type: object + properties: + message: + type: string 400: - description: Invalid request payload + description: Invalid username + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 404: + description: User not found content: application/json: schema: @@ -222,6 +336,52 @@ paths: schema: $ref: '#/components/schemas/ErrorResponse' + /users/{username}/unfollow/{unfollow}: + get: + summary: Allows user to unfollow another user + description: Deletes a current following from a user to another user based on two usernames + parameters: + - name: username + in: path + required: true + schema: + type: string + - name: unfollow + in: path + required: true + schema: + type: string + + responses: + 200: + description: Successfully unfollowed user + content: + application/json: + schema: + type: object + properties: + message: + type: string + 400: + description: Invalid username + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 404: + description: Project not found + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + 500: + description: Internal server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + + components: schemas: User: # this references the User type in internal/types/types.gp diff --git a/backend/api/main.go b/backend/api/main.go index b248181..df10676 100644 --- a/backend/api/main.go +++ b/backend/api/main.go @@ -45,21 +45,21 @@ func main() { router.GET("/users/:username/followers/usernames", handlers.GetUsersFollowersUsernames) router.GET("/users/:username/follows/usernames", handlers.GetUsersFollowingUsernames) - router.POST("/users/:username/follow/:new_follow", handlers.FollowUser) - router.POST("/users/:username/unfollow/:unfollow", handlers.UnfollowUser) + router.POST("/users/:username/follow/:new_follow", handlers.FollowUser) + router.POST("/users/:username/unfollow/:unfollow", handlers.UnfollowUser) - router.GET("/projects/:project_id", handlers.GetProjectById) - router.POST("/projects", handlers.CreateProject) + router.GET("/projects/:project_id", handlers.GetProjectById) + router.POST("/projects", handlers.CreateProject) router.PUT("/projects/:project_id", handlers.UpdateProjectInfo) - router.DELETE("/projects/:project_id", handlers.DeleteProject) + router.DELETE("/projects/:project_id", handlers.DeleteProject) router.GET("/projects/:project_id/followers", handlers.GetProjectFollowers) - router.GET("/projects/follows/:username", handlers.GetProjectFollowing) + router.GET("/projects/follows/:username", handlers.GetProjectFollowing) router.GET("/projects/:project_id/followers/usernames", handlers.GetProjectFollowersUsernames) - router.GET("/projects/follows/:username/usernames", handlers.GetProjectFollowingUsernames) - router.POST("/projects/:username/follow/:project_id", handlers.FollowProject) - router.POST("/projects/:username/unfollow/:project_id", handlers.UnfollowProject) + router.POST("/projects/:username/follow/:project_id", handlers.FollowProject) + router.POST("/projects/:username/unfollow/:project_id", handlers.UnfollowProject) + router.GET("/projects/follows/:username/names", handlers.GetProjectFollowingUsernames) var dbinfo, dbtype string From b69cd18af973a0231aaa76b9f77d4d5145c8f483 Mon Sep 17 00:00:00 2001 From: Derek Corniello Date: Mon, 16 Dec 2024 21:09:47 -0500 Subject: [PATCH 2/4] almost finished docs --- .../api/internal/database/project_queries.go | 98 ++++++- backend/api/internal/database/user_queries.go | 243 ++++++++++++++---- backend/api/internal/database/utils.go | 58 +++++ .../api/internal/handlers/project_routes.go | 4 +- backend/api/main.go | 2 +- 5 files changed, 354 insertions(+), 51 deletions(-) diff --git a/backend/api/internal/database/project_queries.go b/backend/api/internal/database/project_queries.go index 9cde8f5..f9a8967 100644 --- a/backend/api/internal/database/project_queries.go +++ b/backend/api/internal/database/project_queries.go @@ -11,6 +11,16 @@ import ( "backend/api/internal/types" ) +// a function to get all project data based on a username +// +// input: +// +// id (int) - the project id to query for +// +// output: +// +// *types.Project - the user retrieved +// error func QueryProject(id int) (*types.Project, error) { query := `SELECT id, name, description, status, likes, links, tags, owner, creation_date FROM Projects WHERE id = ?;` row := DB.QueryRow(query, id) @@ -45,6 +55,16 @@ func QueryProject(id int) (*types.Project, error) { return &project, nil } +// a function to create a project in the database +// +// input: +// +// user (*types.Project) - the project to be created +// +// output: +// +// int64 - last project id created, for verifcation +// error func QueryCreateProject(proj *types.Project) (int64, error) { linksJSON, err := MarshalToJSON(proj.Links) if err != nil { @@ -74,6 +94,16 @@ func QueryCreateProject(proj *types.Project) (int64, error) { return lastId, nil } +// a function that deletes a project by its id +// +// input: +// +// id (int) - the project id of the project to be deleted +// +// output: +// +// int16 - status code +// error func QueryDeleteProject(id int) (int16, error) { query := `DELETE from Projects WHERE id=?;` res, err := DB.Exec(query, id) @@ -91,6 +121,16 @@ func QueryDeleteProject(id int) (int16, error) { return 200, nil } +// a function that updates a project in the database given the user id +// +// input: +// +// id (int) - the project id of the project to be updated +// updatedData (map[string]interface{}) - the updated data in JSON-like form +// +// output: +// +// error func QueryUpdateProject(id int, updatedData map[string]interface{}) error { query := `UPDATE Projects SET ` var args []interface{} @@ -114,6 +154,17 @@ func QueryUpdateProject(id int, updatedData map[string]interface{}) error { return nil } +// function to retrieve the ids of a project's followers +// +// input: +// +// projectID (int) - the project to retrieve +// +// output: +// +// []int - the int ids +// int - http status code +// error func QueryGetProjectFollowers(projectID int) ([]int, int, error) { query := ` SELECT u.id @@ -124,6 +175,17 @@ func QueryGetProjectFollowers(projectID int) ([]int, int, error) { return getProjectFollowersOrFollowing(query, projectID) } +// function to retrieve the usernames of a project's followers +// +// input: +// +// projectID (int) - the user to retrieve +// +// output: +// +// []string - the string usernames +// int - http status code +// error func QueryGetProjectFollowersUsernames(projectID int) ([]string, int, error) { query := ` SELECT u.username @@ -134,6 +196,17 @@ func QueryGetProjectFollowersUsernames(projectID int) ([]string, int, error) { return getProjectFollowersOrFollowingUsernames(query, projectID) } +// function to retrieve the projects a user is following by id +// +// input: +// +// username (string) - the user to retrieve +// +// output: +// +// []int - the int project ids +// int - http status code +// error func QueryGetProjectFollowing(username string) ([]int, int, error) { userID, err := GetUserIdByUsername(username) if err != nil { @@ -149,7 +222,18 @@ func QueryGetProjectFollowing(username string) ([]int, int, error) { return getProjectFollowersOrFollowing(query, userID) } -func QueryGetProjectFollowingUsernames(username string) ([]string, int, error) { +// function to retrieve the projects a user is following by name +// +// input: +// +// username (string) - the user to retrieve +// +// output: +// +// []string - the string project names +// int - http status code +// error +func QueryGetProjectFollowingNames(username string) ([]string, int, error) { userID, err := GetUserIdByUsername(username) if err != nil { return nil, 0, fmt.Errorf("Error fetching user id from username: %v", err) @@ -164,6 +248,18 @@ func QueryGetProjectFollowingUsernames(username string) ([]string, int, error) { return getProjectFollowersOrFollowingUsernames(query, userID) } +// helper function to use for retrieving followers and following +// +// input: +// +// query (string) - the query used to get users data +// userID (int) - the user's int +// +// output: +// +// []int - the ids of the retrieved users +// int - httpcode +// error func getProjectFollowersOrFollowing(query string, userID int) ([]int, int, error) { rows, err := ExecQuery(query, userID) if err != nil { diff --git a/backend/api/internal/database/user_queries.go b/backend/api/internal/database/user_queries.go index 509e3bb..0c8c3c5 100644 --- a/backend/api/internal/database/user_queries.go +++ b/backend/api/internal/database/user_queries.go @@ -11,6 +11,16 @@ import ( "backend/api/internal/types" ) +// a function to retrieve the username given a user id +// +// input: +// +// id (int64) - the id +// +// output: +// +// string - the username +// error func GetUsernameById(id int64) (string, error) { query := `SELECT username FROM Users WHERE id = ?;` @@ -28,6 +38,37 @@ func GetUsernameById(id int64) (string, error) { return retrievedUserName, nil } +// a function to retrieve the user id given a username +// +// input: +// +// string - the username +// +// output: +// +// id (int64) - the id +// error +func GetUserIdByUsername(username string) (int, error) { + query := `SELECT id FROM Users WHERE username = ?` + var userID int + row := DB.QueryRow(query, username) + err := row.Scan(&userID) + if err != nil { // TODO: Is there a way this can return a 404 vs 500 error? this could be a 404 or 500, but we cannot tell from an err here + return -1, fmt.Errorf("Error fetching user ID for username '%v' (this usually means username does not exist) : %v", username, err) + } + return userID, nil +} + +// a function to get all user data based on a username +// +// input: +// +// username (string) - the username to query for +// +// output: +// +// *types.User - the user retrieved +// error func QueryUsername(username string) (*types.User, error) { query := `SELECT username, picture, bio, links, creation_date FROM Users WHERE username = ?;` @@ -53,6 +94,15 @@ func QueryUsername(username string) (*types.User, error) { return &user, nil } +// a function to create a user in the database +// +// input: +// +// user (*types.User) - the user to be created +// +// output: +// +// error func QueryCreateUser(user *types.User) error { linksJSON, err := json.Marshal(user.Links) if err != nil { @@ -77,6 +127,16 @@ func QueryCreateUser(user *types.User) error { return nil } +// a function that deletes a user by their username +// +// input: +// +// username (string) - the username to be deleted +// +// output: +// +// int16 - status code +// error func QueryDeleteUser(username string) (int16, error) { query := `DELETE from Users WHERE username=?;` res, err := DB.Exec(query, username) @@ -94,6 +154,16 @@ func QueryDeleteUser(username string) (int16, error) { return 200, nil } +// a function that updates a user in the database given their username +// +// input: +// +// username (string) - the username to be updated +// updatedData (map[string]interface{}) - the updated data in JSON-like form +// +// output: +// +// error func QueryUpdateUser(username string, updatedData map[string]interface{}) error { newUsername, usernameExists := updatedData["username"] @@ -126,17 +196,17 @@ func QueryUpdateUser(username string, updatedData map[string]interface{}) error return nil } -func GetUserIdByUsername(username string) (int, error) { - query := `SELECT id FROM Users WHERE username = ?` - var userID int - row := DB.QueryRow(query, username) - err := row.Scan(&userID) - if err != nil { // TODO: Is there a way this can return a 404 vs 500 error? this could be a 404 or 500, but we cannot tell from an err here - return -1, fmt.Errorf("Error fetching user ID for username '%v' (this usually means username does not exist) : %v", username, err) - } - return userID, nil -} - +// function to retrieve the usernames of a user's followers +// +// input: +// +// username (string) - the user to retrieve +// +// output: +// +// []string - the usernames +// int - http status code +// error func QueryGetUsersFollowersUsernames(username string) ([]string, int, error) { userID, err := GetUserIdByUsername(username) if err != nil { @@ -152,6 +222,17 @@ func QueryGetUsersFollowersUsernames(username string) ([]string, int, error) { return getUsersFollowingOrFollowersUsernames(query, userID) } +// function to retrieve the ids of a user's followers +// +// input: +// +// username (string) - the user to retrieve +// +// output: +// +// []int - the int ids +// int - http status code +// error func QueryGetUsersFollowers(username string) ([]int, int, error) { userID, err := GetUserIdByUsername(username) if err != nil { @@ -167,36 +248,70 @@ func QueryGetUsersFollowers(username string) ([]int, int, error) { return getUsersFollowingOrFollowers(query, userID) } -func QueryGetUsersFollowing(username string) ([]int, int, error) { +// function to retrieve the usernames of the users who follow the given user +// +// input: +// +// username (string) - the user to retrieve +// +// output: +// +// []string - the usernames +// int - http status code +// error +func QueryGetUsersFollowingUsernames(username string) ([]string, int, error) { userID, err := GetUserIdByUsername(username) if err != nil { return nil, http.StatusNotFound, err } query := ` - SELECT u.id + SELECT u.username FROM Users u JOIN UserFollows uf ON u.id = uf.follows_id WHERE uf.follower_id = ?` - return getUsersFollowingOrFollowers(query, userID) + return getUsersFollowingOrFollowersUsernames(query, userID) } -func QueryGetUsersFollowingUsernames(username string) ([]string, int, error) { +// function to retrieve the ids of the users who follow the given user +// +// input: +// +// username (string) - the user to retrieve +// +// output: +// +// []int - the int ids +// int - http status code +// error +func QueryGetUsersFollowing(username string) ([]int, int, error) { userID, err := GetUserIdByUsername(username) if err != nil { return nil, http.StatusNotFound, err } query := ` - SELECT u.username + SELECT u.id FROM Users u JOIN UserFollows uf ON u.id = uf.follows_id WHERE uf.follower_id = ?` - return getUsersFollowingOrFollowersUsernames(query, userID) + return getUsersFollowingOrFollowers(query, userID) } +// helper function to use for retrieving +// +// input: +// +// query (string) - the query used to get users data +// userID (int) - the user's int +// +// output: +// +// []int - the ids of the retrieved users +// int - httpcode +// error func getUsersFollowingOrFollowers(query string, userID int) ([]int, int, error) { rows, err := ExecQuery(query, userID) if err != nil { @@ -220,6 +335,18 @@ func getUsersFollowingOrFollowers(query string, userID int) ([]int, int, error) return users, http.StatusOK, nil } +// helper function to use for retrieving +// +// input: +// +// query (string) - the query used to get users data +// userID (int) - the user's int +// +// output: +// +// []int - the ids of the retrieved users +// int - httpcode +// error func getUsersFollowingOrFollowersUsernames(query string, userID int) ([]string, int, error) { rows, err := ExecQuery(query, userID) if err != nil { @@ -243,39 +370,61 @@ func getUsersFollowingOrFollowersUsernames(query string, userID int) ([]string, return users, http.StatusOK, nil } +// function to create a follow from user to follow newFollow +// +// input: +// +// user (string) - the username of the user requesting a follow +// newFollow (string) - the username of the user to be followed +// +// output: +// +// int - http code +// error func CreateNewUserFollow(user string, newFollow string) (int, error) { - userID, err := GetUserIdByUsername(user) - if err != nil { - return http.StatusNotFound, fmt.Errorf("Cannot find user with username '%v'.", user) - } - - newFollowID, err := GetUserIdByUsername(newFollow) - if err != nil { - return http.StatusNotFound, fmt.Errorf("Cannot find user with username '%v'.", newFollow) - } - - currFollowers, httpCode, err := QueryGetUsersFollowing(user) - if err != nil { - return httpCode, fmt.Errorf("Cannot retrieve user's following list: %v", err) - } - - if slices.Contains(currFollowers, newFollowID) { - return http.StatusConflict, fmt.Errorf("User '%v' is already being followed", newFollow) - } - - query := `INSERT INTO UserFollows (follower_id, follows_id) VALUES (?, ?)` - rowsAffected, err := ExecUpdate(query, userID, newFollowID) - if err != nil { - return http.StatusInternalServerError, fmt.Errorf("An error occurred adding follower: %v", err) - } - - if rowsAffected == 0 { - return http.StatusInternalServerError, fmt.Errorf("Failed to add the follow relationship") - } - - return http.StatusOK, nil + userID, err := GetUserIdByUsername(user) + if err != nil { + return http.StatusNotFound, fmt.Errorf("Cannot find user with username '%v'.", user) + } + + newFollowID, err := GetUserIdByUsername(newFollow) + if err != nil { + return http.StatusNotFound, fmt.Errorf("Cannot find user with username '%v'.", newFollow) + } + + currFollowers, httpCode, err := QueryGetUsersFollowing(user) + if err != nil { + return httpCode, fmt.Errorf("Cannot retrieve user's following list: %v", err) + } + + if slices.Contains(currFollowers, newFollowID) { + return http.StatusConflict, fmt.Errorf("User '%v' is already being followed", newFollow) + } + + query := `INSERT INTO UserFollows (follower_id, follows_id) VALUES (?, ?)` + rowsAffected, err := ExecUpdate(query, userID, newFollowID) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred adding follower: %v", err) + } + + if rowsAffected == 0 { + return http.StatusInternalServerError, fmt.Errorf("Failed to add the follow relationship") + } + + return http.StatusOK, nil } +// function to remove a follow from user to unfollow unfollow +// +// input: +// +// user (string) - the username of the user requesting an unfollow +// unfollow (string) - the username of the user to be unfollowed +// +// output: +// +// int - http code +// error func RemoveUserFollow(user string, unfollow string) (int, error) { userID, err := GetUserIdByUsername(user) if err != nil { diff --git a/backend/api/internal/database/utils.go b/backend/api/internal/database/utils.go index 92e8ce6..24121d7 100644 --- a/backend/api/internal/database/utils.go +++ b/backend/api/internal/database/utils.go @@ -1,3 +1,10 @@ +// The database package includes the functions to take in data from a handler +// and do any database CRUD operations to make the workflow of the app +// work correctly. +// +// It mainly uses the database/sql package and encoding/json to +// parse json data and integrate SQL types and communications between +// this package and the database package database import ( @@ -8,6 +15,16 @@ import ( "backend/api/internal/logger" ) + +// takes in some sort of data, and changes it to a JSON +// data type. Will return an error if it is not JSON-esque data +// +// input: +// interface (interface{}) - the data to be converted to JSON +// output: +// the JSON string +// an error + func MarshalToJSON(value interface{}) (string, error) { linksJSON, err := json.Marshal(value) if err != nil { @@ -17,6 +34,16 @@ func MarshalToJSON(value interface{}) (string, error) { return string(linksJSON), nil } + +// takes in some JSON data, and changes it to a JSON +// data type. Will return an error if it is not JSON-esque data +// +// input: +// data (string) - the data in a string, edited in place +// interface (interface{}) - the data to be put into the string +// output: +// error + func UnmarshalFromJSON(data string, target interface{}) error { err := json.Unmarshal([]byte(data), target) if err != nil { @@ -26,6 +53,18 @@ func UnmarshalFromJSON(data string, target interface{}) error { return nil } + +// takes in a query and some params to add to it +// and will execute the query, given the database is +// setup and connected. +// +// input: +// query (string) - the base query string +// args (...interface{}) - the params of the query +// output: +// *sql.Rows - the resulting rows +// error + func ExecQuery(query string, args ...interface{}) (*sql.Rows, error) { rows, err := DB.Query(query, args...) if err != nil { @@ -35,6 +74,18 @@ func ExecQuery(query string, args ...interface{}) (*sql.Rows, error) { return rows, nil } + +// takes in a query and some params to add to it +// and will execute the update query, given the database is +// setup and connected. +// +// input: +// query (string) - the base query string +// args (...interface{}) - the params of the query +// output: +// *sql.Rows - the affected rows +// error + func ExecUpdate(query string, args ...interface{}) (int64, error) { res, err := DB.Exec(query, args...) if err != nil { @@ -52,6 +103,13 @@ func ExecUpdate(query string, args ...interface{}) (int64, error) { // BuildUpdateQuery is a utility function that handles the construction of an UPDATE query // and prepares the corresponding arguments, including marshaling JSON data for special fields (like links and tags). +// +// input: +// updatedData (map[string]interface{}) - a JSON like structure with all of the updatedData for the query +// output: +// string - the partially completed query, with all of the fields added +// []interface{} - the arguments for the query +// the error func BuildUpdateQuery(updatedData map[string]interface{}) (string, []interface{}, error) { var query string var args []interface{} diff --git a/backend/api/internal/handlers/project_routes.go b/backend/api/internal/handlers/project_routes.go index 5cc4d17..1789c2f 100644 --- a/backend/api/internal/handlers/project_routes.go +++ b/backend/api/internal/handlers/project_routes.go @@ -206,10 +206,10 @@ func GetProjectFollowersUsernames(context *gin.Context) { context.JSON(http.StatusOK, followers) } -func GetProjectFollowingUsernames(context *gin.Context) { +func GetProjectFollowingNames(context *gin.Context) { username := context.Param("username") - following, httpcode, err := database.QueryGetProjectFollowingUsernames(username) + following, httpcode, err := database.QueryGetProjectFollowingNames(username) if err != nil { RespondWithError(context, httpcode, fmt.Sprintf("Failed to fetch following: %v", err)) return diff --git a/backend/api/main.go b/backend/api/main.go index df10676..bee6cd2 100644 --- a/backend/api/main.go +++ b/backend/api/main.go @@ -56,10 +56,10 @@ func main() { router.GET("/projects/:project_id/followers", handlers.GetProjectFollowers) router.GET("/projects/follows/:username", handlers.GetProjectFollowing) router.GET("/projects/:project_id/followers/usernames", handlers.GetProjectFollowersUsernames) + router.GET("/projects/follows/:username/names", handlers.GetProjectFollowingNames) router.POST("/projects/:username/follow/:project_id", handlers.FollowProject) router.POST("/projects/:username/unfollow/:project_id", handlers.UnfollowProject) - router.GET("/projects/follows/:username/names", handlers.GetProjectFollowingUsernames) var dbinfo, dbtype string From 804062cebf577b6d29832667b04e169f2a85f4f9 Mon Sep 17 00:00:00 2001 From: Derek Corniello Date: Tue, 17 Dec 2024 10:33:00 -0500 Subject: [PATCH 3/4] finished doc comments --- .../api/internal/database/project_queries.go | 170 +++++++------- backend/api/internal/database/user_queries.go | 210 ++++++++---------- .../api/internal/handlers/project_routes.go | 63 ++++++ backend/api/internal/handlers/user_routes.go | 70 ++++++ 4 files changed, 314 insertions(+), 199 deletions(-) diff --git a/backend/api/internal/database/project_queries.go b/backend/api/internal/database/project_queries.go index f9a8967..f0ac39f 100644 --- a/backend/api/internal/database/project_queries.go +++ b/backend/api/internal/database/project_queries.go @@ -11,16 +11,14 @@ import ( "backend/api/internal/types" ) -// a function to get all project data based on a username +// QueryProject retrieves a project by its ID from the database. // -// input: +// Parameters: +// - id: The unique identifier of the project to query. // -// id (int) - the project id to query for -// -// output: -// -// *types.Project - the user retrieved -// error +// Returns: +// - *types.Project: The project details if found. +// - error: An error if the query fails. Returns nil for both if no project exists. func QueryProject(id int) (*types.Project, error) { query := `SELECT id, name, description, status, likes, links, tags, owner, creation_date FROM Projects WHERE id = ?;` row := DB.QueryRow(query, id) @@ -55,16 +53,14 @@ func QueryProject(id int) (*types.Project, error) { return &project, nil } -// a function to create a project in the database -// -// input: -// -// user (*types.Project) - the project to be created +// QueryCreateProject creates a new project in the database. // -// output: +// Parameters: +// - proj: The project to be created, containing all necessary fields. // -// int64 - last project id created, for verifcation -// error +// Returns: +// - int64: The ID of the newly created project. +// - error: An error if the operation fails. func QueryCreateProject(proj *types.Project) (int64, error) { linksJSON, err := MarshalToJSON(proj.Links) if err != nil { @@ -94,16 +90,14 @@ func QueryCreateProject(proj *types.Project) (int64, error) { return lastId, nil } -// a function that deletes a project by its id +// QueryDeleteProject deletes a project by its ID. // -// input: +// Parameters: +// - id: The unique identifier of the project to delete. // -// id (int) - the project id of the project to be deleted -// -// output: -// -// int16 - status code -// error +// Returns: +// - int16: HTTP-like status code indicating the result of the operation. +// - error: An error if the operation fails or no project is found. func QueryDeleteProject(id int) (int16, error) { query := `DELETE from Projects WHERE id=?;` res, err := DB.Exec(query, id) @@ -121,16 +115,14 @@ func QueryDeleteProject(id int) (int16, error) { return 200, nil } -// a function that updates a project in the database given the user id -// -// input: +// QueryUpdateProject updates an existing project in the database. // -// id (int) - the project id of the project to be updated -// updatedData (map[string]interface{}) - the updated data in JSON-like form +// Parameters: +// - id: The unique identifier of the project to update. +// - updatedData: A map containing the fields to update with their new values. // -// output: -// -// error +// Returns: +// - error: An error if the operation fails or no project is found. func QueryUpdateProject(id int, updatedData map[string]interface{}) error { query := `UPDATE Projects SET ` var args []interface{} @@ -154,17 +146,15 @@ func QueryUpdateProject(id int, updatedData map[string]interface{}) error { return nil } -// function to retrieve the ids of a project's followers -// -// input: -// -// projectID (int) - the project to retrieve +// QueryGetProjectFollowers retrieves the IDs of a project's followers. // -// output: +// Parameters: +// - projectID: The unique identifier of the project. // -// []int - the int ids -// int - http status code -// error +// Returns: +// - []int: A list of user IDs who follow the project. +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the query fails. func QueryGetProjectFollowers(projectID int) ([]int, int, error) { query := ` SELECT u.id @@ -175,17 +165,15 @@ func QueryGetProjectFollowers(projectID int) ([]int, int, error) { return getProjectFollowersOrFollowing(query, projectID) } -// function to retrieve the usernames of a project's followers +// QueryGetProjectFollowersUsernames retrieves the usernames of a project's followers. // -// input: +// Parameters: +// - projectID: The unique identifier of the project. // -// projectID (int) - the user to retrieve -// -// output: -// -// []string - the string usernames -// int - http status code -// error +// Returns: +// - []string: A list of usernames of the project's followers. +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the query fails. func QueryGetProjectFollowersUsernames(projectID int) ([]string, int, error) { query := ` SELECT u.username @@ -196,17 +184,15 @@ func QueryGetProjectFollowersUsernames(projectID int) ([]string, int, error) { return getProjectFollowersOrFollowingUsernames(query, projectID) } -// function to retrieve the projects a user is following by id -// -// input: +// QueryGetProjectFollowing retrieves the project IDs a user is following. // -// username (string) - the user to retrieve +// Parameters: +// - username: The username of the user. // -// output: -// -// []int - the int project ids -// int - http status code -// error +// Returns: +// - []int: A list of project IDs the user is following. +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the query fails. func QueryGetProjectFollowing(username string) ([]int, int, error) { userID, err := GetUserIdByUsername(username) if err != nil { @@ -222,17 +208,15 @@ func QueryGetProjectFollowing(username string) ([]int, int, error) { return getProjectFollowersOrFollowing(query, userID) } -// function to retrieve the projects a user is following by name -// -// input: -// -// username (string) - the user to retrieve +// QueryGetProjectFollowingNames retrieves the project names a user is following. // -// output: +// Parameters: +// - username: The username of the user. // -// []string - the string project names -// int - http status code -// error +// Returns: +// - []string: A list of project names the user is following. +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the query fails. func QueryGetProjectFollowingNames(username string) ([]string, int, error) { userID, err := GetUserIdByUsername(username) if err != nil { @@ -248,18 +232,16 @@ func QueryGetProjectFollowingNames(username string) ([]string, int, error) { return getProjectFollowersOrFollowingUsernames(query, userID) } -// helper function to use for retrieving followers and following +// getProjectFollowersOrFollowing is a helper function for retrieving follower or following IDs. // -// input: +// Parameters: +// - query: The SQL query string to execute. +// - userID: The unique identifier of the user. // -// query (string) - the query used to get users data -// userID (int) - the user's int -// -// output: -// -// []int - the ids of the retrieved users -// int - httpcode -// error +// Returns: +// - []int: A list of IDs retrieved by the query. +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the query fails. func getProjectFollowersOrFollowing(query string, userID int) ([]int, int, error) { rows, err := ExecQuery(query, userID) if err != nil { @@ -283,8 +265,18 @@ func getProjectFollowersOrFollowing(query string, userID int) ([]int, int, error return projectIDs, http.StatusOK, nil } -func getProjectFollowersOrFollowingUsernames(query string, userID int) ([]string, int, error) { - rows, err := ExecQuery(query, userID) +// getProjectFollowersOrFollowingUsernames is a helper function for retrieving follower or following usernames. +// +// Parameters: +// - query: The SQL query string to execute. +// - projectID: The unique identifier of the project. +// +// Returns: +// - []string: A list of usernames retrieved by the query. +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the query fails. +func getProjectFollowersOrFollowingUsernames(query string, projectID int) ([]string, int, error) { + rows, err := ExecQuery(query, projectID) if err != nil { return nil, http.StatusNotFound, err } @@ -306,6 +298,15 @@ func getProjectFollowersOrFollowingUsernames(query string, userID int) ([]string return projectNames, http.StatusOK, nil } +// CreateNewProjectFollow creates a follow relationship between a user and a project. +// +// Parameters: +// - username: The username of the user creating the follow. +// - projectID: The ID of the project to follow (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 already following the project. func CreateNewProjectFollow(username string, projectID string) (int, error) { userID, err := GetUserIdByUsername(username) if err != nil { @@ -338,6 +339,15 @@ func CreateNewProjectFollow(username string, projectID string) (int, error) { return http.StatusOK, nil } +// RemoveProjectFollow removes a follow relationship between a user and a project. +// +// Parameters: +// - username: The username of the user removing the follow. +// - projectID: The ID of the project to unfollow (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 following the project. func RemoveProjectFollow(username string, projectID string) (int, error) { userID, err := GetUserIdByUsername(username) if err != nil { diff --git a/backend/api/internal/database/user_queries.go b/backend/api/internal/database/user_queries.go index 0c8c3c5..4480f22 100644 --- a/backend/api/internal/database/user_queries.go +++ b/backend/api/internal/database/user_queries.go @@ -11,16 +11,14 @@ import ( "backend/api/internal/types" ) -// a function to retrieve the username given a user id +// GetUsernameById retrieves the username associated with the given user ID. // -// input: +// Parameters: +// - id: The unique identifier of the user. // -// id (int64) - the id -// -// output: -// -// string - the username -// error +// Returns: +// - string: The username if found. +// - error: An error if the query fails. Returns nil for both if no user exists. func GetUsernameById(id int64) (string, error) { query := `SELECT username FROM Users WHERE id = ?;` @@ -38,16 +36,14 @@ func GetUsernameById(id int64) (string, error) { return retrievedUserName, nil } -// a function to retrieve the user id given a username -// -// input: -// -// string - the username +// GetUserIdByUsername retrieves the user ID associated with the given username. // -// output: +// Parameters: +// - username: The username to query. // -// id (int64) - the id -// error +// Returns: +// - int: The user ID if found. +// - error: An error if the query fails or the username does not exist. func GetUserIdByUsername(username string) (int, error) { query := `SELECT id FROM Users WHERE username = ?` var userID int @@ -59,16 +55,14 @@ func GetUserIdByUsername(username string) (int, error) { return userID, nil } -// a function to get all user data based on a username +// QueryUsername retrieves all user data for the given username. // -// input: +// Parameters: +// - username: The username to query. // -// username (string) - the username to query for -// -// output: -// -// *types.User - the user retrieved -// error +// Returns: +// - *types.User: The user details if found. +// - error: An error if the query or data parsing fails. func QueryUsername(username string) (*types.User, error) { query := `SELECT username, picture, bio, links, creation_date FROM Users WHERE username = ?;` @@ -94,15 +88,13 @@ func QueryUsername(username string) (*types.User, error) { return &user, nil } -// a function to create a user in the database -// -// input: +// QueryCreateUser creates a new user in the database. // -// user (*types.User) - the user to be created +// Parameters: +// - user: The user data to insert. // -// output: -// -// error +// Returns: +// - error: An error if the user creation fails. func QueryCreateUser(user *types.User) error { linksJSON, err := json.Marshal(user.Links) if err != nil { @@ -127,16 +119,14 @@ func QueryCreateUser(user *types.User) error { return nil } -// a function that deletes a user by their username -// -// input: +// QueryDeleteUser deletes a user by their username. // -// username (string) - the username to be deleted +// Parameters: +// - username: The username of the user to delete. // -// output: -// -// int16 - status code -// error +// Returns: +// - int16: HTTP-like status code indicating the result. +// - error: An error if the deletion fails. func QueryDeleteUser(username string) (int16, error) { query := `DELETE from Users WHERE username=?;` res, err := DB.Exec(query, username) @@ -154,16 +144,14 @@ func QueryDeleteUser(username string) (int16, error) { return 200, nil } -// a function that updates a user in the database given their username -// -// input: +// QueryUpdateUser updates a user's details by their username. // -// username (string) - the username to be updated -// updatedData (map[string]interface{}) - the updated data in JSON-like form +// Parameters: +// - username: The username of the user to update. +// - updatedData: A map of fields to update and their new values. // -// output: -// -// error +// Returns: +// - error: An error if the update fails or no user is found. func QueryUpdateUser(username string, updatedData map[string]interface{}) error { newUsername, usernameExists := updatedData["username"] @@ -196,17 +184,15 @@ func QueryUpdateUser(username string, updatedData map[string]interface{}) error return nil } -// function to retrieve the usernames of a user's followers -// -// input: -// -// username (string) - the user to retrieve +// QueryGetUsersFollowersUsernames retrieves the usernames of users who follow the specified user. // -// output: +// Parameters: +// - username: The username of the user. // -// []string - the usernames -// int - http status code -// error +// Returns: +// - []string: A list of usernames of the followers. +// - int: HTTP-like status code. +// - error: An error if the query fails. func QueryGetUsersFollowersUsernames(username string) ([]string, int, error) { userID, err := GetUserIdByUsername(username) if err != nil { @@ -222,17 +208,15 @@ func QueryGetUsersFollowersUsernames(username string) ([]string, int, error) { return getUsersFollowingOrFollowersUsernames(query, userID) } -// function to retrieve the ids of a user's followers +// function to retrieve the user ids of the users who follow the given user // -// input: +// Parameters: +// - username (string): the user to retrieve // -// username (string) - the user to retrieve -// -// output: -// -// []int - the int ids -// int - http status code -// error +// Returns: +// - []int: a list of user ids of users who follow the specified user +// - int: HTTP status code indicating the result of the operation +// - error: any error encountered during the query func QueryGetUsersFollowers(username string) ([]int, int, error) { userID, err := GetUserIdByUsername(username) if err != nil { @@ -250,15 +234,13 @@ func QueryGetUsersFollowers(username string) ([]int, int, error) { // function to retrieve the usernames of the users who follow the given user // -// input: -// -// username (string) - the user to retrieve -// -// output: +// Parameters: +// - username (string): the user to retrieve // -// []string - the usernames -// int - http status code -// error +// Returns: +// - []string: a list of usernames of users who follow the specified user +// - int: HTTP status code indicating the result of the operation +// - error: any error encountered during the query func QueryGetUsersFollowingUsernames(username string) ([]string, int, error) { userID, err := GetUserIdByUsername(username) if err != nil { @@ -276,15 +258,13 @@ func QueryGetUsersFollowingUsernames(username string) ([]string, int, error) { // function to retrieve the ids of the users who follow the given user // -// input: +// Parameters: +// - username (string) - the user to retrieve // -// username (string) - the user to retrieve -// -// output: -// -// []int - the int ids -// int - http status code -// error +// Returns: +// - []int: a list of user IDs of users who follow the specified user +// - int: HTTP status code indicating the result of the operation +// - error: any error encountered during the query func QueryGetUsersFollowing(username string) ([]int, int, error) { userID, err := GetUserIdByUsername(username) if err != nil { @@ -300,18 +280,16 @@ func QueryGetUsersFollowing(username string) ([]int, int, error) { return getUsersFollowingOrFollowers(query, userID) } -// helper function to use for retrieving -// -// input: +// helper function to retrieve the followers or followings of a user by their IDs // -// query (string) - the query used to get users data -// userID (int) - the user's int +// Parameters: +// - query (string): the SQL query to execute +// - userID (int): the ID of the user to find follow data for // -// output: -// -// []int - the ids of the retrieved users -// int - httpcode -// error +// Returns: +// - []int: a list of user IDs for the followers or followings +// - int: HTTP status code +// - error: any error encountered during the query func getUsersFollowingOrFollowers(query string, userID int) ([]int, int, error) { rows, err := ExecQuery(query, userID) if err != nil { @@ -335,18 +313,16 @@ func getUsersFollowingOrFollowers(query string, userID int) ([]int, int, error) return users, http.StatusOK, nil } -// helper function to use for retrieving -// -// input: +// helper function to retrieve the followers or followings of a user by their usernames // -// query (string) - the query used to get users data -// userID (int) - the user's int +// Parameters: +// - query (string): the SQL query to execute +// - userID (int): the ID of the user to find follow data for // -// output: -// -// []int - the ids of the retrieved users -// int - httpcode -// error +// Returns: +// - []string: a list of usernames for the followers or followings +// - int: HTTP status code +// - error: any error encountered during the query func getUsersFollowingOrFollowersUsernames(query string, userID int) ([]string, int, error) { rows, err := ExecQuery(query, userID) if err != nil { @@ -370,17 +346,15 @@ func getUsersFollowingOrFollowersUsernames(query string, userID int) ([]string, return users, http.StatusOK, nil } -// function to create a follow from user to follow newFollow -// -// input: +// function to create a follow relationship between two users // -// user (string) - the username of the user requesting a follow -// newFollow (string) - the username of the user to be followed +// Parameters: +// - user (string): the username of the user initiating the follow +// - newFollow (string): the username of the user to be followed // -// output: -// -// int - http code -// error +// Returns: +// - int: HTTP status code +// - error: any error encountered during the query func CreateNewUserFollow(user string, newFollow string) (int, error) { userID, err := GetUserIdByUsername(user) if err != nil { @@ -414,17 +388,15 @@ func CreateNewUserFollow(user string, newFollow string) (int, error) { return http.StatusOK, nil } -// function to remove a follow from user to unfollow unfollow -// -// input: -// -// user (string) - the username of the user requesting an unfollow -// unfollow (string) - the username of the user to be unfollowed +// function to remove a follow relationship between two users // -// output: +// Parameters: +// - user (string): the username of the user initiating the unfollow +// - unfollow (string): the username of the user to be unfollowed // -// int - http code -// error +// Returns: +// - int: HTTP status code +// - error: any error encountered during the query func RemoveUserFollow(user string, unfollow string) (int, error) { userID, err := GetUserIdByUsername(user) if err != nil { diff --git a/backend/api/internal/handlers/project_routes.go b/backend/api/internal/handlers/project_routes.go index 1789c2f..35191f7 100644 --- a/backend/api/internal/handlers/project_routes.go +++ b/backend/api/internal/handlers/project_routes.go @@ -11,6 +11,13 @@ import ( "github.com/gin-gonic/gin" ) +// GetProjectById handles GET requests to retrieve project information by its ID. +// It expects the `project_id` parameter in the URL and does not require a request body. +// Returns: +// - 400 Bad Request if the ID is invalid. +// - 404 Not Found if the project does not exist. +// - 500 Internal Server Error if the database query fails. +// On success, responds with a 200 OK status and the project details in JSON format. func GetProjectById(context *gin.Context) { strId := context.Param("project_id") id, err := strconv.Atoi(strId) @@ -31,6 +38,13 @@ func GetProjectById(context *gin.Context) { context.JSON(http.StatusOK, project) } +// CreateProject handles POST requests to create a new project. +// It expects a JSON payload that can be bound to a `types.Project` object. +// Validates the provided owner's ID and ensures the user exists. +// Returns: +// - 400 Bad Request if the JSON payload is invalid or the owner cannot be verified. +// - 500 Internal Server Error if there is a database error. +// On success, responds with a 201 Created status and the new project ID in JSON format. func CreateProject(context *gin.Context) { var newProj types.Project err := context.BindJSON(&newProj) @@ -60,6 +74,14 @@ func CreateProject(context *gin.Context) { context.JSON(http.StatusCreated, gin.H{"message": fmt.Sprintf("Project created successfully with id '%v'", id)}) } +// UpdateProjectInfo handles PATCH requests to update project information. +// It expects the `project_id` parameter in the URL and a JSON payload with update fields. +// Validates the project ID, checks for the existence of the project, and ensures the fields being updated are allowed. +// Returns: +// - 400 Bad Request for invalid input or disallowed fields. +// - 404 Not Found if the project does not exist. +// - 500 Internal Server Error for database errors. +// On success, responds with a 200 OK status and the updated project details in JSON format. func DeleteProject(context *gin.Context) { strId := context.Param("project_id") id, err := strconv.Atoi(strId) @@ -87,6 +109,15 @@ func DeleteProject(context *gin.Context) { "message": fmt.Sprintf("Project %v deleted.", id), }) } + +// UpdateProjectInfo handles PATCH requests to update project information. +// It expects the `project_id` parameter in the URL and a JSON payload with update fields. +// Validates the project ID, checks for the existence of the project, and ensures the fields being updated are allowed. +// Returns: +// - 400 Bad Request for invalid input or disallowed fields. +// - 404 Not Found if the project does not exist. +// - 500 Internal Server Error for database errors. +// On success, responds with a 200 OK status and the updated project details in JSON format. func UpdateProjectInfo(context *gin.Context) { var updateData map[string]interface{} @@ -160,6 +191,12 @@ func UpdateProjectInfo(context *gin.Context) { }) } +// GetProjectFollowers handles GET requests to fetch a list of users following a project. +// It expects the `project_id` parameter in the URL. +// Returns: +// - 400 Bad Request if the project ID is invalid. +// - Appropriate error code (404 if missing data, 500 if error) for database query failures. +// On success, responds with a 200 OK status and a list of followers in JSON format. func GetProjectFollowers(context *gin.Context) { projectId := context.Param("project_id") intProjectId, err := strconv.Atoi(projectId) @@ -177,6 +214,11 @@ func GetProjectFollowers(context *gin.Context) { context.JSON(http.StatusOK, followers) } +// GetProjectFollowing handles GET requests to fetch a list of projects followed by a user. +// It expects the `username` parameter in the URL. +// Returns: +// - Appropriate error code (404 if missing data, 500 if error) for database query failures. +// On success, responds with a 200 OK status and a list of followed projects in JSON format. func GetProjectFollowing(context *gin.Context) { username := context.Param("username") @@ -189,6 +231,12 @@ func GetProjectFollowing(context *gin.Context) { context.JSON(http.StatusOK, following) } +// GetProjectFollowersUsernames handles GET requests to fetch the usernames of users following a project. +// It expects the `project_id` parameter in the URL. +// Returns: +// - 400 Bad Request if the project ID is invalid. +// - Appropriate error code (404 if missing data, 500 if error) for database query failures. +// On success, responds with a 200 OK status and a list of usernames in JSON format. func GetProjectFollowersUsernames(context *gin.Context) { projectId := context.Param("project_id") intProjectId, err := strconv.Atoi(projectId) @@ -206,6 +254,11 @@ func GetProjectFollowersUsernames(context *gin.Context) { context.JSON(http.StatusOK, followers) } +// GetProjectFollowingNames handles GET requests to fetch the names of projects followed by a user. +// It expects the `username` parameter in the URL. +// Returns: +// - Appropriate error code (404 if missing data, 500 if error) for database query failures. +// On success, responds with a 200 OK status and a list of project names in JSON format. func GetProjectFollowingNames(context *gin.Context) { username := context.Param("username") @@ -218,6 +271,11 @@ func GetProjectFollowingNames(context *gin.Context) { context.JSON(http.StatusOK, following) } +// FollowProject handles POST requests to follow a project. +// It expects the `username` and `project_id` parameters in the URL. +// Returns: +// - Appropriate error code (404 if missing data, 500 if error) for database failures or invalid input. +// On success, responds with a 200 OK status and a confirmation message. func FollowProject(context *gin.Context) { username := context.Param("username") projectId := context.Param("project_id") @@ -230,6 +288,11 @@ func FollowProject(context *gin.Context) { context.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("%v now follows %v", username, projectId)}) } +// UnfollowProject handles DELETE requests to unfollow a project. +// It expects the `username` and `project_id` parameters in the URL. +// Returns: +// - Appropriate error code (404 if missing data, 500 if error) for database failures or invalid input. +// On success, responds with a 200 OK status and a confirmation message. func UnfollowProject(context *gin.Context) { username := context.Param("username") projectId := context.Param("project_id") diff --git a/backend/api/internal/handlers/user_routes.go b/backend/api/internal/handlers/user_routes.go index 8ed4f22..0681bcc 100644 --- a/backend/api/internal/handlers/user_routes.go +++ b/backend/api/internal/handlers/user_routes.go @@ -10,6 +10,13 @@ import ( "github.com/gin-gonic/gin" ) +// GetUsernameById handles GET requests to fetch a user by their username. +// It expects the `username` parameter in the URL. +// Returns: +// - 400 Bad Request if the username is invalid. +// - 404 Not Found if no user is found with the given username. +// - 500 Internal Server Error if a database query fails. +// On success, responds with a 200 OK status and the user data in JSON format. func GetUsernameById(context *gin.Context) { username := context.Param("username") @@ -27,6 +34,13 @@ func GetUsernameById(context *gin.Context) { context.JSON(http.StatusOK, user) } +// GetUserByUsername handles GET requests to fetch a user by their username. +// It expects the `username` parameter in the URL. +// Returns: +// - 400 Bad Request if the username is invalid. +// - 404 Not Found if no user is found with the given username. +// - 500 Internal Server Error if a database query fails. +// On success, responds with a 200 OK status and the user data in JSON format. func GetUserByUsername(context *gin.Context) { username := context.Param("username") @@ -44,6 +58,12 @@ func GetUserByUsername(context *gin.Context) { context.JSON(http.StatusOK, user) } +// CreateUser handles POST requests to create a new user. +// It expects a JSON body with the user details. +// Returns: +// - 400 Bad Request if the JSON is invalid or the user details are incomplete. +// - 500 Internal Server Error if an error occurs while creating the user. +// On success, responds with a 201 Created status and a message confirming the user creation. func CreateUser(context *gin.Context) { var newUser types.User err := context.BindJSON(&newUser) @@ -60,6 +80,13 @@ func CreateUser(context *gin.Context) { context.JSON(http.StatusCreated, gin.H{"message": fmt.Sprintf("Created new user: '%s'", newUser.Username)}) } +// DeleteUser handles DELETE requests to delete a user. +// It expects the `username` parameter in the URL. +// Returns: +// - 400 Bad Request if the username is invalid. +// - 404 Not Found if no user is found with the given username. +// - 500 Internal Server Error if a database query fails. +// On success, responds with a 200 OK status and a message confirming the user deletion. func DeleteUser(context *gin.Context) { username := context.Param("username") code, err := database.QueryDeleteUser(username) @@ -79,6 +106,13 @@ func DeleteUser(context *gin.Context) { context.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("User '%v' deleted.", username)}) } +// UpdateUserInfo handles PUT requests to update a user's information. +// It expects the `username` parameter in the URL and a JSON body with the updated data. +// Returns: +// - 400 Bad Request if the data is invalid or contains unallowed fields. +// - 404 Not Found if no user is found with the given username. +// - 500 Internal Server Error if an error occurs while updating the user data. +// On success, responds with a 200 OK status and a message confirming the update. func UpdateUserInfo(context *gin.Context) { // we dont want to create a whole new user, that is // why we dont use a user type here... @@ -139,6 +173,12 @@ func UpdateUserInfo(context *gin.Context) { context.JSON(http.StatusOK, gin.H{"message": "User updated successfully.", "user": validUser}) } +// GetUsersFollowers handles GET requests to fetch the list of user IDs who follow the specified user. +// It expects the `username` parameter in the URL. +// Returns: +// - 404 Not Found if no user is found with the given username. +// - 500 Internal Server Error if a database query fails. +// On success, responds with a 200 OK status and a list of follower IDs in JSON format. func GetUsersFollowers(context *gin.Context) { username := context.Param("username") @@ -151,6 +191,12 @@ func GetUsersFollowers(context *gin.Context) { context.JSON(http.StatusOK, followers) } +// GetUsersFollowing handles GET requests to fetch the list of user IDs that the specified user follows. +// It expects the `username` parameter in the URL. +// Returns: +// - 404 Not Found if no user is found with the given username. +// - 500 Internal Server Error if a database query fails. +// On success, responds with a 200 OK status and a list of following user IDs in JSON format. func GetUsersFollowing(context *gin.Context) { username := context.Param("username") @@ -163,6 +209,12 @@ func GetUsersFollowing(context *gin.Context) { context.JSON(http.StatusOK, following) } +// GetUsersFollowersUsernames handles GET requests to fetch the usernames of users who follow the specified user. +// It expects the `username` parameter in the URL. +// Returns: +// - 404 Not Found if no user is found with the given username. +// - 500 Internal Server Error if a database query fails. +// On success, responds with a 200 OK status and a list of follower usernames in JSON format. func GetUsersFollowersUsernames(context *gin.Context) { username := context.Param("username") @@ -175,6 +227,12 @@ func GetUsersFollowersUsernames(context *gin.Context) { context.JSON(http.StatusOK, gin.H{"message": "Successfully got followers", "followers":followers}) } +// GetUsersFollowingUsernames handles GET requests to fetch the usernames of users whom the specified user follows. +// It expects the `username` parameter in the URL. +// Returns: +// - 404 Not Found if no user is found with the given username. +// - 500 Internal Server Error if a database query fails. +// On success, responds with a 200 OK status and a list of following usernames in JSON format. func GetUsersFollowingUsernames(context *gin.Context) { username := context.Param("username") @@ -187,6 +245,12 @@ func GetUsersFollowingUsernames(context *gin.Context) { context.JSON(http.StatusOK, gin.H{"message": "Successfully got following", "following": following}) } +// FollowUser handles POST requests to create a follow relationship between a user and another user. +// It expects the `username` and `new_follow` parameters in the URL. +// Returns: +// - 400 Bad Request if the follow operation fails or the user is already following the other user. +// - 500 Internal Server Error if a database query fails. +// On success, responds with a 200 OK status and a message confirming the follow operation. func FollowUser(context *gin.Context) { username := context.Param("username") newFollow := context.Param("new_follow") @@ -199,6 +263,12 @@ func FollowUser(context *gin.Context) { context.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("%v now follows %v", username, newFollow)}) } +// UnfollowUser handles DELETE requests to remove a follow relationship between a user and another user. +// It expects the `username` and `unfollow` parameters in the URL. +// Returns: +// - 400 Bad Request if the unfollow operation fails or the user is not following the other user. +// - 500 Internal Server Error if a database query fails. +// On success, responds with a 200 OK status and a message confirming the unfollow operation. func UnfollowUser(context *gin.Context) { username := context.Param("username") unFollow := context.Param("unfollow") From 1a8f11182e12678fc0acde45b50e38a7c353578c Mon Sep 17 00:00:00 2001 From: Derek Corniello Date: Tue, 17 Dec 2024 10:52:34 -0500 Subject: [PATCH 4/4] added package level comments as well --- backend/api/internal/handlers/utils.go | 7 +++++++ backend/api/internal/logger/logger.go | 4 ++++ backend/api/internal/tests/main_test.go | 6 ++++++ backend/api/internal/types/types.go | 10 +++++++--- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/backend/api/internal/handlers/utils.go b/backend/api/internal/handlers/utils.go index 525afee..6931cbc 100644 --- a/backend/api/internal/handlers/utils.go +++ b/backend/api/internal/handlers/utils.go @@ -1,3 +1,10 @@ +// The handlers package includes all of the functionality to +// process http requests through the use of gin and its contexts +// in order for the workflow to process through to the database +// efficiently and effectively. +// +// it mainly uses net/http and gin-gonic/gin as packages to +// efficiently process http requests package handlers import ( diff --git a/backend/api/internal/logger/logger.go b/backend/api/internal/logger/logger.go index b71a8ff..cce8f80 100644 --- a/backend/api/internal/logger/logger.go +++ b/backend/api/internal/logger/logger.go @@ -1,3 +1,7 @@ +// The logger package helps set up the api system's logging system +// this package is used in conjunction with all of the other packages +// to provide both the backend and frontend good details on processes +// and any errors. package logger import ( diff --git a/backend/api/internal/tests/main_test.go b/backend/api/internal/tests/main_test.go index 77238ed..94a8a6b 100644 --- a/backend/api/internal/tests/main_test.go +++ b/backend/api/internal/tests/main_test.go @@ -1,3 +1,9 @@ +// The test package includes all of the functionality to +// test the api in any way we see fit. +// +// it uses all of the packages, along with the +// testing and assert packages to ensure that the tests are +// properly ran effectively and return the correct results package tests import ( diff --git a/backend/api/internal/types/types.go b/backend/api/internal/types/types.go index b9c01ce..4188e2f 100644 --- a/backend/api/internal/types/types.go +++ b/backend/api/internal/types/types.go @@ -1,12 +1,16 @@ +// the types package is used for creating types that will be +// used multiple times across many packages, so we can make +// use of all of the types in a single package +// +// Some of the structs that will be used to interface between +// frontend, api, and db. This will allow for good handling of +// types package types import ( "time" ) -// Some of the structs that will be used to interface between -// frontend, api, and db. Go has good handling for this - type User struct { Username string `json:"username" binding:"required"` Bio string `json:"bio"`