Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NOT YET #22

Merged
merged 5 commits into from
Feb 27, 2024
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
1 change: 1 addition & 0 deletions backend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func main() {
clerkClient := routes.SetupAuthRoutes(r, db)
routes.SetupUserRoutes(r, db, clerkClient)
routes.SetupETradeRoutes(r, db)
routes.SetupPostRoutes(r, db)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

going to add a change on Cam's branch to. move all of these to helper fyi

routes.SetupOnboardingRoutes(r, db)

r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
Expand Down
186 changes: 186 additions & 0 deletions backend/src/controllers/post.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package controllers

import (
"net/http"
"strconv"

"backend/src/models"
"backend/src/services"

"github.com/gin-gonic/gin"
)

type PostController struct {
postService *services.PostService
}

func NewPostController(postService *services.PostService) *PostController {
return &PostController{
postService: postService,
}
}

// GetAllPosts godoc
//
// @Summary Gets all posts
// @Description Returns all posts
// @ID get-all-posts
// @Tags post
// @Produce json
// @Success 200 {object} []models.Post
// @Failure 404 {string} string "Failed to fetch posts"
// @Router /api/posts/ [get]
func (pc *PostController) GetAllPosts(c *gin.Context) {
posts, err := pc.postService.GetAllPosts()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch posts"})
return
}

c.JSON(http.StatusOK, posts)
}

// GetPostsByUserId godoc
//
// @Summary Gets posts by user ID
// @Description Returns all posts for a specific user by user ID
// @ID get-posts-by-user-id
// @Tags post
// @Produce json
// @Param userId path uint true "User ID"
// @Success 200 {object} []models.Post
// @Failure 404 {string} string "Failed to fetch posts"
// @Router /api/posts/user/{userId} [get]
func (pc *PostController) GetPostsByUserId(c *gin.Context) {
userId, err := strconv.ParseUint(c.Param("userId"), 10, 32)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
return
}

posts, err := pc.postService.GetPostsByUserId(uint(userId))
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Failed to fetch posts"})
return
}

c.JSON(http.StatusOK, posts)
}

// CreatePost godoc
//
// @Summary Creates a post
// @Description Creates a post
// @ID create-post
// @Tags post
// @Accept json
// @Produce json
// @Param first_name body string true "First name of the post"
// @Param last_name body string true "Last name of the post"
// @Param postname body string true "Postname of the post"
// @Param email body string true "Email of the post"
// @Param password body string true "Password of the post"
// @Success 201 {object} models.Post
// @Failure 400 {string} string "Failed to create post"
// @Router /api/posts/ [post]
func (pc *PostController) CreatePost(c *gin.Context) {
var post models.Post
if err := c.ShouldBindJSON(&post); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"})
return
}

createdPost, err := pc.postService.CreatePost(&post)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to create post"})
return
}

c.JSON(http.StatusCreated, createdPost)
}

// GetPostById godoc
//
// @Summary Gets a post by id
// @Description Returns a post by id
// @ID get-post-by-id
// @Tags post
// @Produce json
// @Param id path int true "ID of the post"
// @Success 200 {object} models.Post
// @Failure 404 {string} string "Failed to fetch post"
// @Router /api/posts/{id} [get]
func (pc *PostController) GetPostById(c *gin.Context) {
id := c.Param("id")
postID, err := strconv.ParseUint(id, 10, 32)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid post ID"})
return
}

post, err := pc.postService.GetPostById(uint(postID))
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Failed to fetch post"})
return
}

c.JSON(http.StatusOK, post)
}

// UpdatePostById godoc
//
// @Summary Updates a post by id
// @Description Updates a post by id
// @ID update-post-by-id
// @Tags post
// @Accept json
// @Produce json
// @Param id path int true "ID of the post"
// @Param first_name body string true "First name of the post"
// @Param last_name body string true "Last name of the post"
// @Param postname body string true "Postname of the post"
// @Param email body string true "Email of the post"
// @Param password body string true "Password of the post"
// @Success 200 {object} models.Post
// @Failure 400 {string} string "Failed to update post"
// @Router /api/posts/{id} [put]
func (pc *PostController) UpdatePostById(c *gin.Context) {
id := c.Param("id")
postID, _ := strconv.ParseUint(id, 10, 32)
var post *models.Post
if err := c.ShouldBindJSON(&post); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input"})
return
}

updatedPost, err := pc.postService.UpdatePostById(uint(postID), post)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Failed to update post"})
return
}

c.JSON(http.StatusOK, updatedPost)
}

// DeletePostById godoc
//
// @Summary Deletes a post by id
// @Description Deletes a post by id
// @ID delete-post-by-id
// @Tags post
// @Produce json
// @Param id path int true "ID of the post"
// @Success 200 {object} models.Post
// @Failure 404 {string} string "Failed to delete post"
// @Router /api/posts/{id} [delete]
func (pc *PostController) DeletePostById(c *gin.Context) {
id := c.Param("id")
postID, _ := strconv.ParseUint(id, 10, 32)
post, err := pc.postService.DeletePostById(uint(postID))
if err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Failed to delete post"})
return
}

c.JSON(http.StatusOK, post)
}
30 changes: 30 additions & 0 deletions backend/src/db/migrations/7_POST_V1.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
DROP TABLE IF EXISTS post_type_enum;
DROP TABLE IF EXISTS posts;

