Skip to content

Commit

Permalink
Merge branch 'main' into SCRUM-43-Plaid-frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan Saperstein committed Dec 5, 2024
2 parents 7558107 + 3b87adf commit 6154af7
Show file tree
Hide file tree
Showing 145 changed files with 7,817 additions and 4,582 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
/node_modules
*.p12
*.env
.env*
13 changes: 0 additions & 13 deletions backend/internal/auth/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package auth
import (
"backend/internal/api_errors"
"backend/internal/config"
"backend/internal/transactions"

"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt/v5"
Expand Down Expand Up @@ -36,18 +35,6 @@ func (a *AuthFactory) Middleware() func(ctx *fiber.Ctx) error {
return err
}

investorExists, err := transactions.CheckInvestorExists(a.DB, subject)
if err != nil {
return err
}

if !investorExists {
err := transactions.CreateInvestor(a.DB, subject)
if err != nil {
return err
}
}

ctx.Locals("userId", subject)
return ctx.Next()
}
Expand Down
67 changes: 65 additions & 2 deletions backend/internal/controllers/investors.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,54 @@ func (c *InvestorsController) GetProfile(ctx *fiber.Ctx) error {
return ctx.Status(fiber.StatusOK).JSON(investorProfile)
}

func (c *InvestorsController) CreateProfile(ctx *fiber.Ctx) error {
userId, ok := ctx.Locals("userId").(string)
if !ok {
return &api_errors.INVALID_UUID
}

id, err := uuid.Parse(userId)
if err != nil {
return &api_errors.INVALID_UUID
}

var body models.InvestorProfile
if err := ctx.BodyParser(&body); err != nil {
return &api_errors.INVALID_REQUEST_BODY
}

err = transactions.CreateInvestor(c.ServiceParams.DB, id.String(), body)
if err != nil {
return err
}

return ctx.SendStatus(fiber.StatusOK)
}

func (c *InvestorsController) UpdateProfile(ctx *fiber.Ctx) error {
userId, ok := ctx.Locals("userId").(string)
if !ok {
return &api_errors.INVALID_UUID
}

id, err := uuid.Parse(userId)
if err != nil {
return &api_errors.INVALID_UUID
}

var body models.InvestorProfile
if err := ctx.BodyParser(&body); err != nil {
return &api_errors.INVALID_REQUEST_BODY
}

updatedInvestorProfile, err := transactions.UpdateProfile(c.ServiceParams.DB, id, body)
if err != nil {
return err
}

return ctx.Status(fiber.StatusOK).JSON(updatedInvestorProfile)
}

