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

Cc 08 data validation #59

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 2 additions & 1 deletion api/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ require (
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
3 changes: 3 additions & 0 deletions api/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
Expand All @@ -116,6 +118,7 @@ golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
Expand Down
231 changes: 230 additions & 1 deletion api/src/model/types.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package model

import (
"errors"
"regexp"
"time"

"github.com/lib/pq"
"golang.org/x/exp/slices"
"gorm.io/gorm"
"time"
)

type Gift struct {
Expand All @@ -13,6 +17,8 @@ type Gift struct {
Link string
Description string
Demographic string
Category pq.StringArray `gorm:"type:text[]"`
Occasion string
GiftCollections []*GiftCollection `gorm:"many2many:gift_request_gifts;"`
}

Expand Down Expand Up @@ -66,3 +72,226 @@ type Admin struct {
UserID uint
User User
}

func (gift *Gift) BeforeSave(tx *gorm.DB) (err error) {
if len(gift.Name) == 0 {
err = errors.New("gift name cannot be empty")
return err
}

if gift.Price < 0 {
err = errors.New("gift must have a price")
return err
}

// if a gift has no link
if len(gift.Link) == 0 {
err = errors.New("gift must have a link")
return err
}

Demographics := []string{
"For her",
"For him",
"For kids",
"For mom",
"For dad",
"For women",
"For men",
}

for _, demographic := range Demographics {
if !slices.Contains(Demographics, demographic) {
err = errors.New("gift must have a valid demographic")
return err
}
}

Occasions := []string{
"Birthday",
"Bridal",
"Get well soon",
"New baby",
"Thinking of you",
"Thank you",
}

for _, occasion := range Occasions {
if !slices.Contains(Occasions, occasion) {
err = errors.New("gift must have a valid occasion")
return err
}
}

Categories := []string{
"Best selling",
"Fun",
"Gadgets",
"Home",
"Jewelry",
"Kitchen & bar",
"Warm and cozy",
}

for _, category := range Categories {
if !slices.Contains(Categories, category) {
err = errors.New("gift must have a valid category")
return err
}
}

// if a gift has no description
if len(gift.Description) == 0 {
err = errors.New("gift must have a description")
return err
}

return
}

func (gc *GiftCollection) BeforeSave(tx *gorm.DB) (err error) {
// if collection name is not set
if len(gc.CollectionName) == 0 {
err = errors.New("giftCollection must have a name")
return err
}

// if customer is not found
if gc.Customer == nil {
err = errors.New("giftCollection must have a customer")
return err
}

return
}

func (user *User) BeforeSave(tx *gorm.DB) (err error) {
if len(user.FirstName) == 0 {
err = errors.New("user must have a first name")
return err
}

if len(user.LastName) == 0 {
err = errors.New("user must have a last name")
return err
}

if len(user.Password) == 0 {
err = errors.New("Please enter a password")
return err
}

// use regex to validate email
pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
regex := regexp.MustCompile(pattern)

// Use the MatchString function to check if the text matches the pattern
if regex.MatchString(user.Email) == false {
err = errors.New("user email must be a valid email")
return err
}

return
}

func (customer *Customer) BeforeSave(tx *gorm.DB) (err error) {
// if giftRequests is not empty or not populated
if customer.GiftRequests == nil {
err = errors.New("customer must have a giftRequests")
return err
}

return
}

func (giftRequest *GiftRequest) BeforeSave(tx *gorm.DB) (err error) {
// if recipient name is not set
if len(giftRequest.RecipientName) == 0 {
err = errors.New("giftRequest must have a recipient name")
return err
}

// if recipient age is not set
if giftRequest.RecipientAge < 1 || giftRequest.RecipientAge > 150 {
err = errors.New("giftRequest must have a valid recipient age")
return err
}

GiftOccasions := []string{
"Birthday",
"Bridal",
"Get well soon",
"New baby",
"Thinking of you",
"Thank you",
}

// if occasion is not in GiftOccasions
if giftRequest.Occasion != nil {
for _, occasion := range giftRequest.Occasion {
if !slices.Contains(GiftOccasions, occasion) {
err = errors.New("giftRequest must have a valid occasion")
return err
}
}
} else {
err = errors.New("giftRequest must have an occasion")
return err
}

Interests := []string{
"Best selling",
"Fun",
"Gadgets",
"Home",
"Jewelry",
"Kitchen & bar",
"Warm and cozy",
}

// if interests is not in Interests
if giftRequest.RecipientInterests != nil {
for _, interest := range giftRequest.RecipientInterests {
if !slices.Contains(Interests, interest) {
err = errors.New("giftRequest must have a valid interest")
return err
}
}
} else {
err = errors.New("giftRequest must have an interest")
return err
}

// if either budget is below negative
if giftRequest.BudgetMax < 0 || giftRequest.BudgetMin < 0 {
err = errors.New("giftRequest budget cannot be negative")
return err
}

// if max budget is less than min budget
if giftRequest.BudgetMax <= giftRequest.BudgetMin {
err = errors.New("giftRequest max budget must be greater than min budget")
return err
}

// if date needed is not set
if giftRequest.DateNeeded.IsZero() {
err = errors.New("giftRequest must have a date needed")
return err
} else if giftRequest.DateNeeded.Before(time.Now()) {
// if date needed is in the past
err = errors.New("giftRequest date needed must be in the future")
return err
}

return
}

func (giftResponse *GiftResponse) BeforeSave(tx *gorm.DB) (err error) {
if giftResponse.CustomMessage == "" {
err = errors.New("giftResponse must have a custom message")
return err
}

return
}
4 changes: 3 additions & 1 deletion api/tests/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package tests

import (
"CaitsCurates/backend/src/model"
"github.com/stretchr/testify/assert"
"os"
"testing"
"time"

"github.com/stretchr/testify/assert"

"gorm.io/driver/postgres"
"gorm.io/gorm"
)
Expand Down Expand Up @@ -274,6 +275,7 @@ func TestGiftResponseModel(t *testing.T) {
CollectionName: "Cool Toys",
Gifts: []*model.Gift{&gift1, &gift2},
}

// Save the gifts
tx.Save(&gift1)
tx.Save(&gift2)
Expand Down
22 changes: 12 additions & 10 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@

import {
BrowserRouter as Router,
Route,
Routes,
BrowserRouter as Router,
Route,
Routes,
} from 'react-router-dom';
import GiftManagementPage from './pages/GiftManagementPage';
import HomePage from './pages/HomePage';

function App() {
return (
<Router>
<Routes>
<Route path = '/' element={<HomePage/>} />
</Routes>
</Router>
);
return (
<Router>
<Routes>
<Route path = '/' element={<HomePage/>} />
<Route path = '/giftManagement' element={<GiftManagementPage/>} />
</Routes>
</Router>
);
}

export default App;
37 changes: 37 additions & 0 deletions client/src/components/GiftItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react'

interface GiftProps {
name: string
description: string
price: number
link: string
demographic: string
giftCollections: string
}

const GiftItem = (props: GiftProps) => {
return (
<div className='border-2 rounded flex flex-col justify-between p-4 max-w-full' style={{margin: '20px 20px'}}>
<h2>{props.name}</h2>

<div className='flex flex-col mb-3'>

{/** some container that holds the prics and link */}
<div className='flex flex-row space-x-3'>
<p>Price: ${props.price}</p>
<a href="#">Buy Now</a>
</div>
<p>Demographic: {props.demographic}</p>
<p>Description: {props.description}</p>
<p>Collections: {props.giftCollections}</p>
</div>

<div className='w-1/12 flex flex-row space-x-2'>
<button className='px-2 rounded bg-rose-500'>Edit</button>
<button className='px-1 rounded bg-gray-300'>Delete</button>
</div>
</div>
)
}

export default GiftItem
9 changes: 9 additions & 0 deletions client/src/components/MiniTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import React from 'react'

const MiniTab = () => {
return (
<div>MiniTab</div>
)
}

export default MiniTab
Loading