From 7211d51c06d4c38be9697aac7149ed1fb0931ed5 Mon Sep 17 00:00:00 2001 From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com> Date: Mon, 15 Apr 2024 16:19:56 -0400 Subject: [PATCH] feat: support for majors | model struct tag optimizations/cleanup | testing sync (#547) --- backend/src/database/super.go | 1 + backend/src/file/file.go | 1 - backend/src/models/category.go | 2 +- backend/src/models/club.go | 14 +-- backend/src/models/comment.go | 14 +-- backend/src/models/contact.go | 6 +- backend/src/models/event.go | 44 +++++----- backend/src/models/file.go | 10 +-- backend/src/models/follower.go | 4 +- backend/src/models/membership.go | 6 +- backend/src/models/notification.go | 14 +-- backend/src/models/poc.go | 8 +- backend/src/models/root.go | 6 +- backend/src/models/tag.go | 4 +- backend/src/models/user.go | 135 ++++++++++++++++++++++++++--- backend/src/models/verification.go | 4 +- backend/src/services/event.go | 1 + backend/src/utilities/validator.go | 11 +++ backend/tests/api/club_test.go | 2 - backend/tests/api/event_test.go | 83 +++++++++++------- backend/tests/api/helpers/auth.go | 5 ++ backend/tests/api/user_test.go | 20 ++++- 22 files changed, 280 insertions(+), 115 deletions(-) diff --git a/backend/src/database/super.go b/backend/src/database/super.go index 9d357ba24..7cc9c2b8d 100644 --- a/backend/src/database/super.go +++ b/backend/src/database/super.go @@ -22,6 +22,7 @@ func SuperUser(superUserSettings config.SuperUserSettings) (*models.User, *error PasswordHash: *passwordHash, FirstName: "SAC", LastName: "Super", + Major0: models.ComputerScience, College: models.KCCS, GraduationCycle: models.May, GraduationYear: 2025, diff --git a/backend/src/file/file.go b/backend/src/file/file.go index 9314ffce5..9e292dbe1 100644 --- a/backend/src/file/file.go +++ b/backend/src/file/file.go @@ -128,7 +128,6 @@ func (aw *AWSClient) UploadFile(folder string, fileHeader *multipart.FileHeader, Body: bytes.NewReader(file), }) if s3Err != nil { - fmt.Printf("Failed to upload data to %s/%s, %v\n", bucket, key, err) return nil, &errors.FailedToUploadFile } diff --git a/backend/src/models/category.go b/backend/src/models/category.go index 6f9770557..a6d757d10 100644 --- a/backend/src/models/category.go +++ b/backend/src/models/category.go @@ -3,7 +3,7 @@ package models type Category struct { Model - Name string `gorm:"type:varchar(255);unique" json:"name" validate:"required,max=255"` + Name string `gorm:"type:varchar(255);unique;not null" json:"name" validate:"required,max=255"` Tag []Tag `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` } diff --git a/backend/src/models/club.go b/backend/src/models/club.go index 23c931720..2e4b31918 100644 --- a/backend/src/models/club.go +++ b/backend/src/models/club.go @@ -30,13 +30,13 @@ type Club struct { SoftDeletedAt gorm.DeletedAt `gorm:"type:timestamptz;default:NULL" json:"-" validate:"-"` - Name string `gorm:"type:varchar(255)" json:"name" validate:"required,max=255"` - Preview string `gorm:"type:varchar(255)" json:"preview" validate:"required,max=255"` - Description string `gorm:"type:text" json:"description" validate:"required,http_url,s3_url,max=255"` // S3 URL - NumMembers int `gorm:"type:int" json:"num_members" validate:"required,min=1"` - IsRecruiting bool `gorm:"type:bool;default:false" json:"is_recruiting" validate:"required"` - RecruitmentCycle RecruitmentCycle `gorm:"type:varchar(255);default:always" json:"recruitment_cycle" validate:"required,max=255,oneof=fall spring fallSpring always"` - RecruitmentType RecruitmentType `gorm:"type:varchar(255);default:unrestricted" json:"recruitment_type" validate:"required,max=255,oneof=unrestricted tryout application"` + Name string `gorm:"type:varchar(255);not null" json:"name" validate:"required,max=255"` + Preview string `gorm:"type:varchar(255);not null" json:"preview" validate:"required,max=255"` + Description string `gorm:"type:text;not null" json:"description" validate:"required,http_url,s3_url,max=255"` // S3 URL + NumMembers int `gorm:"type:int;not null" json:"num_members" validate:"required,min=1"` + IsRecruiting bool `gorm:"type:bool;default:false;not null" json:"is_recruiting" validate:"required"` + RecruitmentCycle RecruitmentCycle `gorm:"type:varchar(255);default:always;not null" json:"recruitment_cycle" validate:"required,max=255,oneof=fall spring fallSpring always"` + RecruitmentType RecruitmentType `gorm:"type:varchar(255);default:unrestricted;not null" json:"recruitment_type" validate:"required,max=255,oneof=unrestricted tryout application"` ApplicationLink string `gorm:"type:varchar(255);default:NULL" json:"application_link" validate:"required,max=255,http_url"` Logo string `gorm:"type:varchar(255);default:NULL" json:"logo" validate:"omitempty,http_url,s3_url,max=255"` // S3 URL diff --git a/backend/src/models/comment.go b/backend/src/models/comment.go index 9e2bb9eb1..30448d0fd 100644 --- a/backend/src/models/comment.go +++ b/backend/src/models/comment.go @@ -5,15 +5,15 @@ import "github.com/google/uuid" type Comment struct { Model - Question string `gorm:"type:varchar(255)" json:"question" validate:"required,max=255"` - Answer string `gorm:"type:varchar(255)" json:"answer" validate:",max=255"` - NumFoundHelpful uint `gorm:"type:int;default:0" json:"num_found_helpful" validate:"min=0"` + Question string `gorm:"type:varchar(255);not null" json:"question" validate:"required,max=255"` + Answer string `gorm:"type:varchar(255);not null" json:"answer" validate:",max=255"` + NumFoundHelpful uint `gorm:"type:int;default:0;not null" json:"num_found_helpful" validate:"min=0"` - AskedByID uuid.UUID `gorm:"type:uuid" json:"-" validate:"uuid4"` - AskedBy User `gorm:"foreignKey:AskedByID" json:"-" validate:"-"` + AskedByID uuid.UUID `gorm:"type:uuid;not null" json:"-" validate:"uuid4"` + AskedBy User `gorm:"foreignKey:AskedByID;not null" json:"-" validate:"-"` - ClubID uuid.UUID `gorm:"type:uuid" json:"-" validate:"uuid4"` - Club Club `gorm:"foreignKey:ClubID" json:"-" validate:"-"` + ClubID uuid.UUID `gorm:"type:uuid;not null" json:"-" validate:"uuid4"` + Club Club `gorm:"foreignKey:ClubID;not null" json:"-" validate:"-"` AnsweredByID *uuid.UUID `gorm:"type:uuid" json:"-" validate:"uuid4"` AnsweredBy *User `gorm:"foreignKey:AnsweredBy" json:"-" validate:"-"` diff --git a/backend/src/models/contact.go b/backend/src/models/contact.go index 25993f43c..dc5280332 100644 --- a/backend/src/models/contact.go +++ b/backend/src/models/contact.go @@ -43,10 +43,10 @@ func GetContentPrefix(contactType ContactType) string { type Contact struct { Model - Type ContactType `gorm:"type:varchar(255);uniqueIndex:idx_contact_type" json:"type" validate:"required,max=255,oneof=facebook instagram x linkedin youtube github slack discord email customSite"` - Content string `gorm:"type:varchar(255)" json:"content" validate:"required,max=255"` + Type ContactType `gorm:"type:varchar(255);uniqueIndex:idx_contact_type;not null" json:"type" validate:"required,max=255,oneof=facebook instagram x linkedin youtube github slack discord email customSite"` + Content string `gorm:"type:varchar(255);not null" json:"content" validate:"required,max=255"` - ClubID uuid.UUID `gorm:"foreignKey:ClubID;uniqueIndex:idx_contact_type" json:"-" validate:"uuid4"` + ClubID uuid.UUID `gorm:"foreignKey:ClubID;uniqueIndex:idx_contact_type;not null" json:"-" validate:"uuid4"` } type PutContactRequestBody struct { diff --git a/backend/src/models/event.go b/backend/src/models/event.go index a5cf04cd7..3ec9c3db9 100644 --- a/backend/src/models/event.go +++ b/backend/src/models/event.go @@ -24,16 +24,16 @@ const ( type Event struct { Model - Name string `gorm:"type:varchar(255)" json:"name" validate:"required,max=255"` - Preview string `gorm:"type:varchar(255)" json:"preview" validate:"required,max=255"` - Content string `gorm:"type:varchar(255)" json:"content" validate:"required,max=255"` - StartTime time.Time `gorm:"type:timestamptz" json:"start_time" validate:"required,ltecsfield=EndTime"` - EndTime time.Time `gorm:"type:timestamptz" json:"end_time" validate:"required,gtecsfield=StartTime"` - Location string `gorm:"type:varchar(255)" json:"location" validate:"required,max=255"` - EventType EventType `gorm:"type:varchar(255);default:open" json:"event_type" validate:"required,max=255,oneof=open membersOnly"` - IsRecurring bool `gorm:"not null;type:bool;default:false" json:"is_recurring" validate:"-"` - - Host *uuid.UUID `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"uuid4"` + Name string `gorm:"type:varchar(255);not null" json:"name" validate:"required,max=255"` + Preview string `gorm:"type:varchar(255);not null" json:"preview" validate:"required,max=255"` + Content string `gorm:"type:varchar(255);not null" json:"content" validate:"required,max=255"` + StartTime time.Time `gorm:"type:timestamptz;not null" json:"start_time" validate:"required,ltecsfield=EndTime"` + EndTime time.Time `gorm:"type:timestamptz;not null" json:"end_time" validate:"required,gtecsfield=StartTime"` + Location string `gorm:"type:varchar(255);not null" json:"location" validate:"required,max=255"` + EventType EventType `gorm:"type:varchar(255);default:open;not null" json:"event_type" validate:"required,max=255,oneof=open membersOnly"` + IsRecurring bool `gorm:"type:bool;default:false;not null" json:"is_recurring" validate:"-"` + + Host *uuid.UUID `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;not null;" json:"-" validate:"uuid4"` RSVP []User `gorm:"many2many:user_event_rsvps;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` Waitlist []User `gorm:"many2many:user_event_waitlists;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` Clubs []Club `gorm:"many2many:club_events;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` @@ -43,28 +43,27 @@ type Event struct { type Series struct { Model - RecurringType RecurringType `gorm:"type:varchar(255);default:open" json:"recurring_type" validate:"max=255"` - MaxOccurrences int `gorm:"type:int" json:"max_occurrences" validate:"min=1"` + RecurringType RecurringType `gorm:"type:varchar(255);default:open;not null" json:"recurring_type" validate:"max=255"` + MaxOccurrences int `gorm:"type:int;not null" json:"max_occurrences" validate:"min=1"` Events []Event `gorm:"many2many:event_series;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"events" validate:"-"` } -// TODO: add not null to required fields on all gorm models type EventSeries struct { - EventID uuid.UUID `gorm:"not null; type:uuid;" json:"event_id" validate:"uuid4"` + EventID uuid.UUID `gorm:"type:uuid;not null" json:"event_id" validate:"uuid4"` Event Event `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` - SeriesID uuid.UUID `gorm:"not null; type:uuid;" json:"series_id" validate:"uuid4"` + SeriesID uuid.UUID `gorm:"type:uuid;not null" json:"series_id" validate:"uuid4"` Series Series `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` } // Not needed for now, we will just update the events separately type EventInstanceException struct { Model - EventID int `gorm:"not null; type:uuid" json:"event_id" validate:"required"` + EventID int `gorm:"type:uuid;not null" json:"event_id" validate:"required"` Event Event - IsRescheduled bool `gorm:"type:bool;default:true" json:"is_rescheduled" validate:"required"` - IsCancelled bool `gorm:"type:bool;default:false" json:"is_cancelled" validate:"required"` - StartTime time.Time `gorm:"type:timestamptz" json:"start_time" validate:"required,datetime,ltecsfield=EndTime"` - EndTime time.Time `gorm:"type:timestamptz" json:"end_time" validate:"required,datetime,gtecsfield=StartTime"` + IsRescheduled bool `gorm:"type:bool;default:true;not null" json:"is_rescheduled" validate:"required"` + IsCancelled bool `gorm:"type:bool;default:false;not null" json:"is_cancelled" validate:"required"` + StartTime time.Time `gorm:"type:timestamptz;not null" json:"start_time" validate:"required,datetime,ltecsfield=EndTime"` + EndTime time.Time `gorm:"type:timestamptz;not null" json:"end_time" validate:"required,datetime,gtecsfield=StartTime"` } // TODO We will likely need to update the create and update structs to account for recurring series @@ -79,7 +78,7 @@ type CreateEventRequestBody struct { IsRecurring *bool `json:"is_recurring" validate:"required"` // TODO club/tag/notification logic - Host *uuid.UUID `json:"host" validate:"omitempty"` + Host *uuid.UUID `json:"host" validate:"required,uuid4"` Clubs []Club `json:"-" validate:"omitempty"` Tag []Tag `json:"-" validate:"omitempty"` Notification []Notification `json:"-" validate:"omitempty"` @@ -100,8 +99,9 @@ type UpdateEventRequestBody struct { StartTime time.Time `json:"start_time" validate:"omitempty,ltecsfield=EndTime"` EndTime time.Time `json:"end_time" validate:"omitempty,gtecsfield=StartTime"` Location string `json:"location" validate:"omitempty,max=255"` - EventType EventType `gorm:"type:varchar(255);default:open" json:"event_type" validate:"omitempty,max=255,oneof=open membersOnly"` + EventType EventType `json:"event_type" validate:"omitempty,max=255,oneof=open membersOnly"` + Host *uuid.UUID `json:"host" validate:"omitempty"` RSVP []User `json:"-" validate:"omitempty"` Waitlist []User `json:"-" validate:"omitempty"` Clubs []Club `json:"-" validate:"omitempty"` diff --git a/backend/src/models/file.go b/backend/src/models/file.go index c64c95fc5..b33a5c68e 100644 --- a/backend/src/models/file.go +++ b/backend/src/models/file.go @@ -18,11 +18,11 @@ type File struct { OwnerID uuid.UUID `gorm:"uniqueIndex:compositeindex;index;not null;type:uuid" json:"-" validate:"required,uuid4"` OwnerType string `gorm:"uniqueIndex:compositeindex;index;not null;type:varchar(255)" json:"-" validate:"required,max=255"` - FileName string `gorm:"type:varchar(255)" json:"file_name" validate:"required,max=255"` - FileType string `gorm:"type:varchar(255)" json:"file_type" validate:"required,max=255"` - FileSize int `gorm:"type:int" json:"file_size" validate:"required,min=1"` - FileURL string `gorm:"type:varchar(255)" json:"file_url" validate:"required,max=255"` - ObjectKey string `gorm:"type:varchar(255)" json:"object_key" validate:"required,max=255"` + FileName string `gorm:"type:varchar(255);not null" json:"file_name" validate:"required,max=255"` + FileType string `gorm:"type:varchar(255);not null" json:"file_type" validate:"required,max=255"` + FileSize int `gorm:"type:int;not null" json:"file_size" validate:"required,min=1"` + FileURL string `gorm:"type:varchar(255);not null" json:"file_url" validate:"required,max=255"` + ObjectKey string `gorm:"type:varchar(255);not null" json:"object_key" validate:"required,max=255"` } type CreateFileRequestBody struct { diff --git a/backend/src/models/follower.go b/backend/src/models/follower.go index 2fe61d680..8002aeb6d 100644 --- a/backend/src/models/follower.go +++ b/backend/src/models/follower.go @@ -9,8 +9,8 @@ func (Follower) TableName() string { } type Follower struct { - UserID uuid.UUID `gorm:"type:uuid;not null;primaryKey" json:"user_id" validate:"required,uuid4"` - ClubID uuid.UUID `gorm:"type:uuid;not null;primaryKey" json:"club_id" validate:"required,uuid4"` + UserID uuid.UUID `gorm:"type:uuid;primaryKey;not null" json:"user_id" validate:"required,uuid4"` + ClubID uuid.UUID `gorm:"type:uuid;primaryKey;not null" json:"club_id" validate:"required,uuid4"` Club *Club `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` User *User `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` diff --git a/backend/src/models/membership.go b/backend/src/models/membership.go index f225acf72..ec28b2304 100644 --- a/backend/src/models/membership.go +++ b/backend/src/models/membership.go @@ -16,11 +16,11 @@ func (Membership) TableName() string { } type Membership struct { - UserID uuid.UUID `gorm:"type:uuid;not null;primaryKey" json:"user_id" validate:"required,uuid4"` - ClubID uuid.UUID `gorm:"type:uuid;not null;primaryKey" json:"club_id" validate:"required,uuid4"` + UserID uuid.UUID `gorm:"type:uuid;primaryKey;not null" json:"user_id" validate:"required,uuid4"` + ClubID uuid.UUID `gorm:"type:uuid;primaryKey;not null" json:"club_id" validate:"required,uuid4"` Club *Club `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` User *User `gorm:"constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` - MembershipType MembershipType `gorm:"type:varchar(255);not null;default:member" json:"membership_type" validate:"required,oneof=member admin"` + MembershipType MembershipType `gorm:"type:varchar(255);default:member;not null" json:"membership_type" validate:"required,oneof=member admin"` } diff --git a/backend/src/models/notification.go b/backend/src/models/notification.go index fd07e4580..87d84a4ec 100644 --- a/backend/src/models/notification.go +++ b/backend/src/models/notification.go @@ -16,12 +16,12 @@ const ( type Notification struct { Model - SendAt time.Time `gorm:"type:timestamptz" json:"send_at" validate:"required"` - Title string `gorm:"type:varchar(255)" json:"title" validate:"required,max=255"` - Content string `gorm:"type:varchar(255)" json:"content" validate:"required,max=255"` - DeepLink string `gorm:"type:varchar(255)" json:"deep_link" validate:"required,max=255"` - Icon string `gorm:"type:varchar(255)" json:"icon" validate:"required,http_url,max=255"` // S3 URL + SendAt time.Time `gorm:"type:timestamptz;not null" json:"send_at" validate:"required"` + Title string `gorm:"type:varchar(255);not null" json:"title" validate:"required,max=255"` + Content string `gorm:"type:varchar(255);not null" json:"content" validate:"required,max=255"` + DeepLink string `gorm:"type:varchar(255);not null" json:"deep_link" validate:"required,max=255"` + Icon string `gorm:"type:varchar(255);not null" json:"icon" validate:"required,s3_url,http_url,max=255"` // S3 URL - ReferenceID uuid.UUID `gorm:"type:int" json:"-" validate:"uuid4"` - ReferenceType NotificationType `gorm:"type:varchar(255)" json:"-" validate:"max=255"` + ReferenceID uuid.UUID `gorm:"type:int;not null" json:"-" validate:"uuid4"` + ReferenceType NotificationType `gorm:"type:varchar(255);not null" json:"-" validate:"max=255"` } diff --git a/backend/src/models/poc.go b/backend/src/models/poc.go index c2d424f1a..bfbf0a9c9 100644 --- a/backend/src/models/poc.go +++ b/backend/src/models/poc.go @@ -7,13 +7,13 @@ import ( type PointOfContact struct { Model - Name string `gorm:"type:varchar(255)" json:"name" validate:"required,max=255"` + Name string `gorm:"type:varchar(255);not null" json:"name" validate:"required,max=255"` Email string `gorm:"uniqueIndex:compositeindex;index;not null;type:varchar(255)" json:"email" validate:"required,email,max=255"` - Position string `gorm:"type:varchar(255);" json:"position" validate:"required,max=255"` + Position string `gorm:"type:varchar(255);not null" json:"position" validate:"required,max=255"` - ClubID uuid.UUID `gorm:"uniqueIndex:compositeindex;index;not null;foreignKey:ClubID" json:"-" validate:"min=1"` + ClubID uuid.UUID `gorm:"uniqueIndex:compositeindex;index;foreignKey:ClubID;not null" json:"-" validate:"required,uuid4"` - PhotoFile File `gorm:"polymorphic:Owner;" json:"photo_file"` + PhotoFile File `gorm:"polymorphic:Owner;not null" json:"photo_file"` } type CreatePointOfContactBody struct { diff --git a/backend/src/models/root.go b/backend/src/models/root.go index cf9a23f5e..b1f29154c 100644 --- a/backend/src/models/root.go +++ b/backend/src/models/root.go @@ -11,7 +11,7 @@ type Tabler interface { } type Model struct { - ID uuid.UUID `gorm:"type:uuid;primaryKey;default:uuid_generate_v4()" json:"id" example:"123e4567-e89b-12d3-a456-426614174000"` - CreatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"created_at" example:"2023-09-20T16:34:50Z"` - UpdatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"updated_at" example:"2023-09-20T16:34:50Z"` + ID uuid.UUID `gorm:"type:uuid;primaryKey;default:uuid_generate_v4();not null" json:"id" example:"123e4567-e89b-12d3-a456-426614174000"` + CreatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP;not null" json:"created_at" example:"2023-09-20T16:34:50Z"` + UpdatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP;not null" json:"updated_at" example:"2023-09-20T16:34:50Z"` } diff --git a/backend/src/models/tag.go b/backend/src/models/tag.go index 560e74d1b..42a2fea7b 100644 --- a/backend/src/models/tag.go +++ b/backend/src/models/tag.go @@ -5,9 +5,9 @@ import "github.com/google/uuid" type Tag struct { Model - Name string `gorm:"type:varchar(255)" json:"name" validate:"required,max=255"` + Name string `gorm:"type:varchar(255);not null" json:"name" validate:"required,max=255"` - CategoryID uuid.UUID `json:"category_id" validate:"required,uuid4"` + CategoryID uuid.UUID `gorm:"type:uuid;not null" json:"category_id" validate:"required,uuid4"` User []User `gorm:"many2many:user_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` Club []Club `gorm:"many2many:club_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` diff --git a/backend/src/models/user.go b/backend/src/models/user.go index a4b5d3632..aedfd98b7 100644 --- a/backend/src/models/user.go +++ b/backend/src/models/user.go @@ -26,6 +26,112 @@ const ( CSSH College = "CSSH" // College of Social Sciences and Humanities ) +type Major string + +// see https://admissions.northeastern.edu/academics/areas-of-study/ +const ( + AfricanaStudies Major = "africanaStudies" + AmericanSignLanguage Major = "americanSignLanguage" + AmericanSignLanguageEnglishInterpreting Major = "americanSignLanguage-EnglishInterpreting" + AppliedPhysics Major = "appliedPhysics" + ArchitecturalStudies Major = "architecturalStudies" + Architecture Major = "architecture" + ArtArtVisualStudies Major = "art:ArtVisualStudies" + BehavioralNeuroscience Major = "behavioralNeuroscience" + Biochemistry Major = "biochemistry" + Bioengineering Major = "bioengineering" + Biology Major = "biology" + BiomedicalPhysics Major = "biomedicalPhysics" + BusinessAdministration Major = "businessAdministration" + BusinessAdministrationAccounting Major = "businessAdministration:Accounting" + BusinessAdministrationAccountingAndAdvisoryServices Major = "businessAdministration:AccountingAndAdvisoryServices" + BusinessAdministrationBrandManagement Major = "businessAdministration:BrandManagement" + BusinessAdministrationBusinessAnalytics Major = "businessAdministration:BusinessAnalytics" + BusinessAdministrationCorporateInnovation Major = "businessAdministration:CorporateInnovation" + BusinessAdministrationEntrepreneurialStartups Major = "businessAdministration:EntrepreneurialStartups" + BusinessAdministrationFamilyBusiness Major = "businessAdministration:FamilyBusiness" + BusinessAdministrationFinance Major = "businessAdministration:Finance" + BusinessAdministrationFintech Major = "businessAdministration:Fintech" + BusinessAdministrationHealthcareManagementAndConsulting Major = "businessAdministration:HealthcareManagementAndConsulting" + BusinessAdministrationManagement Major = "businessAdministration:Management" + BusinessAdministrationManagementInformationSystems Major = "businessAdministration:ManagementInformationSystems" + BusinessAdministrationMarketing Major = "businessAdministration:Marketing" + BusinessAdministrationMarketingAnalytics Major = "businessAdministration:MarketingAnalytics" + BusinessAdministrationSocialInnovationAndEntrepreneurship Major = "businessAdministration:SocialInnovationAndEntrepreneurship" + BusinessAdministrationSupplyChainManagement Major = "businessAdministration:SupplyChainManagement" + CellAndMolecularBiology Major = "cellAndMolecularBiology" + ChemicalEngineering Major = "chemicalEngineering" + Chemistry Major = "chemistry" + CivilEngineering Major = "civilEngineering" + CommunicationStudies Major = "communicationStudies" + ComputerEngineering Major = "computerEngineering" + ComputerScience Major = "computerScience" + ComputingAndLaw Major = "computingAndLaw" + CriminologyAndCriminalJustice Major = "criminologyAndCriminalJustice" + CulturalAnthropology Major = "culturalAnthropology" + Cybersecurity Major = "cybersecurity" + DataScience Major = "dataScience" + Design Major = "design" + Economics Major = "economics" + ElectricalEngineering Major = "electricalEngineering" + English Major = "english" + EnvironmentalAndSustainabilityStudies Major = "environmentalAndSustainabilityStudies" + EnvironmentalEngineering Major = "environmentalEngineering" + EnvironmentalScience Major = "environmentalScience" + EnvironmentalStudies Major = "environmentalStudies" + GameArtAndAnimation Major = "gameArtAndAnimation" + GameDesign Major = "gameDesign" + GlobalAsianStudies Major = "globalAsianStudies" + HealthScience Major = "healthScience" + History Major = "history" + HistoryCultureAndLaw Major = "historyCultureAndLaw" + HumanServices Major = "humanServices" + IndustrialEngineering Major = "industrialEngineering" + InternationalAffairs Major = "internationalAffairs" + InternationalBusiness Major = "internationalBusiness" + InternationalBusinessAccounting Major = "internationalBusiness:Accounting" + InternationalBusinessAccountingAndAdvisoryServices Major = "internationalBusiness:AccountingAndAdvisoryServices" + InternationalBusinessBrandManagement Major = "internationalBusiness:BrandManagement" + InternationalBusinessBusinessAnalytics Major = "internationalBusiness:BusinessAnalytics" + InternationalBusinessCorporateInnovation Major = "internationalBusiness:CorporateInnovation" + InternationalBusinessEntrepreneurialStartups Major = "internationalBusiness:EntrepreneurialStartups" + InternationalBusinessFamilyBusiness Major = "internationalBusiness:FamilyBusiness" + InternationalBusinessFinance Major = "internationalBusiness:Finance" + InternationalBusinessFintech Major = "internationalBusiness:Fintech" + InternationalBusinessHealthcareManagementAndConsulting Major = "internationalBusiness:HealthcareManagementAndConsulting" + InternationalBusinessManagement Major = "internationalBusiness:Management" + InternationalBusinessManagementInformationSystems Major = "internationalBusiness:ManagementInformationSystems" + InternationalBusinessMarketing Major = "internationalBusiness:Marketing" + InternationalBusinessMarketingAnalytics Major = "internationalBusiness:MarketingAnalytics" + InternationalBusinessSocialInnovationAndEntrepreneurship Major = "internationalBusiness:SocialInnovationAndEntrepreneurship" + InternationalBusinessSupplyChainManagement Major = "internationalBusiness:SupplyChainManagement" + Journalism Major = "journalism" + LandscapeArchitecture Major = "landscapeArchitecture" + Linguistics Major = "linguistics" + MarineBiology Major = "marineBiology" + Mathematics Major = "mathematics" + MechanicalEngineering Major = "mechanicalEngineering" + MediaAndScreenStudies Major = "mediaAndScreenStudies" + MediaArts Major = "mediaArts" + Music Major = "music" + MusicTechnology Major = "musicTechnology" + Nursing Major = "nursing" + PharmaceuticalSciences Major = "pharmaceuticalSciences" + PharmacyPharmD Major = "pharmacy(PharmD)" + Philosophy Major = "philosophy" + Physics Major = "physics" + PoliticalScience Major = "politicalScience" + PoliticsPhilosophyEconomics Major = "politicsPhilosophyEconomics" + Psychology Major = "psychology" + PublicHealth Major = "publicHealth" + PublicRelations Major = "publicRelations" + ReligiousStudies Major = "religiousStudies" + Sociology Major = "sociology" + Spanish Major = "spanish" + SpeechLanguagePathologyAndAudiology Major = "speechLanguagePathologyAndAudiology" + Theatre Major = "theatre" +) + type GraduationCycle string const ( @@ -41,15 +147,18 @@ type Tokens struct { type User struct { Model - Role UserRole `gorm:"type:varchar(255);default:'student'" json:"role" validate:"required,oneof=super student"` - FirstName string `gorm:"type:varchar(255)" json:"first_name" validate:"required,max=255"` - LastName string `gorm:"type:varchar(255)" json:"last_name" validate:"required,max=255"` - Email string `gorm:"type:varchar(255);unique" json:"email" validate:"required,email,max=255"` - PasswordHash string `gorm:"type:varchar(97)" json:"-" validate:"required,len=97"` - College College `gorm:"type:varchar(255)" json:"college" validate:"required,max=255"` - GraduationCycle GraduationCycle `gorm:"type:varchar(255)" json:"graduation_cycle" validate:"required,max=255,oneof=december may"` - GraduationYear int16 `gorm:"type:smallint" json:"graduation_year" validate:"required"` - IsVerified bool `gorm:"type:boolean;default:false" json:"is_verified"` + Role UserRole `gorm:"type:varchar(255);default:'student';not null" json:"role" validate:"required,oneof=super student"` + FirstName string `gorm:"type:varchar(255);not null" json:"first_name" validate:"required,max=255"` + LastName string `gorm:"type:varchar(255);not null" json:"last_name" validate:"required,max=255"` + Email string `gorm:"type:varchar(255);unique;not null" json:"email" validate:"required,email,max=255"` + PasswordHash string `gorm:"type:varchar(97);not null" json:"-" validate:"required,len=97"` + Major0 Major `gorm:"type:varchar(255)" json:"major0" validate:"not_equal_if_not_empty=Major1,not_equal_if_not_empty=Major2,required,max=255,oneof=africanaStudies americanSignLanguage americanSignLanguage-EnglishInterpreting appliedPhysics architecturalStudies architecture art:ArtVisualStudies behavioralNeuroscience biochemistry bioengineering biology biomedicalPhysics businessAdministration businessAdministration:Accounting businessAdministration:AccountingAndAdvisoryServices businessAdministration:BrandManagement businessAdministration:BusinessAnalytics businessAdministration:CorporateInnovation businessAdministration:EntrepreneurialStartups businessAdministration:FamilyBusiness businessAdministration:Finance businessAdministration:Fintech businessAdministration:HealthcareManagementAndConsulting businessAdministration:Management businessAdministration:ManagementInformationSystems businessAdministration:Marketing businessAdministration:MarketingAnalytics businessAdministration:SocialInnovationAndEntrepreneurship businessAdministration:SupplyChainManagement cellAndMolecularBiology chemicalEngineering chemistry civilEngineering communicationStudies computerEngineering computerScience computingAndLaw criminologyAndCriminalJustice culturalAnthropology cybersecurity dataScience design economics electricalEngineering english environmentalAndSustainabilityStudies environmentalEngineering environmentalScience environmentalStudies gameArtAndAnimation gameDesign globalAsianStudies healthScience history historyCultureAndLaw humanServices industrialEngineering internationalAffairs internationalBusiness internationalBusiness:Accounting internationalBusiness:AccountingAndAdvisoryServices internationalBusiness:BrandManagement internationalBusiness:BusinessAnalytics internationalBusiness:CorporateInnovation internationalBusiness:EntrepreneurialStartups internationalBusiness:FamilyBusiness internationalBusiness:Finance internationalBusiness:Fintech internationalBusiness:HealthcareManagementAndConsulting internationalBusiness:Management internationalBusiness:ManagementInformationSystems internationalBusiness:Marketing internationalBusiness:MarketingAnalytics internationalBusiness:SocialInnovationAndEntrepreneurship internationalBusiness:SupplyChainManagement journalism landscapeArchitecture linguistics marineBiology mathematics mechanicalEngineering mediaAndScreenStudies mediaArts music musicTechnology nursing pharmaceuticalSciences pharmacy(PharmD) philosophy physics politicalScience politicsPhilosophyEconomics psychology publicHealth publicRelations religiousStudies sociology spanish speechLanguagePathologyAndAudiology theatre"` + Major1 Major `gorm:"type:varchar(255);" json:"major1" validate:"not_equal_if_not_empty=Major0,not_equal_if_not_empty=Major2,omitempty,max=255,oneof=africanaStudies americanSignLanguage americanSignLanguage-EnglishInterpreting appliedPhysics architecturalStudies architecture art:ArtVisualStudies behavioralNeuroscience biochemistry bioengineering biology biomedicalPhysics businessAdministration businessAdministration:Accounting businessAdministration:AccountingAndAdvisoryServices businessAdministration:BrandManagement businessAdministration:BusinessAnalytics businessAdministration:CorporateInnovation businessAdministration:EntrepreneurialStartups businessAdministration:FamilyBusiness businessAdministration:Finance businessAdministration:Fintech businessAdministration:HealthcareManagementAndConsulting businessAdministration:Management businessAdministration:ManagementInformationSystems businessAdministration:Marketing businessAdministration:MarketingAnalytics businessAdministration:SocialInnovationAndEntrepreneurship businessAdministration:SupplyChainManagement cellAndMolecularBiology chemicalEngineering chemistry civilEngineering communicationStudies computerEngineering computerScience computingAndLaw criminologyAndCriminalJustice culturalAnthropology cybersecurity dataScience design economics electricalEngineering english environmentalAndSustainabilityStudies environmentalEngineering environmentalScience environmentalStudies gameArtAndAnimation gameDesign globalAsianStudies healthScience history historyCultureAndLaw humanServices industrialEngineering internationalAffairs internationalBusiness internationalBusiness:Accounting internationalBusiness:AccountingAndAdvisoryServices internationalBusiness:BrandManagement internationalBusiness:BusinessAnalytics internationalBusiness:CorporateInnovation internationalBusiness:EntrepreneurialStartups internationalBusiness:FamilyBusiness internationalBusiness:Finance internationalBusiness:Fintech internationalBusiness:HealthcareManagementAndConsulting internationalBusiness:Management internationalBusiness:ManagementInformationSystems internationalBusiness:Marketing internationalBusiness:MarketingAnalytics internationalBusiness:SocialInnovationAndEntrepreneurship internationalBusiness:SupplyChainManagement journalism landscapeArchitecture linguistics marineBiology mathematics mechanicalEngineering mediaAndScreenStudies mediaArts music musicTechnology nursing pharmaceuticalSciences pharmacy(PharmD) philosophy physics politicalScience politicsPhilosophyEconomics psychology publicHealth publicRelations religiousStudies sociology spanish speechLanguagePathologyAndAudiology theatre"` + Major2 Major `gorm:"type:varchar(255);" json:"major2" validate:"not_equal_if_not_empty=Major0,not_equal_if_not_empty=Major1,omitempty,max=255,oneof=africanaStudies americanSignLanguage americanSignLanguage-EnglishInterpreting appliedPhysics architecturalStudies architecture art:ArtVisualStudies behavioralNeuroscience biochemistry bioengineering biology biomedicalPhysics businessAdministration businessAdministration:Accounting businessAdministration:AccountingAndAdvisoryServices businessAdministration:BrandManagement businessAdministration:BusinessAnalytics businessAdministration:CorporateInnovation businessAdministration:EntrepreneurialStartups businessAdministration:FamilyBusiness businessAdministration:Finance businessAdministration:Fintech businessAdministration:HealthcareManagementAndConsulting businessAdministration:Management businessAdministration:ManagementInformationSystems businessAdministration:Marketing businessAdministration:MarketingAnalytics businessAdministration:SocialInnovationAndEntrepreneurship businessAdministration:SupplyChainManagement cellAndMolecularBiology chemicalEngineering chemistry civilEngineering communicationStudies computerEngineering computerScience computingAndLaw criminologyAndCriminalJustice culturalAnthropology cybersecurity dataScience design economics electricalEngineering english environmentalAndSustainabilityStudies environmentalEngineering environmentalScience environmentalStudies gameArtAndAnimation gameDesign globalAsianStudies healthScience history historyCultureAndLaw humanServices industrialEngineering internationalAffairs internationalBusiness internationalBusiness:Accounting internationalBusiness:AccountingAndAdvisoryServices internationalBusiness:BrandManagement internationalBusiness:BusinessAnalytics internationalBusiness:CorporateInnovation internationalBusiness:EntrepreneurialStartups internationalBusiness:FamilyBusiness internationalBusiness:Finance internationalBusiness:Fintech internationalBusiness:HealthcareManagementAndConsulting internationalBusiness:Management internationalBusiness:ManagementInformationSystems internationalBusiness:Marketing internationalBusiness:MarketingAnalytics internationalBusiness:SocialInnovationAndEntrepreneurship internationalBusiness:SupplyChainManagement journalism landscapeArchitecture linguistics marineBiology mathematics mechanicalEngineering mediaAndScreenStudies mediaArts music musicTechnology nursing pharmaceuticalSciences pharmacy(PharmD) philosophy physics politicalScience politicsPhilosophyEconomics psychology publicHealth publicRelations religiousStudies sociology spanish speechLanguagePathologyAndAudiology theatre"` + College College `gorm:"type:varchar(255);" json:"college" validate:"required,max=255"` // TODO: gorm not null? + GraduationCycle GraduationCycle `gorm:"type:varchar(255);" json:"graduation_cycle" validate:"required,max=255,oneof=december may"` // TODO: gorm not null? + GraduationYear int16 `gorm:"type:smallint;" json:"graduation_year" validate:"required"` // TODO: gorm not null? + IsVerified bool `gorm:"type:boolean;default:false;not null" json:"is_verified"` Tag []Tag `gorm:"many2many:user_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` Admin []Club `gorm:"many2many:user_club_admins;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"` @@ -68,6 +177,9 @@ type CreateUserRequestBody struct { Email string `json:"email" validate:"required,email,neu_email,max=255"` Password string `json:"password" validate:"required,password,min=8,max=255"` // Optional fields + Major0 Major `json:"major0" validate:"not_equal_if_not_empty=Major1,not_equal_if_not_empty=Major2,omitempty,max=255,oneof=africanaStudies americanSignLanguage americanSignLanguage-EnglishInterpreting appliedPhysics architecturalStudies architecture art:ArtVisualStudies behavioralNeuroscience biochemistry bioengineering biology biomedicalPhysics businessAdministration businessAdministration:Accounting businessAdministration:AccountingAndAdvisoryServices businessAdministration:BrandManagement businessAdministration:BusinessAnalytics businessAdministration:CorporateInnovation businessAdministration:EntrepreneurialStartups businessAdministration:FamilyBusiness businessAdministration:Finance businessAdministration:Fintech businessAdministration:HealthcareManagementAndConsulting businessAdministration:Management businessAdministration:ManagementInformationSystems businessAdministration:Marketing businessAdministration:MarketingAnalytics businessAdministration:SocialInnovationAndEntrepreneurship businessAdministration:SupplyChainManagement cellAndMolecularBiology chemicalEngineering chemistry civilEngineering communicationStudies computerEngineering computerScience computingAndLaw criminologyAndCriminalJustice culturalAnthropology cybersecurity dataScience design economics electricalEngineering english environmentalAndSustainabilityStudies environmentalEngineering environmentalScience environmentalStudies gameArtAndAnimation gameDesign globalAsianStudies healthScience history historyCultureAndLaw humanServices industrialEngineering internationalAffairs internationalBusiness internationalBusiness:Accounting internationalBusiness:AccountingAndAdvisoryServices internationalBusiness:BrandManagement internationalBusiness:BusinessAnalytics internationalBusiness:CorporateInnovation internationalBusiness:EntrepreneurialStartups internationalBusiness:FamilyBusiness internationalBusiness:Finance internationalBusiness:Fintech internationalBusiness:HealthcareManagementAndConsulting internationalBusiness:Management internationalBusiness:ManagementInformationSystems internationalBusiness:Marketing internationalBusiness:MarketingAnalytics internationalBusiness:SocialInnovationAndEntrepreneurship internationalBusiness:SupplyChainManagement journalism landscapeArchitecture linguistics marineBiology mathematics mechanicalEngineering mediaAndScreenStudies mediaArts music musicTechnology nursing pharmaceuticalSciences pharmacy(PharmD) philosophy physics politicalScience politicsPhilosophyEconomics psychology publicHealth publicRelations religiousStudies sociology spanish speechLanguagePathologyAndAudiology theatre"` + Major1 Major `json:"major1" validate:"not_equal_if_not_empty=Major0,not_equal_if_not_empty=Major2,omitempty,max=255,oneof=africanaStudies americanSignLanguage americanSignLanguage-EnglishInterpreting appliedPhysics architecturalStudies architecture art:ArtVisualStudies behavioralNeuroscience biochemistry bioengineering biology biomedicalPhysics businessAdministration businessAdministration:Accounting businessAdministration:AccountingAndAdvisoryServices businessAdministration:BrandManagement businessAdministration:BusinessAnalytics businessAdministration:CorporateInnovation businessAdministration:EntrepreneurialStartups businessAdministration:FamilyBusiness businessAdministration:Finance businessAdministration:Fintech businessAdministration:HealthcareManagementAndConsulting businessAdministration:Management businessAdministration:ManagementInformationSystems businessAdministration:Marketing businessAdministration:MarketingAnalytics businessAdministration:SocialInnovationAndEntrepreneurship businessAdministration:SupplyChainManagement cellAndMolecularBiology chemicalEngineering chemistry civilEngineering communicationStudies computerEngineering computerScience computingAndLaw criminologyAndCriminalJustice culturalAnthropology cybersecurity dataScience design economics electricalEngineering english environmentalAndSustainabilityStudies environmentalEngineering environmentalScience environmentalStudies gameArtAndAnimation gameDesign globalAsianStudies healthScience history historyCultureAndLaw humanServices industrialEngineering internationalAffairs internationalBusiness internationalBusiness:Accounting internationalBusiness:AccountingAndAdvisoryServices internationalBusiness:BrandManagement internationalBusiness:BusinessAnalytics internationalBusiness:CorporateInnovation internationalBusiness:EntrepreneurialStartups internationalBusiness:FamilyBusiness internationalBusiness:Finance internationalBusiness:Fintech internationalBusiness:HealthcareManagementAndConsulting internationalBusiness:Management internationalBusiness:ManagementInformationSystems internationalBusiness:Marketing internationalBusiness:MarketingAnalytics internationalBusiness:SocialInnovationAndEntrepreneurship internationalBusiness:SupplyChainManagement journalism landscapeArchitecture linguistics marineBiology mathematics mechanicalEngineering mediaAndScreenStudies mediaArts music musicTechnology nursing pharmaceuticalSciences pharmacy(PharmD) philosophy physics politicalScience politicsPhilosophyEconomics psychology publicHealth publicRelations religiousStudies sociology spanish speechLanguagePathologyAndAudiology theatre"` + Major2 Major `json:"major2" validate:"not_equal_if_not_empty=Major0,not_equal_if_not_empty=Major1,omitempty,max=255,oneof=africanaStudies americanSignLanguage americanSignLanguage-EnglishInterpreting appliedPhysics architecturalStudies architecture art:ArtVisualStudies behavioralNeuroscience biochemistry bioengineering biology biomedicalPhysics businessAdministration businessAdministration:Accounting businessAdministration:AccountingAndAdvisoryServices businessAdministration:BrandManagement businessAdministration:BusinessAnalytics businessAdministration:CorporateInnovation businessAdministration:EntrepreneurialStartups businessAdministration:FamilyBusiness businessAdministration:Finance businessAdministration:Fintech businessAdministration:HealthcareManagementAndConsulting businessAdministration:Management businessAdministration:ManagementInformationSystems businessAdministration:Marketing businessAdministration:MarketingAnalytics businessAdministration:SocialInnovationAndEntrepreneurship businessAdministration:SupplyChainManagement cellAndMolecularBiology chemicalEngineering chemistry civilEngineering communicationStudies computerEngineering computerScience computingAndLaw criminologyAndCriminalJustice culturalAnthropology cybersecurity dataScience design economics electricalEngineering english environmentalAndSustainabilityStudies environmentalEngineering environmentalScience environmentalStudies gameArtAndAnimation gameDesign globalAsianStudies healthScience history historyCultureAndLaw humanServices industrialEngineering internationalAffairs internationalBusiness internationalBusiness:Accounting internationalBusiness:AccountingAndAdvisoryServices internationalBusiness:BrandManagement internationalBusiness:BusinessAnalytics internationalBusiness:CorporateInnovation internationalBusiness:EntrepreneurialStartups internationalBusiness:FamilyBusiness internationalBusiness:Finance internationalBusiness:Fintech internationalBusiness:HealthcareManagementAndConsulting internationalBusiness:Management internationalBusiness:ManagementInformationSystems internationalBusiness:Marketing internationalBusiness:MarketingAnalytics internationalBusiness:SocialInnovationAndEntrepreneurship internationalBusiness:SupplyChainManagement journalism landscapeArchitecture linguistics marineBiology mathematics mechanicalEngineering mediaAndScreenStudies mediaArts music musicTechnology nursing pharmaceuticalSciences pharmacy(PharmD) philosophy physics politicalScience politicsPhilosophyEconomics psychology publicHealth publicRelations religiousStudies sociology spanish speechLanguagePathologyAndAudiology theatre"` College College `json:"college" validate:"omitempty,oneof=CAMD DMSB KCCS CE BCHS SL CPS CS CSSH"` GraduationCycle GraduationCycle `json:"graduation_cycle" validate:"omitempty,max=255,oneof=december may"` GraduationYear int16 `json:"graduation_year" validate:"omitempty"` @@ -76,6 +188,9 @@ type CreateUserRequestBody struct { type UpdateUserRequestBody struct { FirstName string `json:"first_name" validate:"omitempty,max=255"` LastName string `json:"last_name" validate:"omitempty,max=255"` + Major0 Major `json:"major0" validate:"not_equal_if_not_empty=Major1,not_equal_if_not_empty=Major2,omitempty,max=255,oneof=africanaStudies americanSignLanguage americanSignLanguage-EnglishInterpreting appliedPhysics architecturalStudies architecture art:ArtVisualStudies behavioralNeuroscience biochemistry bioengineering biology biomedicalPhysics businessAdministration businessAdministration:Accounting businessAdministration:AccountingAndAdvisoryServices businessAdministration:BrandManagement businessAdministration:BusinessAnalytics businessAdministration:CorporateInnovation businessAdministration:EntrepreneurialStartups businessAdministration:FamilyBusiness businessAdministration:Finance businessAdministration:Fintech businessAdministration:HealthcareManagementAndConsulting businessAdministration:Management businessAdministration:ManagementInformationSystems businessAdministration:Marketing businessAdministration:MarketingAnalytics businessAdministration:SocialInnovationAndEntrepreneurship businessAdministration:SupplyChainManagement cellAndMolecularBiology chemicalEngineering chemistry civilEngineering communicationStudies computerEngineering computerScience computingAndLaw criminologyAndCriminalJustice culturalAnthropology cybersecurity dataScience design economics electricalEngineering english environmentalAndSustainabilityStudies environmentalEngineering environmentalScience environmentalStudies gameArtAndAnimation gameDesign globalAsianStudies healthScience history historyCultureAndLaw humanServices industrialEngineering internationalAffairs internationalBusiness internationalBusiness:Accounting internationalBusiness:AccountingAndAdvisoryServices internationalBusiness:BrandManagement internationalBusiness:BusinessAnalytics internationalBusiness:CorporateInnovation internationalBusiness:EntrepreneurialStartups internationalBusiness:FamilyBusiness internationalBusiness:Finance internationalBusiness:Fintech internationalBusiness:HealthcareManagementAndConsulting internationalBusiness:Management internationalBusiness:ManagementInformationSystems internationalBusiness:Marketing internationalBusiness:MarketingAnalytics internationalBusiness:SocialInnovationAndEntrepreneurship internationalBusiness:SupplyChainManagement journalism landscapeArchitecture linguistics marineBiology mathematics mechanicalEngineering mediaAndScreenStudies mediaArts music musicTechnology nursing pharmaceuticalSciences pharmacy(PharmD) philosophy physics politicalScience politicsPhilosophyEconomics psychology publicHealth publicRelations religiousStudies sociology spanish speechLanguagePathologyAndAudiology theatre"` + Major1 Major `json:"major1" validate:"not_equal_if_not_empty=Major0,not_equal_if_not_empty=Major2,omitempty,max=255,oneof=africanaStudies americanSignLanguage americanSignLanguage-EnglishInterpreting appliedPhysics architecturalStudies architecture art:ArtVisualStudies behavioralNeuroscience biochemistry bioengineering biology biomedicalPhysics businessAdministration businessAdministration:Accounting businessAdministration:AccountingAndAdvisoryServices businessAdministration:BrandManagement businessAdministration:BusinessAnalytics businessAdministration:CorporateInnovation businessAdministration:EntrepreneurialStartups businessAdministration:FamilyBusiness businessAdministration:Finance businessAdministration:Fintech businessAdministration:HealthcareManagementAndConsulting businessAdministration:Management businessAdministration:ManagementInformationSystems businessAdministration:Marketing businessAdministration:MarketingAnalytics businessAdministration:SocialInnovationAndEntrepreneurship businessAdministration:SupplyChainManagement cellAndMolecularBiology chemicalEngineering chemistry civilEngineering communicationStudies computerEngineering computerScience computingAndLaw criminologyAndCriminalJustice culturalAnthropology cybersecurity dataScience design economics electricalEngineering english environmentalAndSustainabilityStudies environmentalEngineering environmentalScience environmentalStudies gameArtAndAnimation gameDesign globalAsianStudies healthScience history historyCultureAndLaw humanServices industrialEngineering internationalAffairs internationalBusiness internationalBusiness:Accounting internationalBusiness:AccountingAndAdvisoryServices internationalBusiness:BrandManagement internationalBusiness:BusinessAnalytics internationalBusiness:CorporateInnovation internationalBusiness:EntrepreneurialStartups internationalBusiness:FamilyBusiness internationalBusiness:Finance internationalBusiness:Fintech internationalBusiness:HealthcareManagementAndConsulting internationalBusiness:Management internationalBusiness:ManagementInformationSystems internationalBusiness:Marketing internationalBusiness:MarketingAnalytics internationalBusiness:SocialInnovationAndEntrepreneurship internationalBusiness:SupplyChainManagement journalism landscapeArchitecture linguistics marineBiology mathematics mechanicalEngineering mediaAndScreenStudies mediaArts music musicTechnology nursing pharmaceuticalSciences pharmacy(PharmD) philosophy physics politicalScience politicsPhilosophyEconomics psychology publicHealth publicRelations religiousStudies sociology spanish speechLanguagePathologyAndAudiology theatre"` + Major2 Major `json:"major2" validate:"not_equal_if_not_empty=Major0,not_equal_if_not_empty=Major1,omitempty,max=255,oneof=africanaStudies americanSignLanguage americanSignLanguage-EnglishInterpreting appliedPhysics architecturalStudies architecture art:ArtVisualStudies behavioralNeuroscience biochemistry bioengineering biology biomedicalPhysics businessAdministration businessAdministration:Accounting businessAdministration:AccountingAndAdvisoryServices businessAdministration:BrandManagement businessAdministration:BusinessAnalytics businessAdministration:CorporateInnovation businessAdministration:EntrepreneurialStartups businessAdministration:FamilyBusiness businessAdministration:Finance businessAdministration:Fintech businessAdministration:HealthcareManagementAndConsulting businessAdministration:Management businessAdministration:ManagementInformationSystems businessAdministration:Marketing businessAdministration:MarketingAnalytics businessAdministration:SocialInnovationAndEntrepreneurship businessAdministration:SupplyChainManagement cellAndMolecularBiology chemicalEngineering chemistry civilEngineering communicationStudies computerEngineering computerScience computingAndLaw criminologyAndCriminalJustice culturalAnthropology cybersecurity dataScience design economics electricalEngineering english environmentalAndSustainabilityStudies environmentalEngineering environmentalScience environmentalStudies gameArtAndAnimation gameDesign globalAsianStudies healthScience history historyCultureAndLaw humanServices industrialEngineering internationalAffairs internationalBusiness internationalBusiness:Accounting internationalBusiness:AccountingAndAdvisoryServices internationalBusiness:BrandManagement internationalBusiness:BusinessAnalytics internationalBusiness:CorporateInnovation internationalBusiness:EntrepreneurialStartups internationalBusiness:FamilyBusiness internationalBusiness:Finance internationalBusiness:Fintech internationalBusiness:HealthcareManagementAndConsulting internationalBusiness:Management internationalBusiness:ManagementInformationSystems internationalBusiness:Marketing internationalBusiness:MarketingAnalytics internationalBusiness:SocialInnovationAndEntrepreneurship internationalBusiness:SupplyChainManagement journalism landscapeArchitecture linguistics marineBiology mathematics mechanicalEngineering mediaAndScreenStudies mediaArts music musicTechnology nursing pharmaceuticalSciences pharmacy(PharmD) philosophy physics politicalScience politicsPhilosophyEconomics psychology publicHealth publicRelations religiousStudies sociology spanish speechLanguagePathologyAndAudiology theatre"` College College `json:"college" validate:"omitempty,oneof=CAMD DMSB KCCS CE BCHS SL CPS CS CSSH"` GraduationCycle GraduationCycle `json:"graduation_cycle" validate:"omitempty,max=255,oneof=december may"` GraduationYear int16 `json:"graduation_year" validate:"omitempty"` @@ -88,7 +203,7 @@ type LoginUserResponseBody struct { type UpdatePasswordRequestBody struct { OldPassword string `json:"old_password" validate:"required,password,min=8,max=255"` - NewPassword string `json:"new_password" validate:"required,password,nefield=OldPassword,min=8,max=255"` + NewPassword string `json:"new_password" validate:"required,password,not_equal_if_not_empty=OldPassword,min=8,max=255"` } type RefreshTokenRequestBody struct { diff --git a/backend/src/models/verification.go b/backend/src/models/verification.go index 434bb727e..db8fd37da 100644 --- a/backend/src/models/verification.go +++ b/backend/src/models/verification.go @@ -14,9 +14,9 @@ const ( ) type Verification struct { - UserID uuid.UUID `gorm:"type:varchar(36);not null;primaryKey" json:"user_id" validate:"required,uuid4"` + UserID uuid.UUID `gorm:"type:varchar(36);primaryKey;not null" json:"user_id" validate:"required,uuid4"` Token string `gorm:"type:varchar(255);unique" json:"token" validate:"required,max=255"` - ExpiresAt time.Time `gorm:"type:timestamp;not null;primaryKey" json:"expires_at" validate:"required"` + ExpiresAt time.Time `gorm:"type:timestamp;primaryKey;not null" json:"expires_at" validate:"required"` Type VerificationType `gorm:"type:varchar(255);not null" json:"type" validate:"required,oneof=email_verification password_reset"` } diff --git a/backend/src/services/event.go b/backend/src/services/event.go index 949eb6ffb..e544714b1 100644 --- a/backend/src/services/event.go +++ b/backend/src/services/event.go @@ -252,6 +252,7 @@ func mapToEvent(eventBody models.UpdateEventRequestBody) *models.Event { Location: eventBody.Location, EventType: eventBody.EventType, + Host: eventBody.Host, RSVP: eventBody.RSVP, Waitlist: eventBody.Waitlist, Clubs: eventBody.Clubs, diff --git a/backend/src/utilities/validator.go b/backend/src/utilities/validator.go index fe332be54..375335dec 100644 --- a/backend/src/utilities/validator.go +++ b/backend/src/utilities/validator.go @@ -36,6 +36,10 @@ func RegisterCustomValidators() (*validator.Validate, error) { return nil, err } + if err := validate.RegisterValidation("not_equal_if_not_empty", validateNotEqualIfNotEmpty); err != nil { + return nil, err + } + return validate, nil } @@ -97,6 +101,13 @@ func validateContactPointer(validate *validator.Validate, fl validator.FieldLeve return validate.Var(contact.Content, rule) == nil && strings.HasPrefix(contact.Content, models.GetContentPrefix(contact.Type)) } +func validateNotEqualIfNotEmpty(fl validator.FieldLevel) bool { + field := fl.Field().String() + otherField := fl.Parent().FieldByName(fl.Param()).String() + + return field == "" || field != otherField +} + func ValidateID(id string) (*uuid.UUID, *errors.Error) { idAsUUID, err := uuid.Parse(id) if err != nil { diff --git a/backend/tests/api/club_test.go b/backend/tests/api/club_test.go index e725eb1b3..e3b74f06b 100644 --- a/backend/tests/api/club_test.go +++ b/backend/tests/api/club_test.go @@ -42,8 +42,6 @@ func AssertClubBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response, body *ma eaa.Assert.NilError(err) - eaa.Assert.Equal(2, len(dbClubs)) - dbClub := dbClubs[0] eaa.Assert.Equal(dbClub.ID, respClub.ID) diff --git a/backend/tests/api/event_test.go b/backend/tests/api/event_test.go index 37c629ed6..643140e5d 100644 --- a/backend/tests/api/event_test.go +++ b/backend/tests/api/event_test.go @@ -16,9 +16,9 @@ import ( "gorm.io/gorm" ) -type EventFactory func() *map[string]interface{} +type EventFactory func(hostID uuid.UUID) *map[string]interface{} -func SampleEventFactory() *map[string]interface{} { +func SampleEventFactory(hostID uuid.UUID) *map[string]interface{} { return &map[string]interface{}{ "name": "Generate", "preview": "Generate is Northeastern's premier student-led product development studio.", @@ -28,11 +28,13 @@ func SampleEventFactory() *map[string]interface{} { "location": "Carter Fields", "event_type": "open", "is_recurring": false, + "host": hostID, } } -func SampleSeriesFactory() *map[string]interface{} { +func SampleSeriesFactory(hostID uuid.UUID) *map[string]interface{} { return CustomSampleSeriesFactory( + hostID, models.CreateSeriesRequestBody{ RecurringType: "daily", MaxOccurrences: 10, @@ -40,7 +42,7 @@ func SampleSeriesFactory() *map[string]interface{} { ) } -func CustomSampleSeriesFactory(series models.CreateSeriesRequestBody) *map[string]interface{} { +func CustomSampleSeriesFactory(hostID uuid.UUID, series models.CreateSeriesRequestBody) *map[string]interface{} { return &map[string]interface{}{ "name": "Software Development", "preview": "CS4500 at northeastern", @@ -50,6 +52,7 @@ func CustomSampleSeriesFactory(series models.CreateSeriesRequestBody) *map[strin "location": "ISEC", "event_type": "membersOnly", "is_recurring": true, + "host": hostID, "series": series, } } @@ -138,25 +141,25 @@ func AssertEventBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response, body *m return dbEvent.ID } -func AssertSampleEventBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response) []uuid.UUID { - sampleEvent := SampleEventFactory() +func AssertSampleEventBodyRespDB(eaa h.ExistingAppAssert, hostID uuid.UUID, resp *http.Response) []uuid.UUID { + sampleEvent := SampleEventFactory(hostID) return AssertEventListBodyRespDB(eaa, resp, sampleEvent) } -func CreateSampleEvent(existingAppAssert h.ExistingAppAssert, factoryFunction EventFactory) (h.ExistingAppAssert, []uuid.UUID) { +func CreateSampleEvent(existingAppAssert h.ExistingAppAssert, hostID uuid.UUID, factoryFunction EventFactory) (h.ExistingAppAssert, []uuid.UUID) { var sampleEventUUIDs []uuid.UUID newAppAssert := existingAppAssert.TestOnStatusAndTester( h.TestRequest{ Method: fiber.MethodPost, Path: "/api/v1/events/", - Body: factoryFunction(), + Body: factoryFunction(hostID), Role: &models.Super, }, h.TesterWithStatus{ Status: fiber.StatusCreated, Tester: func(eaa h.ExistingAppAssert, resp *http.Response) { - sampleEventUUIDs = AssertSampleEventBodyRespDB(eaa, resp) + sampleEventUUIDs = AssertSampleEventBodyRespDB(eaa, hostID, resp) }, }, ) @@ -185,18 +188,20 @@ func AssertNumSeriesRemainsAtN(eaa h.ExistingAppAssert, resp *http.Response, n i } func TestCreateEventWorks(t *testing.T) { - existingAppAssert, _ := CreateSampleEvent(h.InitTest(t), SampleEventFactory) - existingAppAssert.Close() + eaa, _, clubID := CreateSampleClub(h.InitTest(t)) + eaa, _ = CreateSampleEvent(eaa, clubID, SampleEventFactory) + eaa.Close() } func TestCreateEventSeriesWorks(t *testing.T) { - existingAppAssert, _ := CreateSampleEvent(h.InitTest(t), SampleSeriesFactory) - existingAppAssert.Close() + eaa, _, clubID := CreateSampleClub(h.InitTest(t)) + eaa, _ = CreateSampleEvent(eaa, clubID, SampleSeriesFactory) + eaa.Close() } func TestGetEventWorks(t *testing.T) { - existingAppAssert, eventUUID := CreateSampleEvent(h.InitTest(t), SampleEventFactory) - + existingAppAssert, _, clubID := CreateSampleClub(h.InitTest(t)) + existingAppAssert, eventUUID := CreateSampleEvent(existingAppAssert, clubID, SampleEventFactory) existingAppAssert.TestOnStatusAndTester(h.TestRequest{ Method: fiber.MethodGet, Path: fmt.Sprintf("/api/v1/events/%s", eventUUID[0]), @@ -205,14 +210,15 @@ func TestGetEventWorks(t *testing.T) { h.TesterWithStatus{ Status: fiber.StatusOK, Tester: func(eaa h.ExistingAppAssert, resp *http.Response) { - AssertEventListBodyRespDB(eaa, resp, SampleEventFactory()) + AssertEventListBodyRespDB(eaa, resp, SampleEventFactory(clubID)) }, }, ).Close() } func TestGetEventsWorks(t *testing.T) { - existingAppAssert, _ := CreateSampleEvent(h.InitTest(t), SampleEventFactory) + existingAppAssert, _, clubID := CreateSampleClub(h.InitTest(t)) + existingAppAssert, _ = CreateSampleEvent(existingAppAssert, clubID, SampleEventFactory) existingAppAssert.TestOnStatusAndTester(h.TestRequest{ Method: fiber.MethodGet, @@ -235,7 +241,8 @@ func TestGetEventsWorks(t *testing.T) { } func TestGetSeriesByEventIDWorks(t *testing.T) { - existingAppAssert, eventUUIDs := CreateSampleEvent(h.InitTest(t), SampleSeriesFactory) + existingAppAssert, _, clubID := CreateSampleClub(h.InitTest(t)) + existingAppAssert, eventUUIDs := CreateSampleEvent(existingAppAssert, clubID, SampleSeriesFactory) existingAppAssert.TestOnStatusAndTester(h.TestRequest{ Method: fiber.MethodGet, @@ -254,9 +261,10 @@ func TestGetSeriesByEventIDWorks(t *testing.T) { func AssertCreateBadEventDataFails(t *testing.T, jsonKey string, badValues []interface{}, expectedErr errors.Error) { appAssert, _, _ := CreateSampleStudent(t, nil) + appAssert, _, clubID := CreateSampleClub(appAssert) for _, badValue := range badValues { - sampleEventPermutation := *SampleEventFactory() + sampleEventPermutation := *SampleEventFactory(clubID) sampleEventPermutation[jsonKey] = badValue appAssert.TestOnErrorAndTester( @@ -320,8 +328,9 @@ func TestCreateEventFailsOnInvalidEventType(t *testing.T) { func AssertCreateBadEventSeriesDataFails(t *testing.T, badSeries models.CreateSeriesRequestBody, expectedErr errors.Error) { appAssert, _, _ := CreateSampleStudent(t, nil) + appAssert, _, clubID := CreateSampleClub(appAssert) - sampleSeriesPermutation := CustomSampleSeriesFactory(badSeries) + sampleSeriesPermutation := CustomSampleSeriesFactory(clubID, badSeries) appAssert.TestOnErrorAndTester( h.TestRequest{ @@ -361,9 +370,10 @@ func TestCreateSeriesFailsOnInvalidMaxOccurrences(t *testing.T) { } func TestUpdateEventWorks(t *testing.T) { - appAssert, eventUUID := CreateSampleEvent(h.InitTest(t), SampleEventFactory) + appAssert, _, clubID := CreateSampleClub(h.InitTest(t)) + appAssert, eventUUID := CreateSampleEvent(appAssert, clubID, SampleEventFactory) - updatedEvent := SampleEventFactory() + updatedEvent := SampleEventFactory(clubID) (*updatedEvent)["name"] = "Updated Name" (*updatedEvent)["preview"] = "Updated Preview" @@ -384,7 +394,8 @@ func TestUpdateEventWorks(t *testing.T) { } func TestUpdateEventSeriesWorks(t *testing.T) { - appAssert, eventUUIDs := CreateSampleEvent(h.InitTest(t), SampleSeriesFactory) + appAssert, _, clubID := CreateSampleClub(h.InitTest(t)) + appAssert, eventUUIDs := CreateSampleEvent(appAssert, clubID, SampleSeriesFactory) updatedSeries := &map[string]interface{}{ "recurring_type": "daily", @@ -398,6 +409,7 @@ func TestUpdateEventSeriesWorks(t *testing.T) { "location": "Richards 224", "event_type": "open", "is_recurring": true, + "host": clubID, }, } @@ -418,9 +430,10 @@ func TestUpdateEventSeriesWorks(t *testing.T) { } func TestUpdateEventFailsOnInvalidBody(t *testing.T) { - appAssert, eventUUID := CreateSampleEvent(h.InitTest(t), SampleEventFactory) + appAssert, _, clubID := CreateSampleClub(h.InitTest(t)) + appAssert, eventUUID := CreateSampleEvent(appAssert, clubID, SampleEventFactory) - body := SampleEventFactory() + body := SampleEventFactory(clubID) for _, invalidData := range []map[string]interface{}{ {"start_time": "Not a datetime"}, @@ -466,7 +479,7 @@ func TestUpdateEventFailsOnInvalidBody(t *testing.T) { } func TestUpdateEventFailsBadRequest(t *testing.T) { - appAssert := h.InitTest(t) + appAssert, _, clubID := CreateSampleClub(h.InitTest(t)) badRequests := []string{ "0", @@ -481,7 +494,7 @@ func TestUpdateEventFailsBadRequest(t *testing.T) { h.TestRequest{ Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/events/%s", badRequest), - Body: SampleEventFactory(), + Body: SampleEventFactory(clubID), Role: &models.Super, }, errors.FailedToValidateID, @@ -492,12 +505,14 @@ func TestUpdateEventFailsBadRequest(t *testing.T) { } func TestUpdateEventFailsOnEventIdNotExist(t *testing.T) { + eaa, _, clubID := CreateSampleClub(h.InitTest(t)) + uuid := uuid.New() - h.InitTest(t).TestOnErrorAndTester(h.TestRequest{ + eaa.TestOnErrorAndTester(h.TestRequest{ Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/events/%s", uuid), - Body: SampleEventFactory(), + Body: SampleEventFactory(clubID), Role: &models.Super, TestUserIDReplaces: h.StringToPointer("user_id"), }, @@ -515,7 +530,7 @@ func TestUpdateEventFailsOnEventIdNotExist(t *testing.T) { } func TestUpdateSeriesFailsBadRequest(t *testing.T) { - appAssert := h.InitTest(t) + appAssert, _, clubID := CreateSampleClub(h.InitTest(t)) badRequests := []string{ "0", @@ -530,7 +545,7 @@ func TestUpdateSeriesFailsBadRequest(t *testing.T) { h.TestRequest{ Method: fiber.MethodPatch, Path: fmt.Sprintf("/api/v1/events/%s/series", badRequest), - Body: SampleEventFactory(), + Body: SampleEventFactory(clubID), Role: &models.Super, }, errors.FailedToValidateID, @@ -541,7 +556,8 @@ func TestUpdateSeriesFailsBadRequest(t *testing.T) { } func AssertDeleteWorks(t *testing.T, factoryFunction EventFactory, requestPath string, tester h.Tester) { - appAssert, eventUUIDs := CreateSampleEvent(h.InitTest(t), factoryFunction) + appAssert, _, clubID := CreateSampleClub(h.InitTest(t)) + appAssert, eventUUIDs := CreateSampleEvent(appAssert, clubID, factoryFunction) appAssert.TestOnStatusAndTester( h.TestRequest{ @@ -570,7 +586,8 @@ func TestDeleteSeriesByEventIDWorks(t *testing.T) { } func AssertDeleteNotExistFails(t *testing.T, factoryFunction EventFactory, requestPath string, tester h.Tester, badUUID uuid.UUID) { - appAssert, _ := CreateSampleEvent(h.InitTest(t), factoryFunction) + appAssert, _, clubID := CreateSampleClub(h.InitTest(t)) + appAssert, _ = CreateSampleEvent(appAssert, clubID, factoryFunction) appAssert.TestOnErrorAndTester( h.TestRequest{ diff --git a/backend/tests/api/helpers/auth.go b/backend/tests/api/helpers/auth.go index ab21ac2d0..c36778cb9 100644 --- a/backend/tests/api/helpers/auth.go +++ b/backend/tests/api/helpers/auth.go @@ -151,6 +151,8 @@ func SampleStudentFactory() (models.User, string) { LastName: "Doe", Email: "doe.jane@northeastern.edu", PasswordHash: *hashedPassword, + Major0: models.ComputerScience, + Major1: models.Economics, College: models.KCCS, GraduationCycle: models.May, GraduationYear: 2025, @@ -166,6 +168,9 @@ func SampleStudentJSONFactory(sampleStudent models.User, rawPassword string) *ma "last_name": sampleStudent.LastName, "email": sampleStudent.Email, "password": rawPassword, + "major0": string(sampleStudent.Major0), + "major1": string(sampleStudent.Major1), + "major2": string(sampleStudent.Major2), "college": string(sampleStudent.College), "graduation_cycle": string(sampleStudent.GraduationCycle), "graduation_year": int(sampleStudent.GraduationYear), diff --git a/backend/tests/api/user_test.go b/backend/tests/api/user_test.go index c9df6c605..f6af28f17 100644 --- a/backend/tests/api/user_test.go +++ b/backend/tests/api/user_test.go @@ -42,7 +42,10 @@ func TestGetUsersWorksForSuper(t *testing.T) { eaa.Assert.Equal("SAC", respUser.FirstName) eaa.Assert.Equal("Super", respUser.LastName) eaa.Assert.Equal("generatesac@gmail.com", respUser.Email) - eaa.Assert.Equal(models.College("KCCS"), respUser.College) + eaa.Assert.Equal(models.ComputerScience, respUser.Major0) + eaa.Assert.Equal(models.Major(""), respUser.Major1) + eaa.Assert.Equal(models.Major(""), respUser.Major2) + eaa.Assert.Equal(models.KCCS, respUser.College) eaa.Assert.Equal(models.May, respUser.GraduationCycle) eaa.Assert.Equal(int16(2025), respUser.GraduationYear) @@ -95,6 +98,9 @@ func TestGetUserWorks(t *testing.T) { eaa.Assert.Equal(sampleUser["first_name"].(string), respUser.FirstName) eaa.Assert.Equal(sampleUser["last_name"].(string), respUser.LastName) eaa.Assert.Equal(sampleUser["email"].(string), respUser.Email) + eaa.Assert.Equal(models.Major(sampleUser["major0"].(string)), respUser.Major0) + eaa.Assert.Equal(models.Major(sampleUser["major1"].(string)), respUser.Major1) + eaa.Assert.Equal(models.Major(sampleUser["major2"].(string)), respUser.Major2) eaa.Assert.Equal(models.College(sampleUser["college"].(string)), respUser.College) eaa.Assert.Equal(models.GraduationCycle(sampleUser["graduation_cycle"].(string)), respUser.GraduationCycle) eaa.Assert.Equal(int16(sampleUser["graduation_year"].(int)), respUser.GraduationYear) @@ -187,6 +193,9 @@ func TestUpdateUserWorks(t *testing.T) { eaa.Assert.Equal(newFirstName, respUser.FirstName) eaa.Assert.Equal(newLastName, respUser.LastName) eaa.Assert.Equal((sampleStudentJSON)["email"].(string), respUser.Email) + eaa.Assert.Equal(models.Major((sampleStudentJSON)["major0"].(string)), respUser.Major0) + eaa.Assert.Equal(models.Major((sampleStudentJSON)["major1"].(string)), respUser.Major1) + eaa.Assert.Equal(models.Major((sampleStudentJSON)["major2"].(string)), respUser.Major2) eaa.Assert.Equal(models.College((sampleStudentJSON)["college"].(string)), respUser.College) eaa.Assert.Equal(models.GraduationCycle(sampleStudentJSON["graduation_cycle"].(string)), respUser.GraduationCycle) eaa.Assert.Equal(int16(sampleStudentJSON["graduation_year"].(int)), respUser.GraduationYear) @@ -199,6 +208,9 @@ func TestUpdateUserWorks(t *testing.T) { eaa.Assert.Equal(dbUser.FirstName, respUser.FirstName) eaa.Assert.Equal(dbUser.LastName, respUser.LastName) eaa.Assert.Equal(dbUser.Email, respUser.Email) + eaa.Assert.Equal(dbUser.Major0, respUser.Major0) + eaa.Assert.Equal(dbUser.Major1, respUser.Major1) + eaa.Assert.Equal(dbUser.Major2, respUser.Major2) eaa.Assert.Equal(dbUser.College, respUser.College) eaa.Assert.Equal(dbUser.GraduationCycle, respUser.GraduationCycle) eaa.Assert.Equal(dbUser.GraduationYear, respUser.GraduationYear) @@ -367,6 +379,9 @@ func AssertUserWithIDBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response, bo eaa.Assert.Equal(dbUser.FirstName, respUser.FirstName) eaa.Assert.Equal(dbUser.LastName, respUser.LastName) eaa.Assert.Equal(dbUser.Email, respUser.Email) + eaa.Assert.Equal(dbUser.Major0, respUser.Major0) + eaa.Assert.Equal(dbUser.Major1, respUser.Major1) + eaa.Assert.Equal(dbUser.Major2, respUser.Major2) eaa.Assert.Equal(dbUser.College, respUser.College) eaa.Assert.Equal(dbUser.GraduationCycle, respUser.GraduationCycle) eaa.Assert.Equal(dbUser.GraduationYear, respUser.GraduationYear) @@ -380,6 +395,9 @@ func AssertUserWithIDBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response, bo eaa.Assert.Equal((*body)["first_name"].(string), dbUser.FirstName) eaa.Assert.Equal((*body)["last_name"].(string), dbUser.LastName) eaa.Assert.Equal((*body)["email"].(string), dbUser.Email) + eaa.Assert.Equal(models.Major((*body)["major0"].(string)), dbUser.Major0) + eaa.Assert.Equal(models.Major((*body)["major1"].(string)), dbUser.Major1) + eaa.Assert.Equal(models.Major((*body)["major2"].(string)), dbUser.Major2) eaa.Assert.Equal(models.College((*body)["college"].(string)), dbUser.College) eaa.Assert.Equal(models.GraduationCycle((*body)["graduation_cycle"].(string)), dbUser.GraduationCycle) eaa.Assert.Equal(int16((*body)["graduation_year"].(int)), dbUser.GraduationYear)