func (c *InvestorsController) GetPortfolio(ctx *fiber.Ctx) error {
userId, ok := ctx.Locals("userId").(string)
if !ok {
Expand Down Expand Up @@ -143,12 +191,27 @@ func (c *InvestorsController) GetInvestor(ctx *fiber.Ctx) error {

investor := models.Investor{
ID: id,
FirstName: profile.FirstName,
LastName: profile.LastName,
Profile: profile,
TotalInvestmentAmount: totalValue,
InvestmentBreakdown: investments,
CashBalance: cashBalance,
}

return ctx.Status(fiber.StatusOK).JSON(investor)
}

func (c *InvestorsController) GetCashBalance(ctx *fiber.Ctx) error {
userId, ok := ctx.Locals("userId").(string)
if !ok {
return &api_errors.INVALID_UUID
}

balance, err := transactions.GetCashBalance(c.ServiceParams.DB, userId)
if err != nil {
return err
}

return ctx.Status(fiber.StatusOK).JSON(fiber.Map{
"cash_balance_cents": balance,
})
}
25 changes: 20 additions & 5 deletions backend/internal/controllers/plaid.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,22 @@ func (c *PlaidController) CreateLinkToken(ctx *fiber.Ctx) error {
// Specify the products
request.SetProducts([]plaid.Products{plaid.PRODUCTS_AUTH, plaid.PRODUCTS_TRANSACTIONS})

redirectUri := "threestones://hosted-link-complete"
isMobileApp := true
request.SetHostedLink(plaid.LinkTokenCreateHostedLink{
CompletionRedirectUri: &redirectUri,
IsMobileApp: &isMobileApp,
})

// Execute the request to create a link token
response, _, err := c.ServiceParams.Plaid.PlaidApi.LinkTokenCreate(ctx.Context()).LinkTokenCreateRequest(*request).Execute()
if err != nil {
return err
}

return ctx.Status(fiber.StatusOK).JSON(fiber.Map{
"link_token": response.GetLinkToken(),
"link_token": response.GetLinkToken(),
"hosted_link_url": response.GetHostedLinkUrl(),
})
}

Expand Down Expand Up @@ -156,15 +164,22 @@ func (c *PlaidController) Invest(ctx *fiber.Ctx) error {
return err
}

// Record the investment
err = transactions.RecordInvestment(c.ServiceParams.DB, id, body.PropertyID, body.Amount, "")
transactionId, err := transactions.RecordInvestment(c.ServiceParams.DB, id, body.PropertyID, body.Amount)
if err != nil {
return err
}

type ResponseBody struct {
TransactionId uuid.UUID `json:"transaction_id"`
NominalCents int `json:"nominal_cents"`
AdministrativeCents int `json:"administrative_cents"`
}

// Return success response
return ctx.Status(fiber.StatusOK).JSON(fiber.Map{
"message": "Investment successful",
return ctx.Status(fiber.StatusOK).JSON(ResponseBody{
TransactionId: transactionId,
NominalCents: amountCents,
AdministrativeCents: 0,
})
}

Expand Down
25 changes: 25 additions & 0 deletions backend/internal/controllers/projects.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package controllers

import (
"strings"

"backend/internal/api_errors"
"backend/internal/models"
"backend/internal/transactions"
Expand Down Expand Up @@ -29,6 +31,29 @@ func (c *ProjectsController) GetProjects(ctx *fiber.Ctx) error {
return ctx.JSON(projects)
}

func (c *ProjectsController) SearchProjects(ctx *fiber.Ctx) error {
searchProjectsQuery := new(models.SearchRequestBody)

// parses the incoming request body into the searchProjectsQuery struct
// returns an error if there was an issue such as missing fields
if err := ctx.BodyParser(searchProjectsQuery); err != nil {
return &api_errors.INVALID_REQUEST_BODY
}

// Split into words
words := strings.Split(searchProjectsQuery.Query, " ")

// Join words with | for full text search
parsedQuery := strings.Join(words, " | ")

searchProjects, err := transactions.SearchProjects(c.ServiceParams.DB, parsedQuery)
if err != nil {
return err
}

return ctx.JSON(searchProjects)
}

func (c *ProjectsController) GetProjectById(ctx *fiber.Ctx) error {
idParam := ctx.Params("id")
id, err := uuid.Parse(idParam)
Expand Down
17 changes: 13 additions & 4 deletions backend/internal/models/investor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,25 @@ import "github.com/google/uuid"

type Investor struct {
ID uuid.UUID `json:"id"`
FirstName string `json:"first"`
LastName string `json:"last"`
Profile InvestorProfile `json:"profile"`
TotalInvestmentAmount int `json:"total_investment_amount"`
InvestmentBreakdown map[uuid.UUID]int `json:"investment_breakdown"`
CashBalance int `json:"cash_balance"`
}

type InvestorProfile struct {
FirstName string `json:"first"`
LastName string `json:"last"`
FirstName string `json:"first"`
LastName string `json:"last"`
Email string `json:"email"`
PhoneNumber string `json:"phone_number"`
SSN string `json:"ssn"`
Premise string `json:"premise"`
Subpremise string `json:"subpremise"`
Street string `json:"street"`
Locality string `json:"locality"`
State string `json:"state"`
Zipcode string `json:"zipcode"`
ProfilePictureUrl string `json:"profile_picture_url"`
}

type HistoryEntry struct {
Expand Down
5 changes: 5 additions & 0 deletions backend/internal/models/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ type Project struct {
Latitude float32 `json:"latitude"`
Longitude float32 `json:"longitude"`
Images []ImageLink `json:"images"`
CompletionDate string `json:"completion_date"`
}

type InvestRequestBody struct {
Amount int32 `json:"amount"`
}

type SearchRequestBody struct {
Query string `json:"query"`
}

type ProjectPost struct {
ID uuid.UUID `json:"id"`
CreatedAt time.Time `json:"created_at"`
Expand Down
3 changes: 3 additions & 0 deletions backend/internal/routes/investors.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ func Investors(params types.RouterParams) {
// api/v1/investors/*
investors := params.Router.Group("/investors")
investors.Get("/profile", params.Auth.Middleware(), investorsController.GetProfile)
investors.Post("/profile", params.Auth.Middleware(), investorsController.CreateProfile)
investors.Put("/profile", params.Auth.Middleware(), investorsController.UpdateProfile)
investors.Get("/portfolio", params.Auth.Middleware(), investorsController.GetPortfolio)
investors.Get("/history", params.Auth.Middleware(), investorsController.GetHistory)
investors.Get("/", params.Auth.Middleware(), investorsController.GetInvestor)
investors.Get("/balance", params.Auth.Middleware(), investorsController.GetCashBalance)
}
8 changes: 8 additions & 0 deletions backend/internal/routes/projects.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package routes
import (
"backend/internal/controllers"
"backend/internal/types"

"github.com/gofiber/fiber/v2"
)

func Projects(params types.RouterParams) {
Expand All @@ -11,9 +13,15 @@ func Projects(params types.RouterParams) {
// api/v1/projects/*
projects := params.Router.Group("/projects")

projects.Use("/search-projects", func(c *fiber.Ctx) error {
c.Set("Allow", "POST, GET, HEAD")
return c.Next()
})

projects.Get("/", params.Auth.Middleware(), projectsController.GetProjects)
projects.Get("/:id", params.Auth.Middleware(), projectsController.GetProjectById)
projects.Get("/:id/total-funded", params.Auth.Middleware(), projectsController.GetProjectTotalFunded)
projects.Post("/:id/invest", params.Auth.Middleware(), projectsController.Invest)
projects.Get("/:id/posts", params.Auth.Middleware(), projectsController.GetProjectPosts)
projects.Post("/search-projects", params.Auth.Middleware(), projectsController.SearchProjects)
}
Loading

0 comments on commit 6154af7

Please sign in to comment.