--Create post type table
CREATE TYPE post_type_enum AS ENUM (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For enum can we do Caps / underscore case? IE '1_MONTH_SUM'

Also comment is the stock performance comment right?

'1 month summary',
'Recent trade',
'Share comment'
);

--Create post table
CREATE TABLE IF NOT EXISTS posts (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
post_type post_type_enum NOT NULL,
num_data FLOAT NOT NULL,
ticker_symbol VARCHAR(9),
time_stamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the gorm / sql format for this would be created_at? everything else is good

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just submitted

comment TEXT NOT NULL,
title VARCHAR NOT NULL
);

INSERT INTO posts (user_id, post_type, num_data, ticker_symbol, comment, title)
VALUES
(1, '1 month summary', 100, 'AAPL', 'I made a 100% return on my investment in Apple this month!', 'Apple Investment'),
(1, 'Recent trade', 200, 'TSLA', 'I just bought 200 shares of Tesla!', 'Tesla Investment'),
(1, 'Share comment', 150, 'MSFT', 'I think Microsoft is a great company to invest in!', 'Microsoft Comment'),
(1, '1 month summary', 250, 'GOOGL', 'I made a 250% return on my investment in Google this month!', 'Google Investment'),
(1, 'Recent trade', 300, 'AMZN', 'I just bought 300 shares of Amazon!', 'Amazon Investment'),
(1, 'Share comment', 400, 'FB', 'I think Facebook is a great company to invest in!', 'Facebook Comment');
27 changes: 27 additions & 0 deletions backend/src/models/post.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package models

import (
"backend/src/types"
"time"
)

type PostType string

const (
ONE_MONTH_SUMMARY PostType = "1 month summary"
RECENT_TRADE PostType = "Recent trade"
SHARE_COMMENT PostType = "Share comment"
)

type Post struct {
types.Model
UserID uint `gorm:"not null" json:"user_id"`
User User `gorm:"foreignKey:UserID"`
PostType PostType `gorm:"type:post_type_enum;not null" json:"post_type"`
NumData float64 `gorm:"not null" json:"num_data"`
TickerSymbol string `gorm:"type:varchar(9);" json:"ticker_symbol"`
TimeStamp time.Time `gorm:"default:created_at" json:"time_stamp"`
Comment string `gorm:"not null" json:"comment"`
Title string `gorm:"not null" json:"title"`
}

1 change: 0 additions & 1 deletion backend/src/routes/etrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ func SetupETradeRoutes(router *gin.Engine, db *gorm.DB) {
etradeService := services.NewETradeService(db)
etradeController := controllers.NewETradeController(etradeService)


etradeRoutes := router.Group("/etrade")
{
etradeRoutes.GET("/redirect/:user_id", etradeController.GetRedirectURL)
Expand Down
24 changes: 24 additions & 0 deletions backend/src/routes/post.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package routes

import (
"backend/src/controllers"
"backend/src/services"

"github.com/gin-gonic/gin"
"gorm.io/gorm"
)

func SetupPostRoutes(router *gin.Engine, db *gorm.DB) {
postService := services.NewPostService(db)
postController := controllers.NewPostController(postService)

postRoutes := router.Group("/posts")
{
postRoutes.GET("/", postController.GetAllPosts)
postRoutes.GET("/user-posts/:userId", postController.GetPostsByUserId)
postRoutes.POST("/", postController.CreatePost)
postRoutes.GET("/:id", postController.GetPostById)
postRoutes.PUT("/:id", postController.UpdatePostById)
postRoutes.DELETE("/:id", postController.DeletePostById)
}
}
73 changes: 73 additions & 0 deletions backend/src/services/post.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package services

import (
"backend/src/models"

"gorm.io/gorm"
)

type PostService struct {
DB *gorm.DB
}

func NewPostService(db *gorm.DB) *PostService {
return &PostService{
DB: db,
}
}

func (ps *PostService) GetAllPosts() ([]models.Post, error) {
var posts []models.Post
if err := ps.DB.Find(&posts).Error; err != nil {
return nil, err
}
return posts, nil
}

func (ps *PostService) GetPostsByUserId(userId uint) ([]models.Post, error) {
var posts []models.Post
if err := ps.DB.Where("user_id = ?", userId).Find(&posts).Error; err != nil {
return nil, err
}
return posts, nil
}

//TODO: Add GetPostsFromFollowedUsers once Cam is done

func (ps *PostService) CreatePost(post *models.Post) (*models.Post, error) {
if err := ps.DB.Create(post).Error; err != nil {
return nil, err
}
return post, nil
}

func (ps *PostService) GetPostById(id uint) (*models.Post, error) {
post := &models.Post{}
if err := ps.DB.First(post, id).Error; err != nil {
return nil, err
}
return post, nil
}

func (ps *PostService) UpdatePostById(id uint, post *models.Post) (*models.Post, error) {
// Retrieve the existing post from the database
_, err := ps.GetPostById(id)
if err != nil {
return nil, err
}

// Save the updated post back to the database
if err := ps.DB.Save(post).Error; err != nil {
return nil, err
}

return post, nil
}

func (ps *PostService) DeletePostById(id uint) (*models.Post, error) {
post := &models.Post{}
if err := ps.DB.Delete(post, id).Error; err != nil {
return nil, err
}
return post, nil
}
Loading