From d786b9fc8eef3a575e33cc5fd0fe34bf91f093d2 Mon Sep 17 00:00:00 2001
From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
Date: Wed, 31 Jan 2024 23:47:10 -0500
Subject: [PATCH 01/33] Added Support for Slack and Email Contact Types (#129)
---
backend/src/models/contact.go | 23 +++++++++++++----------
backend/src/utilities/validator.go | 21 ++++++++++++++++++++-
2 files changed, 33 insertions(+), 11 deletions(-)
diff --git a/backend/src/models/contact.go b/backend/src/models/contact.go
index 527ad678d..8c86ada90 100644
--- a/backend/src/models/contact.go
+++ b/backend/src/models/contact.go
@@ -2,23 +2,26 @@ package models
import "github.com/google/uuid"
-type Media string
+type ContactType string
const (
- Facebook Media = "facebook"
- Instagram Media = "instagram"
- Twitter Media = "twitter"
- LinkedIn Media = "linkedin"
- YouTube Media = "youtube"
- GitHub Media = "github"
- Custom Media = "custom"
+ Facebook ContactType = "facebook"
+ Instagram ContactType = "instagram"
+ Twitter ContactType = "twitter"
+ LinkedIn ContactType = "linkedin"
+ YouTube ContactType = "youtube"
+ GitHub ContactType = "github"
+ Slack ContactType = "slack"
+ Discord ContactType = "discord"
+ Email ContactType = "email"
+ CustomSite ContactType = "customSite"
)
type Contact struct {
Model
- Type Media `gorm:"type:varchar(255)" json:"type" validate:"required,max=255"`
- Content string `gorm:"type:varchar(255)" json:"content" validate:"required,http_url,max=255"` // media URL
+ Type ContactType `gorm:"type:varchar(255)" json:"type" validate:"required,max=255"`
+ Content string `gorm:"type:varchar(255)" json:"content" validate:"required,contact_pointer,max=255"`
ClubID uuid.UUID `gorm:"foreignKey:ClubID" json:"-" validate:"uuid4"`
}
diff --git a/backend/src/utilities/validator.go b/backend/src/utilities/validator.go
index e4c6bafeb..7d11f38b3 100644
--- a/backend/src/utilities/validator.go
+++ b/backend/src/utilities/validator.go
@@ -5,6 +5,8 @@ import (
"strconv"
"github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+
"github.com/google/uuid"
"github.com/go-playground/validator/v10"
@@ -16,6 +18,9 @@ func RegisterCustomValidators(validate *validator.Validate) {
validate.RegisterValidation("password", validatePassword)
validate.RegisterValidation("mongo_url", validateMongoURL)
validate.RegisterValidation("s3_url", validateS3URL)
+ validate.RegisterValidation("contact_pointer", func(fl validator.FieldLevel) bool {
+ return validateContactPointer(validate, fl)
+ })
}
func validateEmail(fl validator.FieldLevel) bool {
@@ -48,6 +53,21 @@ func validateS3URL(fl validator.FieldLevel) bool {
return true
}
+func validateContactPointer(validate *validator.Validate, fl validator.FieldLevel) bool {
+ contact, ok := fl.Parent().Interface().(models.Contact)
+
+ if !ok {
+ return false
+ }
+
+ switch contact.Type {
+ case models.Email:
+ return validate.Var(contact.Content, "email") == nil
+ default:
+ return validate.Var(contact.Content, "http_url") == nil
+ }
+}
+
func ValidateID(id string) (*uuid.UUID, *errors.Error) {
idAsUUID, err := uuid.Parse(id)
@@ -58,7 +78,6 @@ func ValidateID(id string) (*uuid.UUID, *errors.Error) {
return &idAsUUID, nil
}
-
func ValidateNonNegative(value string) (*int, *errors.Error) {
valueAsInt, err := strconv.Atoi(value)
From c1b7f016fb24e982a23f72d09e4d472f661174bb Mon Sep 17 00:00:00 2001
From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
Date: Thu, 1 Feb 2024 01:38:24 -0500
Subject: [PATCH 02/33] Fixed Format Step in GitHub Workflow (#131)
---
.github/workflows/auto_assign_author.yml | 4 +++
.github/workflows/auto_request_review.yml | 3 +++
.github/workflows/{go.yml => backend.yml} | 32 +++++++++++++++++++----
.github/workflows/codeql.yml | 4 +++
README.md | 6 ++---
backend/src/auth/password.go | 5 ----
backend/src/config/config.go | 2 --
backend/src/controllers/category.go | 2 --
backend/src/controllers/tag.go | 1 -
backend/src/controllers/user.go | 4 +--
backend/src/database/db.go | 5 ----
backend/src/errors/user.go | 4 +--
backend/src/models/user.go | 2 +-
backend/src/services/category.go | 3 ---
backend/src/services/club.go | 2 --
backend/src/services/user.go | 3 ---
backend/src/transactions/user.go | 2 +-
backend/src/utilities/manipulator.go | 1 -
backend/src/utilities/validator.go | 1 -
backend/tests/api/category_test.go | 6 ++---
backend/tests/api/club_test.go | 2 --
backend/tests/api/helpers.go | 9 +------
backend/tests/api/tag_test.go | 14 +++++-----
backend/tests/api/user_test.go | 7 ++---
cli/commands/be.go | 2 --
cli/commands/clean_tests.go | 9 +------
cli/commands/config.go | 12 +++++----
cli/commands/drop.go | 4 ---
cli/commands/format.go | 6 ++---
cli/commands/insert.go | 3 ---
cli/commands/reset.go | 6 ++---
cli/commands/test.go | 1 -
cli/utils/path.go | 3 +--
33 files changed, 76 insertions(+), 94 deletions(-)
rename .github/workflows/{go.yml => backend.yml} (72%)
diff --git a/.github/workflows/auto_assign_author.yml b/.github/workflows/auto_assign_author.yml
index 5ac4a7936..a92a9c3f4 100644
--- a/.github/workflows/auto_assign_author.yml
+++ b/.github/workflows/auto_assign_author.yml
@@ -4,6 +4,10 @@ on:
pull_request:
types: [opened, ready_for_review, reopened]
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
jobs:
auto-add-assignee:
runs-on: ubuntu-latest
diff --git a/.github/workflows/auto_request_review.yml b/.github/workflows/auto_request_review.yml
index 78f14dcf8..e38f94b42 100644
--- a/.github/workflows/auto_request_review.yml
+++ b/.github/workflows/auto_request_review.yml
@@ -4,6 +4,9 @@ on:
pull_request:
types: [opened, ready_for_review, reopened]
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
jobs:
auto-request-review:
runs-on: ubuntu-latest
diff --git a/.github/workflows/go.yml b/.github/workflows/backend.yml
similarity index 72%
rename from .github/workflows/go.yml
rename to .github/workflows/backend.yml
index e0632ddb0..4c4d773bc 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/backend.yml
@@ -1,4 +1,4 @@
-name: Go
+name: Backend
on:
push:
@@ -6,11 +6,11 @@ on:
- main
paths:
- "backend/**"
- - ".github/workflows/go.yml"
+ - ".github/workflows/backend.yml"
pull_request:
paths:
- "backend/**"
- - ".github/workflows/go.yml"
+ - ".github/workflows/backend.yml"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
@@ -27,8 +27,23 @@ jobs:
uses: actions/setup-go@v3
with:
go-version: "1.21"
- - name: Enforce formatting
- run: gofmt -l ./backend/ | grep ".go$" | xargs -r echo "Files not formatted:"
+ - name: Cache Go Modules
+ uses: actions/cache@v3
+ with:
+ path: ~/go/pkg/mod
+ key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+ restore-keys: |
+ ${{ runner.os }}-go-
+ - name: Install gofumpt
+ run: go install mvdan.cc/gofumpt@latest
+ - name: Check code formatting
+ run: |
+ unformatted_files=$(gofumpt -l ./backend/)
+ if [ -n "$unformatted_files" ]; then
+ echo "Files not formatted:"
+ echo "$unformatted_files"
+ exit 1
+ fi
lint:
name: Lint
@@ -68,6 +83,13 @@ jobs:
uses: actions/setup-go@v3
with:
go-version: "1.21"
+ - name: Cache Go Modules
+ uses: actions/cache@v3
+ with:
+ path: ~/go/pkg/mod
+ key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+ restore-keys: |
+ ${{ runner.os }}-go-
- name: Install Dependencies
run: cd ./backend/ && go get ./...
- name: Increase max_connections in PostgreSQL
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index e4809cde4..c8a51aadf 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -18,6 +18,10 @@ on:
schedule:
- cron: "0 0 * * 1"
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
jobs:
analyze-js-ts:
name: Analyze JavaScript and TypeScript
diff --git a/README.md b/README.md
index f9fdebd02..282b5e824 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,9 @@
diff --git a/backend/src/auth/password.go b/backend/src/auth/password.go
index 109980d02..d27831176 100644
--- a/backend/src/auth/password.go
+++ b/backend/src/auth/password.go
@@ -58,7 +58,6 @@ var (
func ComparePasswordAndHash(password, encodedHash string) (bool, error) {
p, salt, hash, err := decodeHash(encodedHash)
-
if err != nil {
return false, err
}
@@ -82,7 +81,6 @@ func decodeHash(encodedHash string) (p *params, salt []byte, hash []byte, err er
var version int
_, err = fmt.Sscanf(vals[2], "v=%d", &version)
-
if err != nil {
return nil, nil, nil, err
}
@@ -94,13 +92,11 @@ func decodeHash(encodedHash string) (p *params, salt []byte, hash []byte, err er
p = ¶ms{}
_, err = fmt.Sscanf(vals[3], "m=%d,t=%d,p=%d", &p.memory, &p.iterations, &p.parallelism)
-
if err != nil {
return nil, nil, nil, err
}
salt, err = base64.RawStdEncoding.Strict().DecodeString(vals[4])
-
if err != nil {
return nil, nil, nil, err
}
@@ -108,7 +104,6 @@ func decodeHash(encodedHash string) (p *params, salt []byte, hash []byte, err er
p.saltLength = uint32(len(salt))
hash, err = base64.RawStdEncoding.Strict().DecodeString(vals[5])
-
if err != nil {
return nil, nil, nil, err
}
diff --git a/backend/src/config/config.go b/backend/src/config/config.go
index 9b3f85c67..252bce1f7 100644
--- a/backend/src/config/config.go
+++ b/backend/src/config/config.go
@@ -71,7 +71,6 @@ const (
)
func GetConfiguration(path string) (Settings, error) {
-
var environment Environment
if env := os.Getenv("APP_ENVIRONMENT"); env != "" {
environment = Environment(env)
@@ -117,7 +116,6 @@ func GetConfiguration(path string) (Settings, error) {
portStr := os.Getenv(fmt.Sprintf("%sPORT", appPrefix))
portInt, err := strconv.ParseUint(portStr, 10, 16)
-
if err != nil {
return Settings{}, fmt.Errorf("failed to parse port: %w", err)
}
diff --git a/backend/src/controllers/category.go b/backend/src/controllers/category.go
index ba7f24dd7..317e312bc 100644
--- a/backend/src/controllers/category.go
+++ b/backend/src/controllers/category.go
@@ -39,7 +39,6 @@ func (t *CategoryController) CreateCategory(c *fiber.Ctx) error {
}
newCategory, err := t.categoryService.CreateCategory(categoryBody)
-
if err != nil {
return err.FiberError(c)
}
@@ -130,7 +129,6 @@ func (t *CategoryController) UpdateCategory(c *fiber.Ctx) error {
}
updatedCategory, err := t.categoryService.UpdateCategory(c.Params("id"), category)
-
if err != nil {
return err.FiberError(c)
}
diff --git a/backend/src/controllers/tag.go b/backend/src/controllers/tag.go
index 9074c731d..78ca83d77 100644
--- a/backend/src/controllers/tag.go
+++ b/backend/src/controllers/tag.go
@@ -59,7 +59,6 @@ func (t *TagController) CreateTag(c *fiber.Ctx) error {
// @Router /api/v1/tags/{id} [get]
func (t *TagController) GetTag(c *fiber.Ctx) error {
tag, err := t.tagService.GetTag(c.Params("categoryID"), c.Params("tagID"))
-
if err != nil {
return err.FiberError(c)
}
diff --git a/backend/src/controllers/user.go b/backend/src/controllers/user.go
index 86b9c6b23..5b4bcfe94 100644
--- a/backend/src/controllers/user.go
+++ b/backend/src/controllers/user.go
@@ -146,7 +146,7 @@ func (u *UserController) GetUserTags(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(&tags)
}
-func (u *UserController) CreateUserTags(c *fiber.Ctx) error {
+func (u *UserController) CreateUserTags(c *fiber.Ctx) error {
var requestBody models.CreateUserTagsBody
if err := c.BodyParser(&requestBody); err != nil {
return errors.FailedToParseRequestBody.FiberError(c)
@@ -156,6 +156,6 @@ func (u *UserController) CreateUserTags(c *fiber.Ctx) error {
if err != nil {
return err.FiberError(c)
}
-
+
return c.Status(fiber.StatusCreated).JSON(&tags)
}
diff --git a/backend/src/database/db.go b/backend/src/database/db.go
index d7c131a71..9464ef412 100644
--- a/backend/src/database/db.go
+++ b/backend/src/database/db.go
@@ -16,13 +16,11 @@ func ConfigureDB(settings config.Settings) (*gorm.DB, error) {
SkipDefaultTransaction: true,
TranslateError: true,
})
-
if err != nil {
return nil, err
}
err = db.Exec("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"").Error
-
if err != nil {
return nil, err
}
@@ -36,7 +34,6 @@ func ConfigureDB(settings config.Settings) (*gorm.DB, error) {
func ConnPooling(db *gorm.DB) error {
sqlDB, err := db.DB()
-
if err != nil {
return err
}
@@ -58,7 +55,6 @@ func MigrateDB(settings config.Settings, db *gorm.DB) error {
&models.Tag{},
&models.User{},
)
-
if err != nil {
return err
}
@@ -82,7 +78,6 @@ func createSuperUser(settings config.Settings, db *gorm.DB) error {
}
passwordHash, err := auth.ComputePasswordHash(settings.SuperUser.Password)
-
if err != nil {
return err
}
diff --git a/backend/src/errors/user.go b/backend/src/errors/user.go
index dd80dbb98..de07afbb7 100644
--- a/backend/src/errors/user.go
+++ b/backend/src/errors/user.go
@@ -7,9 +7,9 @@ var (
StatusCode: fiber.StatusBadRequest,
Message: "failed to validate user",
}
- FailedToValidateUserTags = Error {
+ FailedToValidateUserTags = Error{
StatusCode: fiber.StatusBadRequest,
- Message: "failed to validate user tags",
+ Message: "failed to validate user tags",
}
FailedToCreateUser = Error{
StatusCode: fiber.StatusInternalServerError,
diff --git a/backend/src/models/user.go b/backend/src/models/user.go
index ee762ab5f..b5b945d63 100644
--- a/backend/src/models/user.go
+++ b/backend/src/models/user.go
@@ -78,5 +78,5 @@ type UpdateUserRequestBody struct {
}
type CreateUserTagsBody struct {
- Tags []uuid.UUID `json:"tags" validate:"required"`
+ Tags []uuid.UUID `json:"tags" validate:"required"`
}
diff --git a/backend/src/services/category.go b/backend/src/services/category.go
index 91aabe3db..75eaf9ab2 100644
--- a/backend/src/services/category.go
+++ b/backend/src/services/category.go
@@ -44,13 +44,11 @@ func (c *CategoryService) CreateCategory(categoryBody models.CategoryRequestBody
func (c *CategoryService) GetCategories(limit string, page string) ([]models.Category, *errors.Error) {
limitAsInt, err := utilities.ValidateNonNegative(limit)
-
if err != nil {
return nil, &errors.FailedToValidateLimit
}
pageAsInt, err := utilities.ValidateNonNegative(page)
-
if err != nil {
return nil, &errors.FailedToValidatePage
}
@@ -62,7 +60,6 @@ func (c *CategoryService) GetCategories(limit string, page string) ([]models.Cat
func (c *CategoryService) GetCategory(id string) (*models.Category, *errors.Error) {
idAsUUID, err := utilities.ValidateID(id)
-
if err != nil {
return nil, err
}
diff --git a/backend/src/services/club.go b/backend/src/services/club.go
index fa93877e3..f7f7ff7d8 100644
--- a/backend/src/services/club.go
+++ b/backend/src/services/club.go
@@ -25,13 +25,11 @@ type ClubService struct {
func (c *ClubService) GetClubs(limit string, page string) ([]models.Club, *errors.Error) {
limitAsInt, err := utilities.ValidateNonNegative(limit)
-
if err != nil {
return nil, &errors.FailedToValidateLimit
}
pageAsInt, err := utilities.ValidateNonNegative(page)
-
if err != nil {
return nil, &errors.FailedToValidatePage
}
diff --git a/backend/src/services/user.go b/backend/src/services/user.go
index 66a41e0b0..b11cc5821 100644
--- a/backend/src/services/user.go
+++ b/backend/src/services/user.go
@@ -51,13 +51,11 @@ func (u *UserService) CreateUser(userBody models.CreateUserRequestBody) (*models
func (u *UserService) GetUsers(limit string, page string) ([]models.User, *errors.Error) {
limitAsInt, err := utilities.ValidateNonNegative(limit)
-
if err != nil {
return nil, &errors.FailedToValidateLimit
}
pageAsInt, err := utilities.ValidateNonNegative(page)
-
if err != nil {
return nil, &errors.FailedToValidatePage
}
@@ -132,7 +130,6 @@ func (u *UserService) CreateUserTags(id string, tagIDs models.CreateUserTagsBody
// Retrieve a list of valid tags from the ids:
tags, err := transactions.GetTagsByIDs(u.DB, tagIDs.Tags)
-
if err != nil {
return nil, err
}
diff --git a/backend/src/transactions/user.go b/backend/src/transactions/user.go
index 3f4540b24..a55adf1b7 100644
--- a/backend/src/transactions/user.go
+++ b/backend/src/transactions/user.go
@@ -84,7 +84,7 @@ func GetUserTags(db *gorm.DB, id uuid.UUID) ([]models.Tag, *errors.Error) {
return nil, &errors.UserNotFound
}
- if err := db.Model(&user).Association("Tag").Find(&tags) ; err != nil {
+ if err := db.Model(&user).Association("Tag").Find(&tags); err != nil {
return nil, &errors.FailedToGetTag
}
return tags, nil
diff --git a/backend/src/utilities/manipulator.go b/backend/src/utilities/manipulator.go
index 64486b93e..ff0a1d791 100644
--- a/backend/src/utilities/manipulator.go
+++ b/backend/src/utilities/manipulator.go
@@ -4,7 +4,6 @@ import (
"github.com/mitchellh/mapstructure"
)
-
// MapRequestToModel maps request data to a target model using mapstructure
func MapRequestToModel[T any, U any](responseData T, targetModel *U) (*U, error) {
config := &mapstructure.DecoderConfig{
diff --git a/backend/src/utilities/validator.go b/backend/src/utilities/validator.go
index 7d11f38b3..357051499 100644
--- a/backend/src/utilities/validator.go
+++ b/backend/src/utilities/validator.go
@@ -70,7 +70,6 @@ func validateContactPointer(validate *validator.Validate, fl validator.FieldLeve
func ValidateID(id string) (*uuid.UUID, *errors.Error) {
idAsUUID, err := uuid.Parse(id)
-
if err != nil {
return nil, &errors.FailedToValidateID
}
diff --git a/backend/tests/api/category_test.go b/backend/tests/api/category_test.go
index f96a2abe7..61fbe6018 100644
--- a/backend/tests/api/category_test.go
+++ b/backend/tests/api/category_test.go
@@ -165,7 +165,7 @@ func TestCreateCategoryFailsIfNameIsMissing(t *testing.T) {
func TestCreateCategoryFailsIfCategoryWithThatNameAlreadyExists(t *testing.T) {
existingAppAssert, _ := CreateSampleCategory(t, nil)
- var TestNumCategoriesRemainsAt1 = func(app TestApp, assert *assert.A, resp *http.Response) {
+ TestNumCategoriesRemainsAt1 := func(app TestApp, assert *assert.A, resp *http.Response) {
AssertNumCategoriesRemainsAtN(app, assert, resp, 1)
}
@@ -295,7 +295,7 @@ func TestUpdateCategoryWorks(t *testing.T) {
"name": "Arts & Crafts",
}
- var AssertUpdatedCategoryBodyRespDB = func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertUpdatedCategoryBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
AssertUpdatedCategoryBodyRespDB(app, assert, resp, &category)
}
@@ -317,7 +317,7 @@ func TestUpdateCategoryWorksWithSameDetails(t *testing.T) {
category := *SampleCategoryFactory()
category["id"] = uuid
- var AssertSampleCategoryBodyRespDB = func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertSampleCategoryBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
AssertUpdatedCategoryBodyRespDB(app, assert, resp, &category)
}
diff --git a/backend/tests/api/club_test.go b/backend/tests/api/club_test.go
index fb78e4fd2..b24f1ac7d 100644
--- a/backend/tests/api/club_test.go
+++ b/backend/tests/api/club_test.go
@@ -283,7 +283,6 @@ func TestCreateClubFailsOnInvalidRecruitmentType(t *testing.T) {
"https://google.com",
},
)
-
}
func TestCreateClubFailsOnInvalidApplicationLink(t *testing.T) {
@@ -294,7 +293,6 @@ func TestCreateClubFailsOnInvalidApplicationLink(t *testing.T) {
"@#139081#$Ad_Axf",
},
)
-
}
func TestCreateClubFailsOnInvalidLogo(t *testing.T) {
diff --git a/backend/tests/api/helpers.go b/backend/tests/api/helpers.go
index 09ee4ff87..dc728ac53 100644
--- a/backend/tests/api/helpers.go
+++ b/backend/tests/api/helpers.go
@@ -44,13 +44,11 @@ type TestApp struct {
func spawnApp() (TestApp, error) {
listener, err := net.Listen("tcp", "127.0.0.1:0")
-
if err != nil {
return TestApp{}, err
}
configuration, err := config.GetConfiguration(filepath.Join("..", "..", "..", "config"))
-
if err != nil {
return TestApp{}, err
}
@@ -60,7 +58,6 @@ func spawnApp() (TestApp, error) {
configuration.Database.DatabaseName = generateRandomDBName()
connectionWithDB, err := configureDatabase(configuration)
-
if err != nil {
return TestApp{}, err
}
@@ -73,6 +70,7 @@ func spawnApp() (TestApp, error) {
InitialDBName: initialDBName,
}, nil
}
+
func generateRandomInt(max int64) int64 {
randInt, _ := crand.Int(crand.Reader, big.NewInt(max))
return randInt.Int64()
@@ -104,18 +102,15 @@ func configureDatabase(config config.Settings) (*gorm.DB, error) {
dsnWithDB := config.Database.WithDb()
dbWithDB, err := gorm.Open(gormPostgres.Open(dsnWithDB), &gorm.Config{SkipDefaultTransaction: true, TranslateError: true})
-
if err != nil {
return nil, err
}
err = dbWithDB.Exec("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"").Error
-
if err != nil {
return nil, err
}
err = database.MigrateDB(config, dbWithDB)
-
if err != nil {
return nil, err
}
@@ -130,13 +125,11 @@ type ExistingAppAssert struct {
func (eaa ExistingAppAssert) Close() {
db, err := eaa.App.Conn.DB()
-
if err != nil {
panic(err)
}
err = db.Close()
-
if err != nil {
panic(err)
}
diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go
index 6354e34cd..17f4f8b1b 100644
--- a/backend/tests/api/tag_test.go
+++ b/backend/tests/api/tag_test.go
@@ -43,15 +43,17 @@ func AssertTagWithBodyRespDB(app TestApp, assert *assert.A, resp *http.Response,
}
func AssertSampleTagBodyRespDB(t *testing.T, app TestApp, assert *assert.A, resp *http.Response) uuid.UUID {
- appAssert, _ := CreateSampleCategory(t, &ExistingAppAssert{App: app,
- Assert: assert})
+ appAssert, _ := CreateSampleCategory(t, &ExistingAppAssert{
+ App: app,
+ Assert: assert,
+ })
return AssertTagWithBodyRespDB(appAssert.App, appAssert.Assert, resp, SampleTagFactory())
}
func CreateSampleTag(t *testing.T) (appAssert ExistingAppAssert, categoryUUID uuid.UUID, tagUUID uuid.UUID) {
appAssert, categoryUUID = CreateSampleCategory(t, nil)
- var AssertSampleTagBodyRespDB = func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertSampleTagBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
tagUUID = AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory())
}
@@ -236,7 +238,7 @@ func TestUpdateTagWorksUpdateName(t *testing.T) {
generateNUTag := *SampleTagFactory()
generateNUTag["name"] = "GenerateNU"
- var AssertUpdatedTagBodyRespDB = func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertUpdatedTagBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
tagUUID = AssertTagWithBodyRespDB(app, assert, resp, &generateNUTag)
}
@@ -258,7 +260,7 @@ func TestUpdateTagWorksUpdateCategory(t *testing.T) {
technologyCategory := *SampleCategoryFactory()
technologyCategory["name"] = "Technology"
- var AssertNewCategoryBodyRespDB = func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertNewCategoryBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
AssertCategoryWithBodyRespDBMostRecent(app, assert, resp, &technologyCategory)
}
@@ -275,7 +277,7 @@ func TestUpdateTagWorksUpdateCategory(t *testing.T) {
technologyTag := *SampleTagFactory()
- var AssertUpdatedTagBodyRespDB = func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertUpdatedTagBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
AssertTagWithBodyRespDB(app, assert, resp, &technologyTag)
}
diff --git a/backend/tests/api/user_test.go b/backend/tests/api/user_test.go
index f55fbd526..acd94abec 100644
--- a/backend/tests/api/user_test.go
+++ b/backend/tests/api/user_test.go
@@ -2,11 +2,11 @@ package tests
import (
"fmt"
-
- stdliberrors "errors"
"net/http"
"testing"
+ stdliberrors "errors"
+
"github.com/GenerateNU/sac/backend/src/auth"
"github.com/GenerateNU/sac/backend/src/errors"
"github.com/GenerateNU/sac/backend/src/models"
@@ -125,7 +125,6 @@ func TestGetUserFailsNotExist(t *testing.T) {
err := app.Conn.Where("id = ?", uuid).First(&user).Error
assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
-
},
},
).Close()
@@ -300,7 +299,6 @@ func TestDeleteUserBadRequest(t *testing.T) {
func SampleUserFactory() *map[string]interface{} {
return &map[string]interface{}{
-
"first_name": "Jane",
"last_name": "Doe",
"email": "doe.jane@northeastern.edu",
@@ -517,7 +515,6 @@ func TestCreateUserFailsOnInvalidCollege(t *testing.T) {
if permutation != khouryAbbreviation {
permutationsWithoutKhoury = append(permutationsWithoutKhoury, permutation)
}
-
}
AssertCreateBadDataFails(t,
diff --git a/cli/commands/be.go b/cli/commands/be.go
index 17ad9ef6c..88a4c01b7 100644
--- a/cli/commands/be.go
+++ b/cli/commands/be.go
@@ -19,7 +19,6 @@ func RunBackendCommand() *cli.Command {
}
err := RunBE()
-
if err != nil {
return cli.Exit(err.Error(), 1)
}
@@ -41,7 +40,6 @@ func RunBE() error {
fmt.Println("Running backend")
err := goCmd.Run()
-
if err != nil {
return fmt.Errorf("error running backend: %w", err)
}
diff --git a/cli/commands/clean_tests.go b/cli/commands/clean_tests.go
index cab99963c..3f3f77bd6 100644
--- a/cli/commands/clean_tests.go
+++ b/cli/commands/clean_tests.go
@@ -2,9 +2,8 @@ package commands
import (
"database/sql"
- "os/user"
-
"fmt"
+ "os/user"
"sync"
_ "github.com/lib/pq"
@@ -21,7 +20,6 @@ func ClearDBCommand() *cli.Command {
}
err := CleanTestDBs()
-
if err != nil {
return cli.Exit(err.Error(), 1)
}
@@ -37,7 +35,6 @@ func CleanTestDBs() error {
fmt.Println("Cleaning test databases")
db, err := sql.Open("postgres", CONFIG.Database.WithDb())
-
if err != nil {
return err
}
@@ -45,13 +42,11 @@ func CleanTestDBs() error {
defer db.Close()
currentUser, err := user.Current()
-
if err != nil {
return fmt.Errorf("failed to get current user: %w", err)
}
rows, err := db.Query("SELECT datname FROM pg_database WHERE datistemplate = false AND datname != 'postgres' AND datname != $1 AND datname != $2", currentUser.Username, CONFIG.Database.DatabaseName)
-
if err != nil {
return err
}
@@ -70,7 +65,6 @@ func CleanTestDBs() error {
wg.Add(1)
go func(dbName string) {
-
defer wg.Done()
fmt.Printf("Dropping database %s\n", dbName)
@@ -78,7 +72,6 @@ func CleanTestDBs() error {
if _, err := db.Exec(fmt.Sprintf("DROP DATABASE %s", dbName)); err != nil {
fmt.Printf("Failed to drop database %s: %v\n", dbName, err)
}
-
}(dbName)
}
diff --git a/cli/commands/config.go b/cli/commands/config.go
index a06375782..203219218 100644
--- a/cli/commands/config.go
+++ b/cli/commands/config.go
@@ -7,8 +7,10 @@ import (
"github.com/GenerateNU/sac/cli/utils"
)
-var ROOT_DIR, _ = utils.GetRootDir()
-var FRONTEND_DIR = filepath.Join(ROOT_DIR, "/frontend")
-var BACKEND_DIR = filepath.Join(ROOT_DIR, "/backend/src")
-var CONFIG, _ = config.GetConfiguration(filepath.Join(ROOT_DIR, "/config"))
-var MIGRATION_FILE = filepath.Join(BACKEND_DIR, "/migrations/data.sql")
+var (
+ ROOT_DIR, _ = utils.GetRootDir()
+ FRONTEND_DIR = filepath.Join(ROOT_DIR, "/frontend")
+ BACKEND_DIR = filepath.Join(ROOT_DIR, "/backend/src")
+ CONFIG, _ = config.GetConfiguration(filepath.Join(ROOT_DIR, "/config"))
+ MIGRATION_FILE = filepath.Join(BACKEND_DIR, "/migrations/data.sql")
+)
diff --git a/cli/commands/drop.go b/cli/commands/drop.go
index abcd5882d..2bcb5d132 100644
--- a/cli/commands/drop.go
+++ b/cli/commands/drop.go
@@ -19,7 +19,6 @@ func DropDBCommand() *cli.Command {
}
err := DropDB()
-
if err != nil {
return cli.Exit(err.Error(), 1)
}
@@ -35,7 +34,6 @@ func DropDB() error {
fmt.Println("Dropping database")
db, err := sql.Open("postgres", CONFIG.Database.WithDb())
-
if err != nil {
return err
}
@@ -45,7 +43,6 @@ func DropDB() error {
var tableCount int
err = db.QueryRow("SELECT COUNT(*) FROM pg_tables WHERE schemaname = 'public'").Scan(&tableCount)
-
if err != nil {
return fmt.Errorf("error checking tables: %w", err)
}
@@ -58,7 +55,6 @@ func DropDB() error {
fmt.Println("Generating DROP TABLE statements...")
rows, err := db.Query("SELECT tablename FROM pg_tables WHERE schemaname = 'public'")
-
if err != nil {
return fmt.Errorf("error generating DROP TABLE statements: %w", err)
}
diff --git a/cli/commands/format.go b/cli/commands/format.go
index 694e3fe57..60f6e35f5 100644
--- a/cli/commands/format.go
+++ b/cli/commands/format.go
@@ -76,7 +76,7 @@ func Format(folder string, runFrontend bool, runBackend bool) error {
func BackendFormat() error {
fmt.Println("Formatting backend")
- cmd := exec.Command("go", "fmt", "./...")
+ cmd := exec.Command("gofumpt", "-l", "-w", ".")
cmd.Dir = BACKEND_DIR
err := cmd.Run()
@@ -90,5 +90,5 @@ func BackendFormat() error {
func FrontendFormat(folder string) error {
fmt.Println("UNIMPLEMENTED")
- return nil
-}
\ No newline at end of file
+ return nil
+}
diff --git a/cli/commands/insert.go b/cli/commands/insert.go
index 41e07dc19..10387b346 100644
--- a/cli/commands/insert.go
+++ b/cli/commands/insert.go
@@ -20,7 +20,6 @@ func InsertDBCommand() *cli.Command {
}
err := InsertDB()
-
if err != nil {
return cli.Exit(err.Error(), 1)
}
@@ -34,7 +33,6 @@ func InsertDBCommand() *cli.Command {
func InsertDB() error {
db, err := sql.Open("postgres", CONFIG.Database.WithDb())
-
if err != nil {
return err
}
@@ -44,7 +42,6 @@ func InsertDB() error {
var exists bool
err = db.QueryRow("SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' LIMIT 1);").Scan(&exists)
-
if err != nil {
return err
}
diff --git a/cli/commands/reset.go b/cli/commands/reset.go
index 44e16fd30..74ac689fb 100644
--- a/cli/commands/reset.go
+++ b/cli/commands/reset.go
@@ -30,7 +30,7 @@ func ResetDB() error {
DropDB()
cmd := exec.Command("sleep", "1")
- cmd.Dir = BACKEND_DIR
+ cmd.Dir = BACKEND_DIR
err := cmd.Run()
if err != nil {
@@ -40,6 +40,6 @@ func ResetDB() error {
Migrate()
fmt.Println("Done resetting database")
-
+
return nil
-}
\ No newline at end of file
+}
diff --git a/cli/commands/test.go b/cli/commands/test.go
index 3c912ecb6..3172b5366 100644
--- a/cli/commands/test.go
+++ b/cli/commands/test.go
@@ -80,7 +80,6 @@ func BackendTest() error {
defer CleanTestDBs()
out, err := cmd.CombinedOutput()
-
if err != nil {
fmt.Println(string(out))
return cli.Exit("Failed to run backend tests", 1)
diff --git a/cli/utils/path.go b/cli/utils/path.go
index d91a6b504..01b9a6a1a 100644
--- a/cli/utils/path.go
+++ b/cli/utils/path.go
@@ -6,7 +6,6 @@ import (
"path/filepath"
)
-
func GetRootDir() (string, error) {
// Get the current working directory
currentDir, err := os.Getwd()
@@ -41,4 +40,4 @@ func FindRootDir(dir string) (string, error) {
// Recursively search in the parent directory
return FindRootDir(parentDir)
-}
\ No newline at end of file
+}
From 08d253fcdfbf94ac5f7632d641f2cf9d20472894 Mon Sep 17 00:00:00 2001
From: garrettladley
Date: Thu, 1 Feb 2024 13:52:15 -0500
Subject: [PATCH 03/33] resolves #133
---
.github/workflows/backend_codeql.yml | 45 +++++++++++++
.github/workflows/cli_codeql.yml | 45 +++++++++++++
.github/workflows/codeql.yml | 98 ----------------------------
.github/workflows/mobile_codeql.yml | 42 ++++++++++++
.github/workflows/web_codeql.yml | 42 ++++++++++++
5 files changed, 174 insertions(+), 98 deletions(-)
create mode 100644 .github/workflows/backend_codeql.yml
create mode 100644 .github/workflows/cli_codeql.yml
delete mode 100644 .github/workflows/codeql.yml
create mode 100644 .github/workflows/mobile_codeql.yml
create mode 100644 .github/workflows/web_codeql.yml
diff --git a/.github/workflows/backend_codeql.yml b/.github/workflows/backend_codeql.yml
new file mode 100644
index 000000000..60bba78cc
--- /dev/null
+++ b/.github/workflows/backend_codeql.yml
@@ -0,0 +1,45 @@
+name: Backend CodeQL
+
+on:
+ push:
+ branches: ["main"]
+ paths:
+ - "backend/**"
+ - ".github/workflows/backend_codeql.yml"
+ pull_request:
+ branches: ["main"]
+ paths:
+ - "backend/**"
+ - ".github/workflows/backend_codeql.yml"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ strategy:
+ fail-fast: false
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Set up Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: "1.21"
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: "go"
+ queries: security-and-quality
+ - name: Build
+ run: |
+ cd ./backend/ && go build -o backend src/main.go
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:go"
diff --git a/.github/workflows/cli_codeql.yml b/.github/workflows/cli_codeql.yml
new file mode 100644
index 000000000..caacf0951
--- /dev/null
+++ b/.github/workflows/cli_codeql.yml
@@ -0,0 +1,45 @@
+name: CLI CodeQL
+
+on:
+ push:
+ branches: ["main"]
+ paths:
+ - "cli/**"
+ - ".github/workflows/cli_codeql.yml"
+ pull_request:
+ branches: ["main"]
+ paths:
+ - "cli/**"
+ - ".github/workflows/cli_codeql.yml"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ strategy:
+ fail-fast: false
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Set up Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: "1.21"
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: "go"
+ queries: security-and-quality
+ - name: Build
+ run: |
+ cd ./cli/ && go build -o cli main.go
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:go"
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
deleted file mode 100644
index c8a51aadf..000000000
--- a/.github/workflows/codeql.yml
+++ /dev/null
@@ -1,98 +0,0 @@
-name: CodeQL
-
-on:
- push:
- branches: ["main"]
- paths:
- - "backend/**"
- - "cli/**"
- - "frontend/**"
- - ".github/workflows/codeql.yml"
- pull_request:
- branches: ["main"]
- paths:
- - "backend/**"
- - "cli/**"
- - "frontend/**"
- - ".github/workflows/codeql.yml"
- schedule:
- - cron: "0 0 * * 1"
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- analyze-js-ts:
- name: Analyze JavaScript and TypeScript
- runs-on: ubuntu-latest
- permissions:
- security-events: write
- strategy:
- fail-fast: false
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: "javascript-typescript"
- queries: security-and-quality
- - name: Autobuild
- uses: github/codeql-action/autobuild@v3
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
- with:
- category: "/language:javascript-typescript"
- analyze-backend-go:
- name: Analyze Backend Go
- runs-on: ubuntu-latest
- permissions:
- security-events: write
- strategy:
- fail-fast: false
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- - name: Set up Go
- uses: actions/setup-go@v3
- with:
- go-version: "1.21"
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: "go"
- queries: security-and-quality
- - name: Build
- run: |
- cd ./backend/ && go build -o backend src/main.go
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
- with:
- category: "/language:go"
- analyze-cli-go:
- name: Analyze CLI Go
- runs-on: ubuntu-latest
- permissions:
- security-events: write
- strategy:
- fail-fast: false
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- - name: Set up Go
- uses: actions/setup-go@v3
- with:
- go-version: "1.21"
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: "go"
- queries: security-and-quality
- - name: Build
- run: |
- cd ./cli/ && go build -o cli main.go
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
- with:
- category: "/language:go"
diff --git a/.github/workflows/mobile_codeql.yml b/.github/workflows/mobile_codeql.yml
new file mode 100644
index 000000000..c0a12f95f
--- /dev/null
+++ b/.github/workflows/mobile_codeql.yml
@@ -0,0 +1,42 @@
+name: Mobile CodeQL
+
+on:
+ push:
+ branches: ["main"]
+ paths:
+ - "frontend/sac-mobile/**"
+ - ".github/workflows/mobile_codeql.yml"
+ pull_request:
+ branches: ["main"]
+ paths:
+ - "frontend/sac-mobile/**"
+ - ".github/workflows/mobile_codeql.yml"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ strategy:
+ fail-fast: false
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: "javascript-typescript"
+ queries: security-and-quality
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v3
+ with:
+ working-directory: frontend/sac-mobile
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:javascript-typescript"
diff --git a/.github/workflows/web_codeql.yml b/.github/workflows/web_codeql.yml
new file mode 100644
index 000000000..b1eee3590
--- /dev/null
+++ b/.github/workflows/web_codeql.yml
@@ -0,0 +1,42 @@
+name: Web CodeQL
+
+on:
+ push:
+ branches: ["main"]
+ paths:
+ - "frontend/sac-web/**"
+ - ".github/workflows/mweb_codeql.yml"
+ pull_request:
+ branches: ["main"]
+ paths:
+ - "frontend/sac-web/**"
+ - ".github/workflows/web_codeql.yml"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ strategy:
+ fail-fast: false
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: "javascript-typescript"
+ queries: security-and-quality
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v3
+ with:
+ working-directory: frontend/sac-web
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:javascript-typescript"
From fa30a3261a2785493687cdf4cf456b77fc07414c Mon Sep 17 00:00:00 2001
From: garrettladley
Date: Thu, 1 Feb 2024 13:55:30 -0500
Subject: [PATCH 04/33] Revert "resolves #133"
This reverts commit 08d253fcdfbf94ac5f7632d641f2cf9d20472894.
---
.github/workflows/backend_codeql.yml | 45 -------------
.github/workflows/cli_codeql.yml | 45 -------------
.github/workflows/codeql.yml | 98 ++++++++++++++++++++++++++++
.github/workflows/mobile_codeql.yml | 42 ------------
.github/workflows/web_codeql.yml | 42 ------------
5 files changed, 98 insertions(+), 174 deletions(-)
delete mode 100644 .github/workflows/backend_codeql.yml
delete mode 100644 .github/workflows/cli_codeql.yml
create mode 100644 .github/workflows/codeql.yml
delete mode 100644 .github/workflows/mobile_codeql.yml
delete mode 100644 .github/workflows/web_codeql.yml
diff --git a/.github/workflows/backend_codeql.yml b/.github/workflows/backend_codeql.yml
deleted file mode 100644
index 60bba78cc..000000000
--- a/.github/workflows/backend_codeql.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-name: Backend CodeQL
-
-on:
- push:
- branches: ["main"]
- paths:
- - "backend/**"
- - ".github/workflows/backend_codeql.yml"
- pull_request:
- branches: ["main"]
- paths:
- - "backend/**"
- - ".github/workflows/backend_codeql.yml"
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
- permissions:
- security-events: write
- strategy:
- fail-fast: false
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- - name: Set up Go
- uses: actions/setup-go@v3
- with:
- go-version: "1.21"
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: "go"
- queries: security-and-quality
- - name: Build
- run: |
- cd ./backend/ && go build -o backend src/main.go
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
- with:
- category: "/language:go"
diff --git a/.github/workflows/cli_codeql.yml b/.github/workflows/cli_codeql.yml
deleted file mode 100644
index caacf0951..000000000
--- a/.github/workflows/cli_codeql.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-name: CLI CodeQL
-
-on:
- push:
- branches: ["main"]
- paths:
- - "cli/**"
- - ".github/workflows/cli_codeql.yml"
- pull_request:
- branches: ["main"]
- paths:
- - "cli/**"
- - ".github/workflows/cli_codeql.yml"
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
- permissions:
- security-events: write
- strategy:
- fail-fast: false
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- - name: Set up Go
- uses: actions/setup-go@v3
- with:
- go-version: "1.21"
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: "go"
- queries: security-and-quality
- - name: Build
- run: |
- cd ./cli/ && go build -o cli main.go
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
- with:
- category: "/language:go"
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 000000000..c8a51aadf
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,98 @@
+name: CodeQL
+
+on:
+ push:
+ branches: ["main"]
+ paths:
+ - "backend/**"
+ - "cli/**"
+ - "frontend/**"
+ - ".github/workflows/codeql.yml"
+ pull_request:
+ branches: ["main"]
+ paths:
+ - "backend/**"
+ - "cli/**"
+ - "frontend/**"
+ - ".github/workflows/codeql.yml"
+ schedule:
+ - cron: "0 0 * * 1"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ analyze-js-ts:
+ name: Analyze JavaScript and TypeScript
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ strategy:
+ fail-fast: false
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: "javascript-typescript"
+ queries: security-and-quality
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v3
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:javascript-typescript"
+ analyze-backend-go:
+ name: Analyze Backend Go
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ strategy:
+ fail-fast: false
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Set up Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: "1.21"
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: "go"
+ queries: security-and-quality
+ - name: Build
+ run: |
+ cd ./backend/ && go build -o backend src/main.go
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:go"
+ analyze-cli-go:
+ name: Analyze CLI Go
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ strategy:
+ fail-fast: false
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Set up Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: "1.21"
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: "go"
+ queries: security-and-quality
+ - name: Build
+ run: |
+ cd ./cli/ && go build -o cli main.go
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:go"
diff --git a/.github/workflows/mobile_codeql.yml b/.github/workflows/mobile_codeql.yml
deleted file mode 100644
index c0a12f95f..000000000
--- a/.github/workflows/mobile_codeql.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-name: Mobile CodeQL
-
-on:
- push:
- branches: ["main"]
- paths:
- - "frontend/sac-mobile/**"
- - ".github/workflows/mobile_codeql.yml"
- pull_request:
- branches: ["main"]
- paths:
- - "frontend/sac-mobile/**"
- - ".github/workflows/mobile_codeql.yml"
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
- permissions:
- security-events: write
- strategy:
- fail-fast: false
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: "javascript-typescript"
- queries: security-and-quality
- - name: Autobuild
- uses: github/codeql-action/autobuild@v3
- with:
- working-directory: frontend/sac-mobile
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
- with:
- category: "/language:javascript-typescript"
diff --git a/.github/workflows/web_codeql.yml b/.github/workflows/web_codeql.yml
deleted file mode 100644
index b1eee3590..000000000
--- a/.github/workflows/web_codeql.yml
+++ /dev/null
@@ -1,42 +0,0 @@
-name: Web CodeQL
-
-on:
- push:
- branches: ["main"]
- paths:
- - "frontend/sac-web/**"
- - ".github/workflows/mweb_codeql.yml"
- pull_request:
- branches: ["main"]
- paths:
- - "frontend/sac-web/**"
- - ".github/workflows/web_codeql.yml"
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
- permissions:
- security-events: write
- strategy:
- fail-fast: false
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: "javascript-typescript"
- queries: security-and-quality
- - name: Autobuild
- uses: github/codeql-action/autobuild@v3
- with:
- working-directory: frontend/sac-web
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
- with:
- category: "/language:javascript-typescript"
From def817633eebeffadf0abfa1c459b5318d913de4 Mon Sep 17 00:00:00 2001
From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
Date: Thu, 1 Feb 2024 13:58:05 -0500
Subject: [PATCH 05/33] Fix: CodeQL Running Too Often (#135)
---
.github/workflows/backend_codeql.yml | 45 +++++++++++++
.github/workflows/cli_codeql.yml | 45 +++++++++++++
.github/workflows/codeql.yml | 98 ----------------------------
.github/workflows/mobile_codeql.yml | 42 ++++++++++++
.github/workflows/web_codeql.yml | 42 ++++++++++++
5 files changed, 174 insertions(+), 98 deletions(-)
create mode 100644 .github/workflows/backend_codeql.yml
create mode 100644 .github/workflows/cli_codeql.yml
delete mode 100644 .github/workflows/codeql.yml
create mode 100644 .github/workflows/mobile_codeql.yml
create mode 100644 .github/workflows/web_codeql.yml
diff --git a/.github/workflows/backend_codeql.yml b/.github/workflows/backend_codeql.yml
new file mode 100644
index 000000000..60bba78cc
--- /dev/null
+++ b/.github/workflows/backend_codeql.yml
@@ -0,0 +1,45 @@
+name: Backend CodeQL
+
+on:
+ push:
+ branches: ["main"]
+ paths:
+ - "backend/**"
+ - ".github/workflows/backend_codeql.yml"
+ pull_request:
+ branches: ["main"]
+ paths:
+ - "backend/**"
+ - ".github/workflows/backend_codeql.yml"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ strategy:
+ fail-fast: false
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Set up Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: "1.21"
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: "go"
+ queries: security-and-quality
+ - name: Build
+ run: |
+ cd ./backend/ && go build -o backend src/main.go
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:go"
diff --git a/.github/workflows/cli_codeql.yml b/.github/workflows/cli_codeql.yml
new file mode 100644
index 000000000..caacf0951
--- /dev/null
+++ b/.github/workflows/cli_codeql.yml
@@ -0,0 +1,45 @@
+name: CLI CodeQL
+
+on:
+ push:
+ branches: ["main"]
+ paths:
+ - "cli/**"
+ - ".github/workflows/cli_codeql.yml"
+ pull_request:
+ branches: ["main"]
+ paths:
+ - "cli/**"
+ - ".github/workflows/cli_codeql.yml"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ strategy:
+ fail-fast: false
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Set up Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: "1.21"
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: "go"
+ queries: security-and-quality
+ - name: Build
+ run: |
+ cd ./cli/ && go build -o cli main.go
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:go"
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
deleted file mode 100644
index c8a51aadf..000000000
--- a/.github/workflows/codeql.yml
+++ /dev/null
@@ -1,98 +0,0 @@
-name: CodeQL
-
-on:
- push:
- branches: ["main"]
- paths:
- - "backend/**"
- - "cli/**"
- - "frontend/**"
- - ".github/workflows/codeql.yml"
- pull_request:
- branches: ["main"]
- paths:
- - "backend/**"
- - "cli/**"
- - "frontend/**"
- - ".github/workflows/codeql.yml"
- schedule:
- - cron: "0 0 * * 1"
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- analyze-js-ts:
- name: Analyze JavaScript and TypeScript
- runs-on: ubuntu-latest
- permissions:
- security-events: write
- strategy:
- fail-fast: false
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: "javascript-typescript"
- queries: security-and-quality
- - name: Autobuild
- uses: github/codeql-action/autobuild@v3
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
- with:
- category: "/language:javascript-typescript"
- analyze-backend-go:
- name: Analyze Backend Go
- runs-on: ubuntu-latest
- permissions:
- security-events: write
- strategy:
- fail-fast: false
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- - name: Set up Go
- uses: actions/setup-go@v3
- with:
- go-version: "1.21"
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: "go"
- queries: security-and-quality
- - name: Build
- run: |
- cd ./backend/ && go build -o backend src/main.go
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
- with:
- category: "/language:go"
- analyze-cli-go:
- name: Analyze CLI Go
- runs-on: ubuntu-latest
- permissions:
- security-events: write
- strategy:
- fail-fast: false
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- - name: Set up Go
- uses: actions/setup-go@v3
- with:
- go-version: "1.21"
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v3
- with:
- languages: "go"
- queries: security-and-quality
- - name: Build
- run: |
- cd ./cli/ && go build -o cli main.go
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v3
- with:
- category: "/language:go"
diff --git a/.github/workflows/mobile_codeql.yml b/.github/workflows/mobile_codeql.yml
new file mode 100644
index 000000000..c0a12f95f
--- /dev/null
+++ b/.github/workflows/mobile_codeql.yml
@@ -0,0 +1,42 @@
+name: Mobile CodeQL
+
+on:
+ push:
+ branches: ["main"]
+ paths:
+ - "frontend/sac-mobile/**"
+ - ".github/workflows/mobile_codeql.yml"
+ pull_request:
+ branches: ["main"]
+ paths:
+ - "frontend/sac-mobile/**"
+ - ".github/workflows/mobile_codeql.yml"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ strategy:
+ fail-fast: false
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: "javascript-typescript"
+ queries: security-and-quality
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v3
+ with:
+ working-directory: frontend/sac-mobile
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:javascript-typescript"
diff --git a/.github/workflows/web_codeql.yml b/.github/workflows/web_codeql.yml
new file mode 100644
index 000000000..b1eee3590
--- /dev/null
+++ b/.github/workflows/web_codeql.yml
@@ -0,0 +1,42 @@
+name: Web CodeQL
+
+on:
+ push:
+ branches: ["main"]
+ paths:
+ - "frontend/sac-web/**"
+ - ".github/workflows/mweb_codeql.yml"
+ pull_request:
+ branches: ["main"]
+ paths:
+ - "frontend/sac-web/**"
+ - ".github/workflows/web_codeql.yml"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+ permissions:
+ security-events: write
+ strategy:
+ fail-fast: false
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: "javascript-typescript"
+ queries: security-and-quality
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v3
+ with:
+ working-directory: frontend/sac-web
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:javascript-typescript"
From 1673ef0477a60e71c9ef3a3ce5ef2348f0bdd200 Mon Sep 17 00:00:00 2001
From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
Date: Thu, 1 Feb 2024 14:15:19 -0500
Subject: [PATCH 06/33] GHWF Updates (#136)
---
.github/workflows/auto_assign_author.yml | 2 +-
.github/workflows/auto_request_review.yml | 2 +-
.github/workflows/backend.yml | 5 +-
.github/workflows/backend_codeql.yml | 3 +-
.github/workflows/cli.yml | 63 +++++++++++++++++++++++
.github/workflows/cli_codeql.yml | 3 +-
.github/workflows/mobile_codeql.yml | 3 +-
.github/workflows/web_codeql.yml | 3 +-
README.md | 31 +++++++++++
9 files changed, 101 insertions(+), 14 deletions(-)
create mode 100644 .github/workflows/cli.yml
diff --git a/.github/workflows/auto_assign_author.yml b/.github/workflows/auto_assign_author.yml
index a92a9c3f4..00512ecf4 100644
--- a/.github/workflows/auto_assign_author.yml
+++ b/.github/workflows/auto_assign_author.yml
@@ -2,7 +2,7 @@ name: Auto Assign Author
on:
pull_request:
- types: [opened, ready_for_review, reopened]
+ types: [opened, reopened]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
diff --git a/.github/workflows/auto_request_review.yml b/.github/workflows/auto_request_review.yml
index e38f94b42..797b2771b 100644
--- a/.github/workflows/auto_request_review.yml
+++ b/.github/workflows/auto_request_review.yml
@@ -2,7 +2,7 @@ name: Auto Request Review
on:
pull_request:
- types: [opened, ready_for_review, reopened]
+ types: [opened, reopened]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml
index 4c4d773bc..1fa9b0328 100644
--- a/.github/workflows/backend.yml
+++ b/.github/workflows/backend.yml
@@ -2,12 +2,11 @@ name: Backend
on:
push:
- branches:
- - main
paths:
- "backend/**"
- ".github/workflows/backend.yml"
pull_request:
+ types: opened
paths:
- "backend/**"
- ".github/workflows/backend.yml"
@@ -44,7 +43,6 @@ jobs:
echo "$unformatted_files"
exit 1
fi
-
lint:
name: Lint
runs-on: ubuntu-latest
@@ -63,7 +61,6 @@ jobs:
echo "::error::Linting issues found"
exit 1
fi
-
test:
name: Test
runs-on: ubuntu-latest
diff --git a/.github/workflows/backend_codeql.yml b/.github/workflows/backend_codeql.yml
index 60bba78cc..9e394fc1a 100644
--- a/.github/workflows/backend_codeql.yml
+++ b/.github/workflows/backend_codeql.yml
@@ -2,12 +2,11 @@ name: Backend CodeQL
on:
push:
- branches: ["main"]
paths:
- "backend/**"
- ".github/workflows/backend_codeql.yml"
pull_request:
- branches: ["main"]
+ types: opened
paths:
- "backend/**"
- ".github/workflows/backend_codeql.yml"
diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml
new file mode 100644
index 000000000..8abe3bc6d
--- /dev/null
+++ b/.github/workflows/cli.yml
@@ -0,0 +1,63 @@
+name: CLI
+
+on:
+ push:
+ paths:
+ - "cli/**"
+ - ".github/workflows/cli.yml"
+ pull_request:
+ types: opened
+ paths:
+ - "cli/**"
+ - ".github/workflows/cli.yml"
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+jobs:
+ format:
+ name: Format
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v3
+ - name: Set up Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: "1.21"
+ - name: Cache Go Modules
+ uses: actions/cache@v3
+ with:
+ path: ~/go/pkg/mod
+ key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+ restore-keys: |
+ ${{ runner.os }}-go-
+ - name: Install gofumpt
+ run: go install mvdan.cc/gofumpt@latest
+ - name: Check code formatting
+ run: |
+ unformatted_files=$(gofumpt -l ./cli/)
+ if [ -n "$unformatted_files" ]; then
+ echo "Files not formatted:"
+ echo "$unformatted_files"
+ exit 1
+ fi
+ lint:
+ name: Lint
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v3
+ - name: Set up Go
+ uses: actions/setup-go@v3
+ with:
+ go-version: "1.21"
+ - name: Enforce linting
+ run: |
+ cd ./cli/ && lint_output=$(go vet ./...)
+ if [[ -n "$lint_output" ]]; then
+ echo "$lint_output"
+ echo "::error::Linting issues found"
+ exit 1
+ fi
diff --git a/.github/workflows/cli_codeql.yml b/.github/workflows/cli_codeql.yml
index caacf0951..529636c27 100644
--- a/.github/workflows/cli_codeql.yml
+++ b/.github/workflows/cli_codeql.yml
@@ -2,12 +2,11 @@ name: CLI CodeQL
on:
push:
- branches: ["main"]
paths:
- "cli/**"
- ".github/workflows/cli_codeql.yml"
pull_request:
- branches: ["main"]
+ types: opened
paths:
- "cli/**"
- ".github/workflows/cli_codeql.yml"
diff --git a/.github/workflows/mobile_codeql.yml b/.github/workflows/mobile_codeql.yml
index c0a12f95f..3b85ea66a 100644
--- a/.github/workflows/mobile_codeql.yml
+++ b/.github/workflows/mobile_codeql.yml
@@ -2,12 +2,11 @@ name: Mobile CodeQL
on:
push:
- branches: ["main"]
paths:
- "frontend/sac-mobile/**"
- ".github/workflows/mobile_codeql.yml"
pull_request:
- branches: ["main"]
+ types: opened
paths:
- "frontend/sac-mobile/**"
- ".github/workflows/mobile_codeql.yml"
diff --git a/.github/workflows/web_codeql.yml b/.github/workflows/web_codeql.yml
index b1eee3590..1b6b87bc0 100644
--- a/.github/workflows/web_codeql.yml
+++ b/.github/workflows/web_codeql.yml
@@ -2,12 +2,11 @@ name: Web CodeQL
on:
push:
- branches: ["main"]
paths:
- "frontend/sac-web/**"
- ".github/workflows/mweb_codeql.yml"
pull_request:
- branches: ["main"]
+ types: opened
paths:
- "frontend/sac-web/**"
- ".github/workflows/web_codeql.yml"
diff --git a/README.md b/README.md
index 282b5e824..cf5aa1666 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
## Contributors
From 66ba6832313ebf1a4883f865a77cdecba677349c Mon Sep 17 00:00:00 2001
From: David Oduneye <44040421+DOOduneye@users.noreply.github.com>
Date: Thu, 1 Feb 2024 20:27:57 -0500
Subject: [PATCH 07/33] feat: updated cli (#138)
---
CONTRIBUTING.md | 32 ++++++++++---
cli/commands/clean_tests.go | 6 ++-
cli/commands/drop.go | 94 +++++++++++++++++++++++++++++--------
cli/commands/format.go | 5 +-
cli/commands/insert.go | 8 ++--
cli/commands/lint.go | 5 +-
cli/commands/migrate.go | 5 +-
cli/commands/reset.go | 63 ++++++++++++++++++++++---
cli/commands/swagger.go | 5 +-
cli/commands/test.go | 10 ++--
cli/main.go | 9 ++--
11 files changed, 184 insertions(+), 58 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 594be0b25..6be217e80 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -128,13 +128,31 @@
To install use `./install.sh` and then run `sac-cli` to see all commands.
- ```console
- sac-cli migrate // run migrations
- sac-cli reset // reset database
- sac-cli swagger // generate swagger docs
- sac-cli lint // lint code
- sac-cli format // format code
- sac-cli test // run tests
+ ```console
+ NAME:
+ sac-cli - CLI for SAC
+
+ USAGE:
+ sac-cli [global options] command [command options]
+
+ COMMANDS:
+ swagger, swag Updates the swagger documentation
+ be Run the backend
+ test, t Runs tests
+ format, f Runs formatting tools
+ lint, l Runs linting tools
+ help, h Shows a list of commands or help for one command
+ Database Operations:
+ clean Remove databases used for testing
+ migrate Migrate the database, creating tables and relationships
+ insert, i Inserts mock data into the database
+ reset, r Resets the database, dropping all tables, clearing data, and re-running migrations
+ * can use --data to just reset data and not drop tables
+ drop, d Drop data with a migration or drops the entire database
+ * can use --data to just drop data and not drop tables
+
+ GLOBAL OPTIONS:
+ --help, -h show help
```
# Git Flow
diff --git a/cli/commands/clean_tests.go b/cli/commands/clean_tests.go
index 3f3f77bd6..a984d6c77 100644
--- a/cli/commands/clean_tests.go
+++ b/cli/commands/clean_tests.go
@@ -12,8 +12,10 @@ import (
func ClearDBCommand() *cli.Command {
command := cli.Command{
- Name: "clean",
- Usage: "Remove databases used for testing",
+ Name: "clean",
+ Category: "Database Operations",
+ Aliases: []string{"c"},
+ Usage: "Remove databases used for testing",
Action: func(c *cli.Context) error {
if c.Args().Len() > 0 {
return cli.Exit("Invalid arguments", 1)
diff --git a/cli/commands/drop.go b/cli/commands/drop.go
index 2bcb5d132..5f75e1726 100644
--- a/cli/commands/drop.go
+++ b/cli/commands/drop.go
@@ -5,22 +5,38 @@ import (
"fmt"
"sync"
- _ "github.com/lib/pq"
"github.com/urfave/cli/v2"
)
-func DropDBCommand() *cli.Command {
+var dbMutex sync.Mutex
+
+func DropCommand() *cli.Command {
command := cli.Command{
- Name: "drop",
- Usage: "Drops the database",
+ Name: "drop",
+ Aliases: []string{"d"},
+ Usage: "Drop data with a migration or drops the entire database",
+ Category: "Database Operations",
+ Flags: []cli.Flag{
+ &cli.BoolFlag{
+ Name: "data",
+ Usage: "Drop only data, not the entire database",
+ },
+ },
Action: func(c *cli.Context) error {
if c.Args().Len() > 0 {
return cli.Exit("Invalid arguments", 1)
}
- err := DropDB()
- if err != nil {
- return cli.Exit(err.Error(), 1)
+ if c.Bool("data") {
+ err := DropData()
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
+ } else {
+ err := DropDB()
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
}
return nil
@@ -30,9 +46,54 @@ func DropDBCommand() *cli.Command {
return &command
}
+func DropData() error {
+ fmt.Println("Clearing database")
+
+ dbMutex.Lock()
+ defer dbMutex.Unlock()
+
+ db, err := sql.Open("postgres", CONFIG.Database.WithDb())
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+
+ rows, err := db.Query("SELECT tablename FROM pg_tables WHERE schemaname = 'public'")
+ if err != nil {
+ return fmt.Errorf("error retrieving tables: %w", err)
+ }
+ defer rows.Close()
+
+ for rows.Next() {
+ var tablename string
+ if err := rows.Scan(&tablename); err != nil {
+ return fmt.Errorf("error scanning table name: %w", err)
+ }
+
+ deleteStmt := fmt.Sprintf("DELETE FROM \"%s\"", tablename)
+ _, err := db.Exec(deleteStmt)
+ if err != nil {
+ return fmt.Errorf("error deleting rows from table %s: %w", tablename, err)
+ }
+ fmt.Printf("Removed all rows from table %s\n", tablename)
+ }
+
+ if err := rows.Err(); err != nil {
+ return fmt.Errorf("error in rows handling: %w", err)
+ }
+
+ Migrate()
+
+ fmt.Println("All rows removed successfully.")
+ return nil
+}
+
func DropDB() error {
fmt.Println("Dropping database")
+ dbMutex.Lock()
+ defer dbMutex.Unlock()
+
db, err := sql.Open("postgres", CONFIG.Database.WithDb())
if err != nil {
return err
@@ -61,8 +122,6 @@ func DropDB() error {
defer rows.Close()
- var wg sync.WaitGroup
-
fmt.Println("Dropping tables...")
for rows.Next() {
@@ -71,23 +130,18 @@ func DropDB() error {
return fmt.Errorf("error reading table name: %w", err)
}
- wg.Add(1)
- go func(table string) {
- defer wg.Done()
- dropStmt := fmt.Sprintf("DROP TABLE IF EXISTS \"%s\" CASCADE", table)
- if _, err := db.Exec(dropStmt); err != nil {
- fmt.Printf("Error dropping table %s: %v\n", table, err)
- } else {
- fmt.Printf("Dropped table %s\n", table)
- }
- }(tablename)
+ dropStmt := fmt.Sprintf("DROP TABLE IF EXISTS \"%s\" CASCADE", tablename)
+ if _, err := db.Exec(dropStmt); err != nil {
+ fmt.Printf("Error dropping table %s: %v\n", tablename, err)
+ } else {
+ fmt.Printf("Dropped table %s\n", tablename)
+ }
}
if err := rows.Err(); err != nil {
return fmt.Errorf("error in rows handling: %w", err)
}
- wg.Wait()
fmt.Println("All tables dropped successfully.")
return nil
}
diff --git a/cli/commands/format.go b/cli/commands/format.go
index 60f6e35f5..9b60c9fc2 100644
--- a/cli/commands/format.go
+++ b/cli/commands/format.go
@@ -10,8 +10,9 @@ import (
func FormatCommand() *cli.Command {
command := cli.Command{
- Name: "format",
- Usage: "Runs formatting tools",
+ Name: "format",
+ Usage: "Runs formatting tools",
+ Aliases: []string{"f"},
Flags: []cli.Flag{
&cli.StringFlag{
Name: "frontend",
diff --git a/cli/commands/insert.go b/cli/commands/insert.go
index 10387b346..9f50d4171 100644
--- a/cli/commands/insert.go
+++ b/cli/commands/insert.go
@@ -10,10 +10,12 @@ import (
"github.com/urfave/cli/v2"
)
-func InsertDBCommand() *cli.Command {
+func InsertCommand() *cli.Command {
command := cli.Command{
- Name: "insert",
- Usage: "Inserts mock data into the database",
+ Name: "insert",
+ Category: "Database Operations",
+ Aliases: []string{"i"},
+ Usage: "Inserts mock data into the database",
Action: func(c *cli.Context) error {
if c.Args().Len() > 0 {
return cli.Exit("Invalid arguments", 1)
diff --git a/cli/commands/lint.go b/cli/commands/lint.go
index ea1680fb8..3351ab554 100644
--- a/cli/commands/lint.go
+++ b/cli/commands/lint.go
@@ -10,8 +10,9 @@ import (
func LintCommand() *cli.Command {
command := cli.Command{
- Name: "lint",
- Usage: "Runs linting tools",
+ Name: "lint",
+ Aliases: []string{"l"},
+ Usage: "Runs linting tools",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "frontend",
diff --git a/cli/commands/migrate.go b/cli/commands/migrate.go
index 390eb07a8..a2189e805 100644
--- a/cli/commands/migrate.go
+++ b/cli/commands/migrate.go
@@ -9,8 +9,9 @@ import (
func MigrateCommand() *cli.Command {
command := cli.Command{
- Name: "migrate",
- Usage: "Migrate the database",
+ Name: "migrate",
+ Usage: "Migrate the database, creating tables and relationships",
+ Category: "Database Operations",
Action: func(c *cli.Context) error {
if c.Args().Len() > 0 {
return cli.Exit("Invalid arguments", 1)
diff --git a/cli/commands/reset.go b/cli/commands/reset.go
index 74ac689fb..07929c1fb 100644
--- a/cli/commands/reset.go
+++ b/cli/commands/reset.go
@@ -7,16 +7,35 @@ import (
"github.com/urfave/cli/v2"
)
-func ResetDBCommand() *cli.Command {
+func ResetCommand() *cli.Command {
command := cli.Command{
- Name: "reset",
- Usage: "Resets the database",
+ Name: "reset",
+ Aliases: []string{"r"},
+ Usage: "Resets the database, dropping all tables, clearing data, and re-running migrations",
+ Category: "Database Operations",
+ Flags: []cli.Flag{
+ &cli.BoolFlag{
+ Name: "data",
+ Usage: "Reset only data, not the entire database, will re-run migrations",
+ },
+ },
Action: func(c *cli.Context) error {
if c.Args().Len() > 0 {
return cli.Exit("Invalid arguments", 1)
}
- ResetDB()
+ if c.Bool("data") {
+ err := ResetData()
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
+ } else {
+ err := ResetMigration()
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
+ }
+
return nil
},
}
@@ -24,8 +43,38 @@ func ResetDBCommand() *cli.Command {
return &command
}
-func ResetDB() error {
- fmt.Println("Resetting database")
+func ResetData() error {
+ fmt.Println("Clearing database")
+
+ DropData()
+
+ cmd := exec.Command("sleep", "1")
+ cmd.Dir = BACKEND_DIR
+
+ err := cmd.Run()
+ if err != nil {
+ return cli.Exit("Error running sleep", 1)
+ }
+
+ Migrate()
+
+ cmd = exec.Command("sleep", "1")
+ cmd.Dir = BACKEND_DIR
+
+ err = cmd.Run()
+ if err != nil {
+ return cli.Exit("Error running sleep", 1)
+ }
+
+ InsertDB()
+
+ fmt.Println("Data reset successfully")
+
+ return nil
+}
+
+func ResetMigration() error {
+ fmt.Println("Resetting migration")
DropDB()
@@ -39,7 +88,7 @@ func ResetDB() error {
Migrate()
- fmt.Println("Done resetting database")
+ fmt.Println("Migration reset successfully")
return nil
}
diff --git a/cli/commands/swagger.go b/cli/commands/swagger.go
index 467c58b2c..bac4191d3 100644
--- a/cli/commands/swagger.go
+++ b/cli/commands/swagger.go
@@ -9,8 +9,9 @@ import (
func SwaggerCommand() *cli.Command {
command := cli.Command{
- Name: "swagger",
- Usage: "Updates the swagger documentation",
+ Name: "swagger",
+ Aliases: []string{"swag"},
+ Usage: "Updates the swagger documentation",
Action: func(c *cli.Context) error {
if c.Args().Len() > 0 {
return cli.Exit("Invalid arguments", 1)
diff --git a/cli/commands/test.go b/cli/commands/test.go
index 3172b5366..0fa9d6a27 100644
--- a/cli/commands/test.go
+++ b/cli/commands/test.go
@@ -10,8 +10,9 @@ import (
func TestCommand() *cli.Command {
command := cli.Command{
- Name: "test",
- Usage: "Runs tests",
+ Name: "test",
+ Aliases: []string{"t"},
+ Usage: "Runs tests",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "frontend",
@@ -37,13 +38,10 @@ func TestCommand() *cli.Command {
folder := c.String("frontend")
runFrontend := folder != ""
runBackend := c.Bool("backend")
-
Test(folder, runFrontend, runBackend)
-
return nil
},
}
-
return &command
}
@@ -69,7 +67,6 @@ func Test(folder string, runFrontend bool, runBackend bool) error {
}
wg.Wait()
-
return nil
}
@@ -86,7 +83,6 @@ func BackendTest() error {
}
fmt.Println(string(out))
-
return nil
}
diff --git a/cli/main.go b/cli/main.go
index 9e5d6e4a2..7ed2f9a0b 100755
--- a/cli/main.go
+++ b/cli/main.go
@@ -16,16 +16,17 @@ func main() {
commands.SwaggerCommand(),
commands.ClearDBCommand(),
commands.MigrateCommand(),
- commands.ResetDBCommand(),
- commands.InsertDBCommand(),
- commands.DropDBCommand(),
+ commands.ResetCommand(),
+ commands.InsertCommand(),
+ commands.DropCommand(),
+ commands.ResetCommand(),
+ commands.DropCommand(),
commands.RunBackendCommand(),
commands.TestCommand(), // TODO: frontend tests
commands.FormatCommand(), // TODO: frontend format
commands.LintCommand(), // TODO: frontend lint
},
}
-
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
From e2c3dbb89060b3e4e34dce8b1463e57e896b519a Mon Sep 17 00:00:00 2001
From: David Oduneye <44040421+DOOduneye@users.noreply.github.com>
Date: Thu, 1 Feb 2024 22:38:31 -0500
Subject: [PATCH 08/33] SAC-17 User Authentication (#38)
Co-authored-by: garrettladley
Co-authored-by: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
---
.github/workflows/backend.yml | 2 +-
backend/go.mod | 1 +
backend/go.sum | 2 +
backend/src/auth/password.go | 2 +-
backend/src/auth/tokens.go | 193 +++++++++++++++++++++++++++
backend/src/config/config.go | 28 ++++
backend/src/controllers/auth.go | 151 +++++++++++++++++++++
backend/src/database/db.go | 12 +-
backend/src/errors/club.go | 4 +
backend/src/errors/common.go | 36 +++++
backend/src/main.go | 2 +-
backend/src/middleware/auth.go | 60 +++++++++
backend/src/middleware/club.go | 47 +++++++
backend/src/middleware/middleware.go | 27 ++++
backend/src/middleware/user.go | 37 +++++
backend/src/migrations/data.sql | 7 +-
backend/src/models/club.go | 11 +-
backend/src/models/membership.go | 30 +++++
backend/src/models/user.go | 12 +-
backend/src/server/server.go | 71 +++++++---
backend/src/services/auth.go | 81 +++++++++++
backend/src/services/category.go | 4 +
backend/src/services/club.go | 4 +
backend/src/services/tag.go | 4 +
backend/src/services/user.go | 4 +
backend/src/transactions/club.go | 15 +++
backend/src/transactions/user.go | 10 ++
backend/src/types/custom_claims.go | 8 ++
backend/src/types/permissions.go | 91 +++++++++++++
backend/src/utilities/response.go | 7 +
backend/src/utilities/validator.go | 8 +-
backend/tests/api/helpers.go | 2 +-
backend/tests/auth_test.go | 148 ++++++++++++++++++++
cli/commands/insert.go | 12 +-
config/local.yml | 5 +
go.work.sum | 59 +-------
36 files changed, 1102 insertions(+), 95 deletions(-)
create mode 100644 backend/src/auth/tokens.go
create mode 100644 backend/src/controllers/auth.go
create mode 100644 backend/src/middleware/auth.go
create mode 100644 backend/src/middleware/club.go
create mode 100644 backend/src/middleware/middleware.go
create mode 100644 backend/src/middleware/user.go
create mode 100644 backend/src/models/membership.go
create mode 100644 backend/src/services/auth.go
create mode 100644 backend/src/types/custom_claims.go
create mode 100644 backend/src/types/permissions.go
create mode 100644 backend/src/utilities/response.go
create mode 100644 backend/tests/auth_test.go
diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml
index 1fa9b0328..b370f8a1e 100644
--- a/.github/workflows/backend.yml
+++ b/.github/workflows/backend.yml
@@ -99,6 +99,6 @@ jobs:
- name: Migrate DB
run: cd ./backend/src && go run main.go --only-migrate
- name: Run Tests with Coverage
- run: cd ./backend/ && go test -failfast -benchmem -race -coverprofile=coverage.txt ./...
+ run: cd ./backend/ && go test -benchmem -race -coverprofile=coverage.txt ./...
- name: Print Coverage
run: cd ./backend/ && go tool cover -func=coverage.txt
diff --git a/backend/go.mod b/backend/go.mod
index 29e88a6a8..7ea6f10af 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -6,6 +6,7 @@ require (
github.com/go-playground/validator/v10 v10.17.0
github.com/gofiber/fiber/v2 v2.52.0
github.com/gofiber/swagger v0.1.14
+ github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/spf13/viper v1.18.2
github.com/swaggo/swag v1.16.2
gorm.io/driver/postgres v1.5.4
diff --git a/backend/go.sum b/backend/go.sum
index 965c30971..3e4d881f4 100644
--- a/backend/go.sum
+++ b/backend/go.sum
@@ -44,6 +44,8 @@ github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/
github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/gofiber/swagger v0.1.14 h1:o524wh4QaS4eKhUCpj7M0Qhn8hvtzcyxDsfZLXuQcRI=
github.com/gofiber/swagger v0.1.14/go.mod h1:DCk1fUPsj+P07CKaZttBbV1WzTZSQcSxfub8y9/BFr8=
+github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
+github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
diff --git a/backend/src/auth/password.go b/backend/src/auth/password.go
index d27831176..852d5c2ed 100644
--- a/backend/src/auth/password.go
+++ b/backend/src/auth/password.go
@@ -56,7 +56,7 @@ var (
ErrIncompatibleVersion = errors.New("incompatible version of argon2")
)
-func ComparePasswordAndHash(password, encodedHash string) (bool, error) {
+func ComparePasswordAndHash(password string, encodedHash string) (bool, error) {
p, salt, hash, err := decodeHash(encodedHash)
if err != nil {
return false, err
diff --git a/backend/src/auth/tokens.go b/backend/src/auth/tokens.go
new file mode 100644
index 000000000..255b2a5be
--- /dev/null
+++ b/backend/src/auth/tokens.go
@@ -0,0 +1,193 @@
+package auth
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/GenerateNU/sac/backend/src/config"
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/types"
+
+ "github.com/gofiber/fiber/v2"
+ "github.com/golang-jwt/jwt"
+)
+
+func CreateTokenPair(id string, role string, authSettings config.AuthSettings) (*string, *string, *errors.Error) {
+ accessToken, catErr := CreateAccessToken(id, role, authSettings.AccessTokenExpiry, authSettings.AccessToken)
+ if catErr != nil {
+ return nil, nil, catErr
+ }
+
+ refreshToken, crtErr := CreateRefreshToken(id, authSettings.RefreshTokenExpiry, authSettings.RefreshToken)
+ if crtErr != nil {
+ return nil, nil, crtErr
+ }
+
+ return accessToken, refreshToken, nil
+}
+
+// CreateAccessToken creates a new access token for the user
+func CreateAccessToken(id string, role string, accessExpiresAfter uint, accessTokenSecret string) (*string, *errors.Error) {
+ if id == "" || role == "" {
+ return nil, &errors.FailedToCreateAccessToken
+ }
+
+ accessTokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, &types.CustomClaims{
+ StandardClaims: jwt.StandardClaims{
+ IssuedAt: time.Now().Unix(),
+ Issuer: id,
+ ExpiresAt: time.Now().Add(time.Duration(accessExpiresAfter)).Unix(),
+ },
+ Role: role,
+ })
+
+ accessToken, err := SignToken(accessTokenClaims, accessTokenSecret)
+ if err != nil {
+ return nil, err
+ }
+
+ return accessToken, nil
+}
+
+// CreateRefreshToken creates a new refresh token for the user
+func CreateRefreshToken(id string, refreshExpiresAfter uint, refreshTokenSecret string) (*string, *errors.Error) {
+ if id == "" {
+ return nil, &errors.FailedToCreateRefreshToken
+ }
+
+ refreshTokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, &jwt.StandardClaims{
+ IssuedAt: time.Now().Unix(),
+ Issuer: id,
+ ExpiresAt: time.Now().Add(time.Hour * 24 * time.Duration(refreshExpiresAfter)).Unix(),
+ })
+
+ refreshToken, err := SignToken(refreshTokenClaims, refreshTokenSecret)
+ if err != nil {
+ return nil, err
+ }
+
+ return refreshToken, nil
+}
+
+func SignToken(token *jwt.Token, secret string) (*string, *errors.Error) {
+ if token == nil || secret == "" {
+ fmt.Println(token)
+ return nil, &errors.FailedToSignToken
+ }
+
+ tokenString, err := token.SignedString([]byte(secret))
+ if err != nil {
+ return nil, &errors.FailedToSignToken
+ }
+ return &tokenString, nil
+}
+
+// CreateCookie creates a new cookie
+func CreateCookie(name string, value string, expires time.Time) *fiber.Cookie {
+ return &fiber.Cookie{
+ Name: name,
+ Value: value,
+ Expires: expires,
+ HTTPOnly: true,
+ }
+}
+
+// ExpireCookie expires a cookie
+func ExpireCookie(name string) *fiber.Cookie {
+ return &fiber.Cookie{
+ Name: name,
+ Value: "",
+ Expires: time.Now().Add(-time.Hour),
+ HTTPOnly: true,
+ }
+}
+
+// RefreshAccessToken refreshes the access token
+func RefreshAccessToken(refreshCookie string, role string, accessExpiresAfter uint, accessTokenSecret string) (*string, *errors.Error) {
+ // Parse the refresh token
+ refreshToken, err := ParseRefreshToken(refreshCookie)
+ if err != nil {
+ return nil, &errors.FailedToParseRefreshToken
+ }
+
+ // Extract the claims from the refresh token
+ claims, ok := refreshToken.Claims.(*jwt.StandardClaims)
+ if !ok || !refreshToken.Valid {
+ return nil, &errors.FailedToValidateRefreshToken
+ }
+
+ // Create a new access token
+ accessToken, catErr := CreateAccessToken(claims.Issuer, role, accessExpiresAfter, accessTokenSecret)
+ if catErr != nil {
+ return nil, &errors.FailedToCreateAccessToken
+ }
+
+ return accessToken, nil
+}
+
+// ParseAccessToken parses the access token
+func ParseAccessToken(cookie string) (*jwt.Token, error) {
+ var settings config.Settings
+
+ return jwt.ParseWithClaims(cookie, &types.CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
+ return []byte(settings.Auth.AccessToken), nil
+ })
+}
+
+// ParseRefreshToken parses the refresh token
+func ParseRefreshToken(cookie string) (*jwt.Token, error) {
+ var settings config.Settings
+
+ return jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
+ return []byte(settings.Auth.RefreshToken), nil
+ })
+}
+
+// GetRoleFromToken gets the role from the custom claims
+func GetRoleFromToken(tokenString string) (*string, error) {
+ token, err := ParseAccessToken(tokenString)
+ if err != nil {
+ return nil, err
+ }
+
+ claims, ok := token.Claims.(*types.CustomClaims)
+ if !ok || !token.Valid {
+ return nil, &errors.FailedToValidateAccessToken
+ }
+
+ return &claims.Role, nil
+}
+
+// ExtractClaims extracts the claims from the token
+func ExtractAccessClaims(tokenString string) (*types.CustomClaims, *errors.Error) {
+ token, err := ParseAccessToken(tokenString)
+ if err != nil {
+ return nil, &errors.FailedToParseAccessToken
+ }
+
+ claims, ok := token.Claims.(*types.CustomClaims)
+ if !ok || !token.Valid {
+ return nil, &errors.FailedToValidateAccessToken
+ }
+
+ return claims, nil
+}
+
+// ExtractClaims extracts the claims from the token
+func ExtractRefreshClaims(tokenString string) (*jwt.StandardClaims, *errors.Error) {
+ token, err := ParseRefreshToken(tokenString)
+ if err != nil {
+ return nil, &errors.FailedToParseRefreshToken
+ }
+
+ claims, ok := token.Claims.(*jwt.StandardClaims)
+ if !ok || !token.Valid {
+ return nil, &errors.FailedToValidateRefreshToken
+ }
+
+ return claims, nil
+}
+
+func IsBlacklisted(token string) bool {
+ return false
+}
diff --git a/backend/src/config/config.go b/backend/src/config/config.go
index 252bce1f7..fdb435248 100644
--- a/backend/src/config/config.go
+++ b/backend/src/config/config.go
@@ -12,6 +12,7 @@ type Settings struct {
Application ApplicationSettings `yaml:"application"`
Database DatabaseSettings `yaml:"database"`
SuperUser SuperUserSettings `yaml:"superuser"`
+ Auth AuthSettings `yaml:"authsecret"`
}
type ProductionSettings struct {
@@ -63,6 +64,13 @@ type SuperUserSettings struct {
Password string `yaml:"password"`
}
+type AuthSettings struct {
+ AccessToken string `yaml:"accesstoken"`
+ RefreshToken string `yaml:"refreshtoken"`
+ AccessTokenExpiry uint `yaml:"accesstokenexpiry"`
+ RefreshTokenExpiry uint `yaml:"refreshtokenexpiry"`
+}
+
type Environment string
const (
@@ -113,6 +121,20 @@ func GetConfiguration(path string) (Settings, error) {
applicationPrefix := fmt.Sprintf("%sAPPLICATION__", appPrefix)
dbPrefix := fmt.Sprintf("%sDATABASE__", appPrefix)
superUserPrefix := fmt.Sprintf("%sSUPERUSER__", appPrefix)
+ authSecretPrefix := fmt.Sprintf("%sAUTHSECRET__", appPrefix)
+
+ authAccessExpiry := os.Getenv(fmt.Sprintf("%sACCESS_TOKEN_EXPIRY", authSecretPrefix))
+ authRefreshExpiry := os.Getenv(fmt.Sprintf("%sREFRESH_TOKEN_EXPIRY", authSecretPrefix))
+
+ authAccessExpiryInt, err := strconv.ParseUint(authAccessExpiry, 10, 16)
+ if err != nil {
+ return Settings{}, fmt.Errorf("failed to parse access token expiry: %w", err)
+ }
+
+ authRefreshExpiryInt, err := strconv.ParseUint(authRefreshExpiry, 10, 16)
+ if err != nil {
+ return Settings{}, fmt.Errorf("failed to parse refresh token expiry: %w", err)
+ }
portStr := os.Getenv(fmt.Sprintf("%sPORT", appPrefix))
portInt, err := strconv.ParseUint(portStr, 10, 16)
@@ -137,6 +159,12 @@ func GetConfiguration(path string) (Settings, error) {
SuperUser: SuperUserSettings{
Password: os.Getenv(fmt.Sprintf("%sPASSWORD", superUserPrefix)),
},
+ Auth: AuthSettings{
+ AccessToken: os.Getenv(fmt.Sprintf("%sACCESS_TOKEN", authSecretPrefix)),
+ RefreshToken: os.Getenv(fmt.Sprintf("%sREFRESH_TOKEN", authSecretPrefix)),
+ AccessTokenExpiry: uint(authAccessExpiryInt),
+ RefreshTokenExpiry: uint(authRefreshExpiryInt),
+ },
}, nil
}
}
diff --git a/backend/src/controllers/auth.go b/backend/src/controllers/auth.go
new file mode 100644
index 000000000..d0a4c40e6
--- /dev/null
+++ b/backend/src/controllers/auth.go
@@ -0,0 +1,151 @@
+package controllers
+
+import (
+ "time"
+
+ "github.com/GenerateNU/sac/backend/src/auth"
+ "github.com/GenerateNU/sac/backend/src/config"
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/GenerateNU/sac/backend/src/utilities"
+ "github.com/gofiber/fiber/v2"
+)
+
+type AuthController struct {
+ authService services.AuthServiceInterface
+ blacklist []string
+ AuthSettings config.AuthSettings
+}
+
+func NewAuthController(authService services.AuthServiceInterface, authSettings config.AuthSettings) *AuthController {
+ return &AuthController{authService: authService, blacklist: []string{}, AuthSettings: authSettings}
+}
+
+// Me godoc
+//
+// @Summary Gets the current user
+// @Description Returns the current user
+// @ID get-current-user
+// @Tags user
+// @Produce json
+// @Success 200 {object} models.User
+// @Failure 401 {string} string "failed to get current user"
+// @Router /api/v1/auth/me [get]
+func (a *AuthController) Me(c *fiber.Ctx) error {
+ // Extract token values from cookies
+ accessTokenValue := c.Cookies("access_token")
+
+ claims, err := auth.ExtractAccessClaims(accessTokenValue)
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ user, err := a.authService.Me(claims.Issuer)
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ return c.JSON(user)
+}
+
+// Login godoc
+//
+// @Summary Logs in a user
+// @Description Logs in a user
+// @ID login-user
+// @Tags user
+// @Accept json
+// @Produce json
+// @Param userBody body []string true "User Body"
+// @Success 200 {object} string "success"
+// @Failure 400 {string} string "failed to parse body"
+// @Failure 401 {string} string "failed to login user"
+// @Router /api/v1/auth/login [post]
+func (a *AuthController) Login(c *fiber.Ctx) error {
+ var userBody models.LoginUserResponseBody
+
+ if err := c.BodyParser(&userBody); err != nil {
+ errors.FailedToParseRequestBody.FiberError(c)
+ }
+
+ user, err := a.authService.Login(userBody)
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ accessToken, refreshToken, err := auth.CreateTokenPair(user.ID.String(), string(user.Role), a.AuthSettings)
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ // Set the tokens in the response
+ c.Cookie(auth.CreateCookie("access_token", *accessToken, time.Now().Add(time.Minute*60)))
+ c.Cookie(auth.CreateCookie("refresh_token", *refreshToken, time.Now().Add(time.Hour*24*30)))
+
+ return utilities.FiberMessage(c, fiber.StatusOK, "success")
+}
+
+// Refresh godoc
+//
+// @Summary Refreshes a user's access token
+// @Description Refreshes a user's access token
+// @ID refresh-user
+// @Tags user
+// @Accept json
+// @Produce json
+// @Success 200 {object} string "success"
+// @Failure 401 {string} string "failed to refresh access token"
+// @Router /api/v1/auth/refresh [get]
+func (a *AuthController) Refresh(c *fiber.Ctx) error {
+ // Extract token values from cookies
+ refreshTokenValue := c.Cookies("refresh_token")
+
+ // Extract id from refresh token
+ claims, err := auth.ExtractRefreshClaims(refreshTokenValue)
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ role, err := a.authService.GetRole(claims.Issuer)
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ accessToken, err := auth.RefreshAccessToken(refreshTokenValue, string(*role), a.AuthSettings.AccessTokenExpiry, a.AuthSettings.AccessToken)
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ // Set the access token in the response (e.g., in a cookie or JSON response)
+ c.Cookie(auth.CreateCookie("access_token", *accessToken, time.Now().Add(time.Minute*60)))
+
+ return utilities.FiberMessage(c, fiber.StatusOK, "success")
+}
+
+// Logout godoc
+//
+// @Summary Logs out a user
+// @Description Logs out a user
+// @ID logout-user
+// @Tags user
+// @Accept json
+// @Produce json
+// @Success 200 {object} string
+// @Failure 401 {string} string "failed to logout user"
+// @Router /api/v1/auth/logout [get]
+func (a *AuthController) Logout(c *fiber.Ctx) error {
+ // Extract token values from cookies
+ accessTokenValue := c.Cookies("access_token")
+ refreshTokenValue := c.Cookies("refresh_token")
+
+ // TODO: Redis
+ a.blacklist = append(a.blacklist, accessTokenValue)
+ a.blacklist = append(a.blacklist, refreshTokenValue)
+
+ // Expire and clear the cookies
+ c.Cookie(auth.ExpireCookie("access_token"))
+ c.Cookie(auth.ExpireCookie("refresh_token"))
+
+ return utilities.FiberMessage(c, fiber.StatusOK, "success")
+}
diff --git a/backend/src/database/db.go b/backend/src/database/db.go
index 9464ef412..32014e066 100644
--- a/backend/src/database/db.go
+++ b/backend/src/database/db.go
@@ -54,6 +54,7 @@ func MigrateDB(settings config.Settings, db *gorm.DB) error {
&models.PointOfContact{},
&models.Tag{},
&models.User{},
+ &models.Membership{},
)
if err != nil {
return err
@@ -117,19 +118,20 @@ func createSuperUser(settings config.Settings, db *gorm.DB) error {
RecruitmentType: models.Application,
ApplicationLink: "https://generatenu.com/apply",
Logo: "https://aws.amazon.com/s3",
- Admin: []models.User{superUser},
}
+
if err := tx.Create(&superClub).Error; err != nil {
tx.Rollback()
return err
}
- if err := tx.Model(&superClub).Association("Member").Append(&superUser); err != nil {
- tx.Rollback()
- return err
+ membership := models.Membership{
+ ClubID: superClub.ID,
+ UserID: superUser.ID,
+ MembershipType: models.MembershipTypeAdmin,
}
- if err := tx.Model(&superClub).Update("num_members", gorm.Expr("num_members + ?", 1)).Error; err != nil {
+ if err := tx.Create(&membership).Error; err != nil {
tx.Rollback()
return err
}
diff --git a/backend/src/errors/club.go b/backend/src/errors/club.go
index 226c44890..7b05d03bd 100644
--- a/backend/src/errors/club.go
+++ b/backend/src/errors/club.go
@@ -35,4 +35,8 @@ var (
StatusCode: fiber.StatusNotFound,
Message: "club not found",
}
+ FailedtoGetAdminIDs = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "failed to get admin ids",
+ }
)
diff --git a/backend/src/errors/common.go b/backend/src/errors/common.go
index b3d9a040e..a42f9b4fd 100644
--- a/backend/src/errors/common.go
+++ b/backend/src/errors/common.go
@@ -31,4 +31,40 @@ var (
StatusCode: fiber.StatusBadRequest,
Message: "failed to validate page",
}
+ Unauthorized = Error{
+ StatusCode: fiber.StatusUnauthorized,
+ Message: "unauthorized",
+ }
+ FailedToSignToken = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "failed to sign token",
+ }
+ FailedToCreateAccessToken = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "failed to create access token",
+ }
+ FailedToCreateRefreshToken = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "failed to create refresh token",
+ }
+ FailedToParseRefreshToken = Error{
+ StatusCode: fiber.StatusBadRequest,
+ Message: "failed to parse refresh token",
+ }
+ FailedToParseAccessToken = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "failed to parse access token",
+ }
+ FailedToValidateRefreshToken = Error{
+ StatusCode: fiber.StatusBadRequest,
+ Message: "failed to validate refresh token",
+ }
+ FailedToValidateAccessToken = Error{
+ StatusCode: fiber.StatusUnauthorized,
+ Message: "failed to validate access token",
+ }
+ FailedToParseUUID = Error{
+ StatusCode: fiber.StatusBadRequest,
+ Message: "failed to parse uuid",
+ }
)
diff --git a/backend/src/main.go b/backend/src/main.go
index c47cd14b0..a165ca95d 100644
--- a/backend/src/main.go
+++ b/backend/src/main.go
@@ -43,7 +43,7 @@ func main() {
panic(err)
}
- app := server.Init(db)
+ app := server.Init(db, config)
app.Listen(fmt.Sprintf("%s:%d", config.Application.Host, config.Application.Port))
}
diff --git a/backend/src/middleware/auth.go b/backend/src/middleware/auth.go
new file mode 100644
index 000000000..0900eeb1e
--- /dev/null
+++ b/backend/src/middleware/auth.go
@@ -0,0 +1,60 @@
+package middleware
+
+import (
+ "slices"
+
+ "github.com/GenerateNU/sac/backend/src/auth"
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/GenerateNU/sac/backend/src/types"
+
+ "github.com/gofiber/fiber/v2"
+)
+
+var paths = []string{
+ "/api/v1/auth/login",
+ "/api/v1/auth/refresh",
+ "/api/v1/users/",
+ "/api/v1/auth/logout",
+}
+
+func (m *MiddlewareService) Authenticate(c *fiber.Ctx) error {
+ if slices.Contains(paths, c.Path()) {
+ return c.Next()
+ }
+
+ token, err := auth.ParseAccessToken(c.Cookies("access_token"))
+ if err != nil {
+ return errors.FailedToParseAccessToken.FiberError(c)
+ }
+
+ _, ok := token.Claims.(*types.CustomClaims)
+ if !ok || !token.Valid {
+ return errors.FailedToValidateAccessToken.FiberError(c)
+ }
+
+ if auth.IsBlacklisted(c.Cookies("access_token")) {
+ return errors.Unauthorized.FiberError(c)
+ }
+
+ return c.Next()
+}
+
+func (m *MiddlewareService) Authorize(requiredPermissions ...types.Permission) func(c *fiber.Ctx) error {
+ return func(c *fiber.Ctx) error {
+ role, err := auth.GetRoleFromToken(c.Cookies("access_token"))
+ if err != nil {
+ return errors.FailedToParseAccessToken.FiberError(c)
+ }
+
+ userPermissions := types.GetPermissions(models.UserRole(*role))
+
+ for _, requiredPermission := range requiredPermissions {
+ if !slices.Contains(userPermissions, requiredPermission) {
+ return errors.Unauthorized.FiberError(c)
+ }
+ }
+
+ return c.Next()
+ }
+}
diff --git a/backend/src/middleware/club.go b/backend/src/middleware/club.go
new file mode 100644
index 000000000..c8529415e
--- /dev/null
+++ b/backend/src/middleware/club.go
@@ -0,0 +1,47 @@
+package middleware
+
+import (
+ "slices"
+
+ "github.com/GenerateNU/sac/backend/src/auth"
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/transactions"
+ "github.com/GenerateNU/sac/backend/src/types"
+ "github.com/GenerateNU/sac/backend/src/utilities"
+ "github.com/gofiber/fiber/v2"
+)
+
+func (m *MiddlewareService) ClubAuthorizeById(c *fiber.Ctx) error {
+ clubUUID, err := utilities.ValidateID(c.Params("id"))
+ if err != nil {
+ return errors.FailedToParseUUID.FiberError(c)
+ }
+
+ token, tokenErr := auth.ParseAccessToken(c.Cookies("access_token"))
+ if tokenErr != nil {
+ return errors.FailedToParseAccessToken.FiberError(c)
+ }
+
+ claims, ok := token.Claims.(*types.CustomClaims)
+ if !ok || !token.Valid {
+ return errors.FailedToValidateAccessToken.FiberError(c)
+ }
+
+ issuerUUID, issueErr := utilities.ValidateID(claims.Issuer)
+ if issueErr != nil {
+ return errors.FailedToParseAccessToken.FiberError(c)
+ }
+
+ // use club_id to get the list of admin for a certain club
+ clubAdmin, clubErr := transactions.GetAdminIDs(m.DB, *clubUUID)
+ if clubErr != nil {
+ return err
+ }
+
+ // check issuerID against the list of admin for the certain club
+ if slices.Contains(clubAdmin, *issuerUUID) {
+ return c.Next()
+ }
+
+ return errors.Unauthorized.FiberError(c)
+}
diff --git a/backend/src/middleware/middleware.go b/backend/src/middleware/middleware.go
new file mode 100644
index 000000000..97107b9bb
--- /dev/null
+++ b/backend/src/middleware/middleware.go
@@ -0,0 +1,27 @@
+package middleware
+
+import (
+ "github.com/GenerateNU/sac/backend/src/types"
+ "github.com/go-playground/validator/v10"
+ "github.com/gofiber/fiber/v2"
+ "gorm.io/gorm"
+)
+
+type MiddlewareInterface interface {
+ ClubAuthorizeById(c *fiber.Ctx) error
+ UserAuthorizeById(c *fiber.Ctx) error
+ Authenticate(c *fiber.Ctx) error
+ Authorize(requiredPermissions ...types.Permission) func(c *fiber.Ctx) error
+}
+
+type MiddlewareService struct {
+ DB *gorm.DB
+ Validate *validator.Validate
+}
+
+func NewMiddlewareService(db *gorm.DB, validate *validator.Validate) *MiddlewareService {
+ return &MiddlewareService{
+ DB: db,
+ Validate: validate,
+ }
+}
diff --git a/backend/src/middleware/user.go b/backend/src/middleware/user.go
new file mode 100644
index 000000000..38ee0e3e0
--- /dev/null
+++ b/backend/src/middleware/user.go
@@ -0,0 +1,37 @@
+package middleware
+
+import (
+ "github.com/GenerateNU/sac/backend/src/auth"
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/types"
+ "github.com/GenerateNU/sac/backend/src/utilities"
+ "github.com/gofiber/fiber/v2"
+)
+
+func (m *MiddlewareService) UserAuthorizeById(c *fiber.Ctx) error {
+ idAsUUID, err := utilities.ValidateID(c.Params("id"))
+ if err != nil {
+ return errors.FailedToParseUUID.FiberError(c)
+ }
+
+ token, tokenErr := auth.ParseAccessToken(c.Cookies("access_token"))
+ if tokenErr != nil {
+ return err
+ }
+
+ claims, ok := token.Claims.(*types.CustomClaims)
+ if !ok || !token.Valid {
+ return errors.FailedToValidateAccessToken.FiberError(c)
+ }
+
+ issuerIDAsUUID, err := utilities.ValidateID(claims.Issuer)
+ if err != nil {
+ return errors.FailedToParseUUID.FiberError(c)
+ }
+
+ if issuerIDAsUUID.String() == idAsUUID.String() {
+ return c.Next()
+ }
+
+ return errors.Unauthorized.FiberError(c)
+}
diff --git a/backend/src/migrations/data.sql b/backend/src/migrations/data.sql
index d5ef8e180..c53bc1918 100644
--- a/backend/src/migrations/data.sql
+++ b/backend/src/migrations/data.sql
@@ -1,8 +1,9 @@
-- BEGIN MOCK DATA TRANSACTION
BEGIN;
-INSERT INTO users (role, nuid, email, password_hash, first_name, last_name, college, year) VALUES ('super', '002183108', 'oduneye.d@northeastern.edu', '$argon2id$v=19$m=65536,t=3,p=2$zYyFSnLvC5Q482mzMJrTjg$WUhpXwulvfipyWg7asQyCRUqBEnjizDOoMP2/GvWQR8', 'David', 'Oduneye', 'KCCS', 3);
-INSERT INTO users (role, nuid, email, password_hash, first_name, last_name, college, year) VALUES ('super', '002172052', 'ladley.g@northeastern.edu', '$argon2id$v=19$m=65536,t=3,p=2$zYyFSnLvC5Q482mzMJrTjg$WUhpXwulvfipyWg7asQyCRUqBEnjizDOoMP2/GvWQR8', 'Garrett', 'Ladley', 'KCCS', 3);
-
+INSERT INTO users (id, role, nuid, email, password_hash, first_name, last_name, college, year) VALUES ('29cac84a-362c-4ffa-9f4c-2f76057b7902', 'super', '002183108', 'oduneye.d@northeastern.edu', '$argon2id$v=19$m=65536,t=3,p=2$zYyFSnLvC5Q482mzMJrTjg$WUhpXwulvfipyWg7asQyCRUqBEnjizDOoMP2/GvWQR8', 'David', 'Oduneye', 'KCCS', 3);
+INSERT INTO users (id, role, nuid, email, password_hash, first_name, last_name, college, year) VALUES ('4f4d9990-7d26-4229-911d-1aa61851c292', 'super', '002172052', 'ladley.g@northeastern.edu', '$argon2id$v=19$m=65536,t=3,p=2$zYyFSnLvC5Q482mzMJrTjg$WUhpXwulvfipyWg7asQyCRUqBEnjizDOoMP2/GvWQR8', 'Garrett', 'Ladley', 'KCCS', 3);
+INSERT INTO user_club_members (user_id, club_id, membership_type) VALUES ('29cac84a-362c-4ffa-9f4c-2f76057b7902', (SELECT id FROM clubs WHERE name = 'SAC'), 'admin');
+INSERT INTO user_club_members (user_id, club_id, membership_type) VALUES ('4f4d9990-7d26-4229-911d-1aa61851c292', (SELECT id FROM clubs WHERE name = 'SAC'), 'admin');
COMMIT;
-- END MOCK DATA TRANSACTION
diff --git a/backend/src/models/club.go b/backend/src/models/club.go
index 353a56f47..919d30fb6 100644
--- a/backend/src/models/club.go
+++ b/backend/src/models/club.go
@@ -40,7 +40,6 @@ type Club struct {
Parent *uuid.UUID `gorm:"foreignKey:Parent" json:"-" validate:"uuid4"`
Tag []Tag `gorm:"many2many:club_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
// User
- Admin []User `gorm:"many2many:user_club_admins;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"required"`
Member []User `gorm:"many2many:user_club_members;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"required"`
Follower []User `gorm:"many2many:user_club_followers;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
IntendedApplicant []User `gorm:"many2many:user_club_intended_applicants;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
@@ -76,3 +75,13 @@ type UpdateClubRequestBody struct {
ApplicationLink string `json:"application_link" validate:"omitempty,required,max=255,http_url"`
Logo string `json:"logo" validate:"omitempty,http_url,s3_url,max=255"` // S3 URL
}
+
+func (c *Club) AfterCreate(tx *gorm.DB) (err error) {
+ tx.Model(&c).Update("num_members", c.NumMembers+1)
+ return
+}
+
+func (c *Club) AfterDelete(tx *gorm.DB) (err error) {
+ tx.Model(&c).Update("num_members", c.NumMembers-1)
+ return
+}
diff --git a/backend/src/models/membership.go b/backend/src/models/membership.go
new file mode 100644
index 000000000..5ffc69d34
--- /dev/null
+++ b/backend/src/models/membership.go
@@ -0,0 +1,30 @@
+package models
+
+import "github.com/google/uuid"
+
+type MembershipType string
+
+const (
+ MembershipTypeMember MembershipType = "member"
+ MembershipTypeAdmin MembershipType = "admin"
+)
+
+type Tabler interface {
+ TableName() string
+}
+
+func (Membership) TableName() string {
+ return "user_club_members"
+}
+
+type Membership struct {
+ Model
+
+ UserID uuid.UUID `gorm:"type:uuid;not null" json:"user_id" validate:"required,uuid4"`
+ ClubID uuid.UUID `gorm:"type:uuid;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"`
+}
diff --git a/backend/src/models/user.go b/backend/src/models/user.go
index b5b945d63..46eee1311 100644
--- a/backend/src/models/user.go
+++ b/backend/src/models/user.go
@@ -5,9 +5,8 @@ import "github.com/google/uuid"
type UserRole string
const (
- Super UserRole = "super"
- ClubAdmin UserRole = "clubAdmin"
- Student UserRole = "student"
+ Super UserRole = "super"
+ Student UserRole = "student"
)
type College string
@@ -38,7 +37,7 @@ const (
type User struct {
Model
- Role UserRole `gorm:"type:varchar(255);" json:"user_role,omitempty" validate:"required,max=255"`
+ Role UserRole `gorm:"type:varchar(255);default:'student'" json:"role" validate:"required,oneof=super student"`
NUID string `gorm:"column:nuid;type:varchar(9);unique" json:"nuid" validate:"required,numeric,len=9"`
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"`
@@ -77,6 +76,11 @@ type UpdateUserRequestBody struct {
Year Year `json:"year" validate:"omitempty,min=1,max=6"`
}
+type LoginUserResponseBody struct {
+ Email string `json:"email" validate:"required,email"`
+ Password string `json:"password" validate:"min=8,max=255"`
+}
+
type CreateUserTagsBody struct {
Tags []uuid.UUID `json:"tags" validate:"required"`
}
diff --git a/backend/src/server/server.go b/backend/src/server/server.go
index 61aa4e805..dc5a956a1 100644
--- a/backend/src/server/server.go
+++ b/backend/src/server/server.go
@@ -1,10 +1,12 @@
package server
import (
+ "github.com/GenerateNU/sac/backend/src/config"
"github.com/GenerateNU/sac/backend/src/controllers"
+ "github.com/GenerateNU/sac/backend/src/middleware"
"github.com/GenerateNU/sac/backend/src/services"
+ "github.com/GenerateNU/sac/backend/src/types"
"github.com/GenerateNU/sac/backend/src/utilities"
- "github.com/go-playground/validator/v10"
"github.com/goccy/go-json"
"github.com/gofiber/fiber/v2"
@@ -23,21 +25,21 @@ import (
// @contact.email oduneye.d@northeastern.edu and ladley.g@northeastern.edu
// @host 127.0.0.1:8080
// @BasePath /
-func Init(db *gorm.DB) *fiber.App {
+func Init(db *gorm.DB, settings config.Settings) *fiber.App {
app := newFiberApp()
- validate := validator.New(validator.WithRequiredStructEnabled())
- // MARK: Custom validator tags can be registered here.
- utilities.RegisterCustomValidators(validate)
-
- utilityRoutes(app)
+ validate := utilities.RegisterCustomValidators()
+ middlewareService := middleware.NewMiddlewareService(db, validate)
apiv1 := app.Group("/api/v1")
+ apiv1.Use(middlewareService.Authenticate)
- userRoutes(apiv1, &services.UserService{DB: db, Validate: validate})
- clubRoutes(apiv1, &services.ClubService{DB: db, Validate: validate})
- categoryRouter := categoryRoutes(apiv1, &services.CategoryService{DB: db, Validate: validate})
- tagRoutes(categoryRouter, &services.TagService{DB: db, Validate: validate})
+ utilityRoutes(app)
+ authRoutes(apiv1, services.NewAuthService(db, validate), settings.Auth)
+ userRoutes(apiv1, services.NewUserService(db, validate), middlewareService)
+ clubRoutes(apiv1, services.NewClubService(db, validate), middlewareService)
+ categoryRouter := categoryRoutes(apiv1, services.NewCategoryService(db, validate))
+ tagRoutes(categoryRouter, services.NewTagService(db, validate))
return app
}
@@ -48,7 +50,10 @@ func newFiberApp() *fiber.App {
JSONDecoder: json.Unmarshal,
})
- app.Use(cors.New())
+ app.Use(cors.New(cors.Config{
+ AllowOrigins: "*",
+ AllowCredentials: true,
+ }))
app.Use(requestid.New())
app.Use(logger.New(logger.Config{
Format: "[${time}] ${ip}:${port} ${pid} ${locals:requestid} ${status} - ${latency} ${method} ${path}\n",
@@ -64,12 +69,24 @@ func utilityRoutes(router fiber.Router) {
})
}
-func userRoutes(router fiber.Router, userService services.UserServiceInterface) {
+func userRoutes(router fiber.Router, userService services.UserServiceInterface, middlewareService middleware.MiddlewareInterface) {
userController := controllers.NewUserController(userService)
+ // api/v1/users/*
users := router.Group("/users")
-
users.Post("/", userController.CreateUser)
+ users.Get("/", middlewareService.Authorize(types.UserReadAll), userController.GetUsers)
+
+ // api/v1/users/:id/*
+ usersID := users.Group("/:id")
+ usersID.Use(middlewareService.UserAuthorizeById)
+
+ usersID.Get("/", middlewareService.Authorize(types.UserRead), userController.GetUser)
+ usersID.Patch("/", middlewareService.Authorize(types.UserWrite), userController.UpdateUser)
+ usersID.Delete("/", middlewareService.Authorize(types.UserDelete), userController.DeleteUser)
+
+ usersID.Post("/tags", userController.CreateUserTags)
+ usersID.Get("/tags", userController.GetUserTags)
users.Get("/", userController.GetUsers)
users.Get("/:id", userController.GetUser)
users.Patch("/:id", userController.UpdateUser)
@@ -81,16 +98,32 @@ func userRoutes(router fiber.Router, userService services.UserServiceInterface)
userTags.Get("/", userController.GetUserTags)
}
-func clubRoutes(router fiber.Router, clubService services.ClubServiceInterface) {
+func clubRoutes(router fiber.Router, clubService services.ClubServiceInterface, middlewareService middleware.MiddlewareInterface) {
clubController := controllers.NewClubController(clubService)
clubs := router.Group("/clubs")
- clubs.Get("/", clubController.GetAllClubs)
+ clubs.Get("/", middlewareService.Authorize(types.ClubReadAll), clubController.GetAllClubs)
clubs.Post("/", clubController.CreateClub)
- clubs.Get("/:id", clubController.GetClub)
- clubs.Patch("/:id", clubController.UpdateClub)
- clubs.Delete("/:id", clubController.DeleteClub)
+
+ // api/v1/clubs/:id/*
+ clubsID := clubs.Group("/:id")
+ clubsID.Use(middlewareService.ClubAuthorizeById)
+
+ clubsID.Get("/", clubController.GetClub)
+ clubsID.Patch("/", middlewareService.Authorize(types.ClubWrite), clubController.UpdateClub)
+ clubsID.Delete("/", middlewareService.Authorize(types.ClubDelete), clubController.DeleteClub)
+}
+
+func authRoutes(router fiber.Router, authService services.AuthServiceInterface, authSettings config.AuthSettings) {
+ authController := controllers.NewAuthController(authService, authSettings)
+
+ // api/v1/auth/*
+ auth := router.Group("/auth")
+ auth.Post("/login", authController.Login)
+ auth.Get("/logout", authController.Logout)
+ auth.Get("/refresh", authController.Refresh)
+ auth.Get("/me", authController.Me)
}
func categoryRoutes(router fiber.Router, categoryService services.CategoryServiceInterface) fiber.Router {
diff --git a/backend/src/services/auth.go b/backend/src/services/auth.go
new file mode 100644
index 000000000..4c87dd7e4
--- /dev/null
+++ b/backend/src/services/auth.go
@@ -0,0 +1,81 @@
+package services
+
+import (
+ "github.com/GenerateNU/sac/backend/src/auth"
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/GenerateNU/sac/backend/src/transactions"
+ "github.com/GenerateNU/sac/backend/src/utilities"
+ "github.com/go-playground/validator/v10"
+ "gorm.io/gorm"
+)
+
+type AuthServiceInterface interface {
+ GetRole(id string) (*models.UserRole, *errors.Error)
+ Me(id string) (*models.User, *errors.Error)
+ Login(userBody models.LoginUserResponseBody) (*models.User, *errors.Error)
+}
+
+type AuthService struct {
+ DB *gorm.DB
+ Validate *validator.Validate
+}
+
+func NewAuthService(db *gorm.DB, validate *validator.Validate) *AuthService {
+ return &AuthService{
+ DB: db,
+ Validate: validate,
+ }
+}
+
+func (a *AuthService) Me(id string) (*models.User, *errors.Error) {
+ idAsUint, idErr := utilities.ValidateID(id)
+ if idErr != nil {
+ return nil, idErr
+ }
+
+ user, err := transactions.GetUser(a.DB, *idAsUint)
+ if err != nil {
+ return nil, &errors.UserNotFound
+ }
+
+ return user, nil
+}
+
+func (a *AuthService) Login(userBody models.LoginUserResponseBody) (*models.User, *errors.Error) {
+ if err := a.Validate.Struct(userBody); err != nil {
+ return nil, &errors.FailedToValidateUser
+ }
+
+ user, err := transactions.GetUserByEmail(a.DB, userBody.Email)
+ if err != nil {
+ return nil, &errors.UserNotFound
+ }
+
+ correct, passwordErr := auth.ComparePasswordAndHash(userBody.Password, user.PasswordHash)
+ if passwordErr != nil {
+ return nil, &errors.FailedToValidateUser
+ }
+
+ if !correct {
+ return nil, &errors.FailedToValidateUser
+ }
+
+ return user, nil
+}
+
+func (a *AuthService) GetRole(id string) (*models.UserRole, *errors.Error) {
+ idAsUint, idErr := utilities.ValidateID(id)
+ if idErr != nil {
+ return nil, idErr
+ }
+
+ user, err := transactions.GetUser(a.DB, *idAsUint)
+ if err != nil {
+ return nil, &errors.UserNotFound
+ }
+
+ role := models.UserRole(user.Role)
+
+ return &role, nil
+}
diff --git a/backend/src/services/category.go b/backend/src/services/category.go
index 75eaf9ab2..ead4df2c0 100644
--- a/backend/src/services/category.go
+++ b/backend/src/services/category.go
@@ -27,6 +27,10 @@ type CategoryService struct {
Validate *validator.Validate
}
+func NewCategoryService(db *gorm.DB, validate *validator.Validate) *CategoryService {
+ return &CategoryService{DB: db, Validate: validate}
+}
+
func (c *CategoryService) CreateCategory(categoryBody models.CategoryRequestBody) (*models.Category, *errors.Error) {
if err := c.Validate.Struct(categoryBody); err != nil {
return nil, &errors.FailedToValidateCategory
diff --git a/backend/src/services/club.go b/backend/src/services/club.go
index f7f7ff7d8..c05610c1f 100644
--- a/backend/src/services/club.go
+++ b/backend/src/services/club.go
@@ -23,6 +23,10 @@ type ClubService struct {
Validate *validator.Validate
}
+func NewClubService(db *gorm.DB, validate *validator.Validate) *ClubService {
+ return &ClubService{DB: db, Validate: validate}
+}
+
func (c *ClubService) GetClubs(limit string, page string) ([]models.Club, *errors.Error) {
limitAsInt, err := utilities.ValidateNonNegative(limit)
if err != nil {
diff --git a/backend/src/services/tag.go b/backend/src/services/tag.go
index 5d4c99bd2..8fda17d26 100644
--- a/backend/src/services/tag.go
+++ b/backend/src/services/tag.go
@@ -21,6 +21,10 @@ type TagService struct {
Validate *validator.Validate
}
+func NewTagService(db *gorm.DB, validate *validator.Validate) *TagService {
+ return &TagService{DB: db, Validate: validate}
+}
+
func (t *TagService) CreateTag(categoryID string, tagBody models.TagRequestBody) (*models.Tag, *errors.Error) {
categoryIDAsUUID, idErr := utilities.ValidateID(categoryID)
diff --git a/backend/src/services/user.go b/backend/src/services/user.go
index b11cc5821..4a985332b 100644
--- a/backend/src/services/user.go
+++ b/backend/src/services/user.go
@@ -28,6 +28,10 @@ type UserService struct {
Validate *validator.Validate
}
+func NewUserService(db *gorm.DB, validate *validator.Validate) *UserService {
+ return &UserService{DB: db, Validate: validate}
+}
+
func (u *UserService) CreateUser(userBody models.CreateUserRequestBody) (*models.User, *errors.Error) {
if err := u.Validate.Struct(userBody); err != nil {
return nil, &errors.FailedToValidateUser
diff --git a/backend/src/transactions/club.go b/backend/src/transactions/club.go
index 18413bd26..a7fe4495c 100644
--- a/backend/src/transactions/club.go
+++ b/backend/src/transactions/club.go
@@ -10,6 +10,21 @@ import (
"gorm.io/gorm"
)
+func GetAdminIDs(db *gorm.DB, clubID uuid.UUID) ([]uuid.UUID, *errors.Error) {
+ var adminIDs []models.Membership
+
+ if err := db.Where("club_id = ? AND membership_type = ?", clubID, models.MembershipTypeAdmin).Find(&adminIDs).Error; err != nil {
+ return nil, &errors.FailedtoGetAdminIDs
+ }
+
+ var adminUUIDs []uuid.UUID
+ for _, adminID := range adminIDs {
+ adminUUIDs = append(adminUUIDs, adminID.ClubID)
+ }
+
+ return adminUUIDs, nil
+}
+
func GetClubs(db *gorm.DB, limit int, offset int) ([]models.Club, *errors.Error) {
var clubs []models.Club
result := db.Limit(limit).Offset(offset).Find(&clubs)
diff --git a/backend/src/transactions/user.go b/backend/src/transactions/user.go
index a55adf1b7..73d9f07c2 100644
--- a/backend/src/transactions/user.go
+++ b/backend/src/transactions/user.go
@@ -22,6 +22,16 @@ func CreateUser(db *gorm.DB, user *models.User) (*models.User, *errors.Error) {
return user, nil
}
+func GetUserByEmail(db *gorm.DB, email string) (*models.User, *errors.Error) {
+ var user models.User
+
+ if err := db.Where("email = ?", email).First(&user).Error; err != nil {
+ return nil, &errors.UserNotFound
+ }
+
+ return &user, nil
+}
+
func GetUsers(db *gorm.DB, limit int, offset int) ([]models.User, *errors.Error) {
var users []models.User
diff --git a/backend/src/types/custom_claims.go b/backend/src/types/custom_claims.go
new file mode 100644
index 000000000..b53da552e
--- /dev/null
+++ b/backend/src/types/custom_claims.go
@@ -0,0 +1,8 @@
+package types
+
+import "github.com/golang-jwt/jwt"
+
+type CustomClaims struct {
+ jwt.StandardClaims
+ Role string `json:"role"`
+}
diff --git a/backend/src/types/permissions.go b/backend/src/types/permissions.go
new file mode 100644
index 000000000..f99fff06f
--- /dev/null
+++ b/backend/src/types/permissions.go
@@ -0,0 +1,91 @@
+package types
+
+import "github.com/GenerateNU/sac/backend/src/models"
+
+type Permission string
+
+const (
+ UserReadAll Permission = "user:readAll"
+ UserRead Permission = "user:read"
+ UserWrite Permission = "user:write"
+ UserDelete Permission = "user:delete"
+
+ TagReadAll Permission = "tag:readAll"
+ TagRead Permission = "tag:read"
+ TagWrite Permission = "tag:write"
+ TagCreate Permission = "tag:create"
+ TagDelete Permission = "tag:delete"
+
+ ClubReadAll Permission = "club:readAll"
+ ClubRead Permission = "club:read"
+ ClubWrite Permission = "club:write"
+ ClubCreate Permission = "club:create"
+ ClubDelete Permission = "club:delete"
+
+ PointOfContactReadAll Permission = "pointOfContact:readAll"
+ PointOfContactRead Permission = "pointOfContact:read"
+ PointOfContactCreate Permission = "pointOfContact:create"
+ PointOfContactWrite Permission = "pointOfContact:write"
+ PointOfContactDelete Permission = "pointOfContact:delete"
+
+ CommentReadAll Permission = "comment:readAll"
+ CommentRead Permission = "comment:read"
+ CommentCreate Permission = "comment:create"
+ CommentWrite Permission = "comment:write"
+ CommentDelete Permission = "comment:delete"
+
+ EventReadAll Permission = "event:readAll"
+ EventRead Permission = "event:read"
+ EventWrite Permission = "event:write"
+ EventCreate Permission = "event:create"
+ EventDelete Permission = "event:delete"
+
+ ContactReadAll Permission = "contact:readAll"
+ ContactRead Permission = "contact:read"
+ ContactWrite Permission = "contact:write"
+ ContactCreate Permission = "contact:create"
+ ContactDelete Permission = "contact:delete"
+
+ CategoryReadAll Permission = "category:readAll"
+ CategoryRead Permission = "category:read"
+ CategoryWrite Permission = "category:write"
+ CategoryCreate Permission = "category:create"
+ CategoryDelete Permission = "category:delete"
+
+ NotificationReadAll Permission = "notification:readAll"
+ NotificationRead Permission = "notification:read"
+ NotificationWrite Permission = "notification:write"
+ NotificationCreate Permission = "notification:create"
+ NotificationDelete Permission = "notification:delete"
+)
+
+var rolePermissions = map[models.UserRole][]Permission{
+ models.Super: {
+ UserRead, UserWrite, UserDelete,
+ TagRead, TagCreate, TagWrite, TagDelete,
+ ClubRead, ClubCreate, ClubWrite, ClubDelete,
+ PointOfContactRead, PointOfContactCreate, PointOfContactWrite, PointOfContactDelete,
+ CommentRead, CommentCreate, CommentWrite, CommentDelete,
+ EventRead, EventCreate, EventWrite, EventDelete,
+ ContactRead, ContactCreate, ContactWrite, ContactDelete,
+ CategoryRead, CategoryCreate, CategoryWrite, CategoryDelete,
+ NotificationRead, NotificationCreate, NotificationWrite, NotificationDelete,
+ UserReadAll, TagReadAll, ClubReadAll, PointOfContactReadAll, CommentReadAll, EventReadAll, ContactReadAll, CategoryReadAll, NotificationReadAll,
+ },
+ models.Student: {
+ UserRead,
+ TagRead,
+ ClubRead,
+ PointOfContactRead,
+ CommentRead,
+ EventRead,
+ ContactRead,
+ CategoryRead,
+ NotificationRead,
+ },
+}
+
+// Returns the permissions for a given role
+func GetPermissions(role models.UserRole) []Permission {
+ return rolePermissions[role]
+}
diff --git a/backend/src/utilities/response.go b/backend/src/utilities/response.go
new file mode 100644
index 000000000..790993135
--- /dev/null
+++ b/backend/src/utilities/response.go
@@ -0,0 +1,7 @@
+package utilities
+
+import "github.com/gofiber/fiber/v2"
+
+func FiberMessage(c *fiber.Ctx, statusCode int, response string) error {
+ return c.Status(statusCode).JSON(fiber.Map{"message": response})
+}
diff --git a/backend/src/utilities/validator.go b/backend/src/utilities/validator.go
index 357051499..78c2e53ef 100644
--- a/backend/src/utilities/validator.go
+++ b/backend/src/utilities/validator.go
@@ -8,12 +8,14 @@ import (
"github.com/GenerateNU/sac/backend/src/models"
"github.com/google/uuid"
+ "github.com/mcnijman/go-emailaddress"
"github.com/go-playground/validator/v10"
- "github.com/mcnijman/go-emailaddress"
)
-func RegisterCustomValidators(validate *validator.Validate) {
+func RegisterCustomValidators() *validator.Validate {
+ validate := validator.New(validator.WithRequiredStructEnabled())
+
validate.RegisterValidation("neu_email", validateEmail)
validate.RegisterValidation("password", validatePassword)
validate.RegisterValidation("mongo_url", validateMongoURL)
@@ -21,6 +23,8 @@ func RegisterCustomValidators(validate *validator.Validate) {
validate.RegisterValidation("contact_pointer", func(fl validator.FieldLevel) bool {
return validateContactPointer(validate, fl)
})
+
+ return validate
}
func validateEmail(fl validator.FieldLevel) bool {
diff --git a/backend/tests/api/helpers.go b/backend/tests/api/helpers.go
index dc728ac53..c08fc95d0 100644
--- a/backend/tests/api/helpers.go
+++ b/backend/tests/api/helpers.go
@@ -63,7 +63,7 @@ func spawnApp() (TestApp, error) {
}
return TestApp{
- App: server.Init(connectionWithDB),
+ App: server.Init(connectionWithDB, configuration),
Address: fmt.Sprintf("http://%s", listener.Addr().String()),
Conn: connectionWithDB,
Settings: configuration,
diff --git a/backend/tests/auth_test.go b/backend/tests/auth_test.go
new file mode 100644
index 000000000..d7d49bb40
--- /dev/null
+++ b/backend/tests/auth_test.go
@@ -0,0 +1,148 @@
+package tests
+
+import (
+ "testing"
+
+ "github.com/GenerateNU/sac/backend/src/auth"
+ "github.com/GenerateNU/sac/backend/src/config"
+
+ "github.com/golang-jwt/jwt"
+ "github.com/huandu/go-assert"
+)
+
+func AuthSettings() config.AuthSettings {
+ return config.AuthSettings{
+ AccessToken: "g(r|##*?>\\Qp}h37e+,T2",
+ AccessTokenExpiry: 60,
+ RefreshToken: "amk*2!gG}1i\"8D9RwJS$p",
+ RefreshTokenExpiry: 30,
+ }
+}
+
+func TestCreateTokenPairSuccess(t *testing.T) {
+ assert := assert.New(t)
+
+ id := "user123"
+ role := "admin"
+
+ accessToken, refreshToken, err := auth.CreateTokenPair(id, role, AuthSettings())
+
+ assert.Assert(err == nil)
+
+ assert.Assert(accessToken != nil)
+ assert.Assert(refreshToken != nil)
+}
+
+func TestCreateTokenPairFailure(t *testing.T) {
+ assert := assert.New(t)
+
+ id := "user123"
+ role := ""
+
+ accessToken, refreshToken, err := auth.CreateTokenPair(id, role, AuthSettings())
+
+ assert.Assert(err != nil)
+
+ assert.Assert(accessToken == nil)
+ assert.Assert(refreshToken == nil)
+}
+
+func TestCreateAccessTokenSuccess(t *testing.T) {
+ assert := assert.New(t)
+
+ id := "user123"
+ role := "admin"
+
+ authSettings := AuthSettings()
+
+ accessToken, err := auth.CreateAccessToken(id, role, authSettings.AccessTokenExpiry, authSettings.AccessToken)
+
+ assert.Assert(err == nil)
+
+ assert.Assert(accessToken != nil)
+}
+
+func TestCreateAccessTokenFailure(t *testing.T) {
+ assert := assert.New(t)
+
+ id := "user123"
+ role := ""
+
+ authSettings := AuthSettings()
+
+ accessToken, err := auth.CreateAccessToken(id, role, authSettings.AccessTokenExpiry, authSettings.AccessToken)
+
+ assert.Assert(err != nil)
+
+ assert.Assert(accessToken == nil)
+}
+
+func TestCreateRefreshTokenSuccess(t *testing.T) {
+ assert := assert.New(t)
+
+ id := "user123"
+
+ authSettings := AuthSettings()
+
+ refreshToken, err := auth.CreateRefreshToken(id, authSettings.RefreshTokenExpiry, authSettings.RefreshToken)
+
+ assert.Assert(err == nil)
+
+ assert.Assert(refreshToken != nil)
+}
+
+func TestCreateRefreshTokenFailure(t *testing.T) {
+ assert := assert.New(t)
+
+ id := ""
+
+ authSettings := AuthSettings()
+
+ refreshToken, err := auth.CreateRefreshToken(id, authSettings.RefreshTokenExpiry, authSettings.RefreshToken)
+
+ assert.Assert(err != nil)
+
+ assert.Assert(refreshToken == nil)
+}
+
+func TestSignTokenSuccess(t *testing.T) {
+ assert := assert.New(t)
+
+ token := jwt.New(jwt.SigningMethodHS256)
+
+ assert.Assert(token != nil)
+
+ token.Claims = jwt.MapClaims{
+ "sub": "user123",
+ "exp": 1234567890,
+ "iat": 1234567890,
+ "iss": "sac",
+ }
+
+ signedToken, err := auth.SignToken(token, "secret")
+
+ assert.Assert(err == nil)
+
+ assert.Assert(signedToken != nil)
+}
+
+func TestSignTokenFailure(t *testing.T) {
+ assert := assert.New(t)
+
+ token := jwt.New(jwt.SigningMethodHS256)
+
+ assert.Assert(token != nil)
+
+ token.Claims = jwt.MapClaims{
+ "sub": "user123",
+ "exp": 1234567890,
+ "iat": 1234567890,
+ "iss": "sac",
+ }
+
+ signedToken, err := auth.SignToken(token, "")
+
+ assert.Assert(err != nil)
+
+ assert.Assert(signedToken == nil)
+}
diff --git a/cli/commands/insert.go b/cli/commands/insert.go
index 9f50d4171..376468ad3 100644
--- a/cli/commands/insert.go
+++ b/cli/commands/insert.go
@@ -1,10 +1,13 @@
package commands
import (
+ "bytes"
"database/sql"
+ "errors"
"fmt"
"os/exec"
"strconv"
+ "strings"
_ "github.com/lib/pq"
"github.com/urfave/cli/v2"
@@ -64,11 +67,18 @@ func InsertDB() error {
insertCmd := exec.Command("psql", "-h", CONFIG.Database.Host, "-p", strconv.Itoa(int(CONFIG.Database.Port)), "-U", CONFIG.Database.Username, "-d", CONFIG.Database.DatabaseName, "-a", "-f", MIGRATION_FILE)
+ var output bytes.Buffer
+ insertCmd.Stdout = &output
+ insertCmd.Stderr = &output
+
if err := insertCmd.Run(); err != nil {
- fmt.Println(insertCmd.String())
return fmt.Errorf("error inserting data: %w", err)
}
+ if strings.Contains(output.String(), "ROLLBACK") {
+ return errors.New("insertion failed, rolling back")
+ }
+
fmt.Println("Data inserted successfully.")
return nil
diff --git a/config/local.yml b/config/local.yml
index d500bb335..57da31f0f 100644
--- a/config/local.yml
+++ b/config/local.yml
@@ -11,3 +11,8 @@ database:
requiressl: false
superuser:
password: password
+auth:
+ accesstoken: g(r|##*?>\Qp}h37e+,T2
+ accesstokenexpiry: 60 # in minutes
+ refreshtoken: amk*2!gG}1i"8D9RwJS$p
+ refreshtokenexpiry: 30 # in days
diff --git a/go.work.sum b/go.work.sum
index 71c01eeaa..9dfa3c6d2 100644
--- a/go.work.sum
+++ b/go.work.sum
@@ -1,69 +1,16 @@
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
-github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
-github.com/mcnijman/go-emailaddress v1.1.1 h1:AGhgVDG3tCDaL0/Vc6erlPQjDuDN3dAT7rRdgFtetr0=
-github.com/mcnijman/go-emailaddress v1.1.1/go.mod h1:5whZrhS8Xp5LxO8zOD35BC+b76kROtsh+dPomeRt/II=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
-golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
-golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
-github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
-github.com/sagikazarmark/crypt v0.17.0 h1:ZA/7pXyjkHoK4bW4mIdnCLvL8hd+Nrbiw7Dqk7D4qUk=
-github.com/sagikazarmark/crypt v0.17.0/go.mod h1:SMtHTvdmsZMuY/bpZoqokSoChIrcJ/epOxZN58PbZDg=
-github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
-github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
-github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
-github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
-go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k=
-go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI=
-go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0=
-go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U=
-go.etcd.io/etcd/client/v2 v2.305.10 h1:MrmRktzv/XF8CvtQt+P6wLUlURaNpSDJHFZhe//2QE4=
-go.etcd.io/etcd/client/v2 v2.305.10/go.mod h1:m3CKZi69HzilhVqtPDcjhSGp+kA1OmbNn0qamH80xjA=
-go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao=
-go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc=
-go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
-go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
-go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
-go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
-golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
-golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
-golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
-golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
-golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
-golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
-golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
-golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
-golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-google.golang.org/api v0.153.0 h1:N1AwGhielyKFaUqH07/ZSIQR3uNPcV7NVw0vj+j4iR4=
-google.golang.org/api v0.153.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY=
-google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
-google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
-google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
-google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
-google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
-google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
-google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
From 6e257486d7215709c31cdb84aa1fa460d50556c0 Mon Sep 17 00:00:00 2001
From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
Date: Fri, 2 Feb 2024 17:32:08 -0500
Subject: [PATCH 09/33] Nested Route Refactor (#140)
---
backend/src/controllers/category_tag.go | 38 +++
backend/src/controllers/tag.go | 8 +-
backend/src/controllers/user.go | 22 --
backend/src/controllers/user_tag.go | 38 +++
backend/src/errors/tag.go | 4 +
backend/src/models/tag.go | 3 +-
backend/src/server/server.go | 33 ++-
backend/src/services/category_tag.go | 61 +++++
backend/src/services/tag.go | 48 +---
backend/src/services/user.go | 32 ---
backend/src/services/user_tag.go | 54 ++++
backend/src/transactions/category_tag.go | 34 +++
backend/src/transactions/tag.go | 8 +-
backend/src/transactions/user.go | 27 --
backend/src/transactions/user_tag.go | 35 +++
backend/tests/api/category_tag_test.go | 182 +++++++++++++
backend/tests/api/tag_test.go | 225 +++++-----------
backend/tests/api/user_tag_test.go | 322 +++++++++++++++++++++++
backend/tests/api/user_test.go | 306 ---------------------
19 files changed, 871 insertions(+), 609 deletions(-)
create mode 100644 backend/src/controllers/category_tag.go
create mode 100644 backend/src/controllers/user_tag.go
create mode 100644 backend/src/services/category_tag.go
create mode 100644 backend/src/services/user_tag.go
create mode 100644 backend/src/transactions/category_tag.go
create mode 100644 backend/src/transactions/user_tag.go
create mode 100644 backend/tests/api/category_tag_test.go
create mode 100644 backend/tests/api/user_tag_test.go
diff --git a/backend/src/controllers/category_tag.go b/backend/src/controllers/category_tag.go
new file mode 100644
index 000000000..0c3fa7a27
--- /dev/null
+++ b/backend/src/controllers/category_tag.go
@@ -0,0 +1,38 @@
+package controllers
+
+import (
+ "strconv"
+
+ "github.com/GenerateNU/sac/backend/src/services"
+
+ "github.com/gofiber/fiber/v2"
+)
+
+type CategoryTagController struct {
+ categoryTagService services.CategoryTagServiceInterface
+}
+
+func NewCategoryTagController(categoryTagService services.CategoryTagServiceInterface) *CategoryTagController {
+ return &CategoryTagController{categoryTagService: categoryTagService}
+}
+
+func (t *CategoryTagController) GetTagsByCategory(c *fiber.Ctx) error {
+ defaultLimit := 10
+ defaultPage := 1
+
+ tags, err := t.categoryTagService.GetTagsByCategory(c.Params("categoryID"), c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage)))
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ return c.Status(fiber.StatusOK).JSON(&tags)
+}
+
+func (t *CategoryTagController) GetTagByCategory(c *fiber.Ctx) error {
+ tag, err := t.categoryTagService.GetTagByCategory(c.Params("categoryID"), c.Params("tagID"))
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ return c.Status(fiber.StatusOK).JSON(&tag)
+}
diff --git a/backend/src/controllers/tag.go b/backend/src/controllers/tag.go
index 78ca83d77..9dccea98c 100644
--- a/backend/src/controllers/tag.go
+++ b/backend/src/controllers/tag.go
@@ -36,7 +36,7 @@ func (t *TagController) CreateTag(c *fiber.Ctx) error {
return errors.FailedToParseRequestBody.FiberError(c)
}
- dbTag, err := t.tagService.CreateTag(c.Params("categoryID"), tagBody)
+ dbTag, err := t.tagService.CreateTag(tagBody)
if err != nil {
return err.FiberError(c)
}
@@ -58,7 +58,7 @@ func (t *TagController) CreateTag(c *fiber.Ctx) error {
// @Failure 500 {string} string "failed to retrieve tag"
// @Router /api/v1/tags/{id} [get]
func (t *TagController) GetTag(c *fiber.Ctx) error {
- tag, err := t.tagService.GetTag(c.Params("categoryID"), c.Params("tagID"))
+ tag, err := t.tagService.GetTag(c.Params("tagID"))
if err != nil {
return err.FiberError(c)
}
@@ -89,7 +89,7 @@ func (t *TagController) UpdateTag(c *fiber.Ctx) error {
return errors.FailedToParseRequestBody.FiberError(c)
}
- tag, err := t.tagService.UpdateTag(c.Params("categoryID"), c.Params("tagID"), tagBody)
+ tag, err := t.tagService.UpdateTag(c.Params("tagID"), tagBody)
if err != nil {
return err.FiberError(c)
}
@@ -110,7 +110,7 @@ func (t *TagController) UpdateTag(c *fiber.Ctx) error {
// @Failure 500 {string} string "failed to delete tag"
// @Router /api/v1/tags/{id} [delete]
func (t *TagController) DeleteTag(c *fiber.Ctx) error {
- err := t.tagService.DeleteTag(c.Params("categoryID"), c.Params("tagID"))
+ err := t.tagService.DeleteTag(c.Params("tagID"))
if err != nil {
return err.FiberError(c)
}
diff --git a/backend/src/controllers/user.go b/backend/src/controllers/user.go
index 5b4bcfe94..1b509bf3d 100644
--- a/backend/src/controllers/user.go
+++ b/backend/src/controllers/user.go
@@ -137,25 +137,3 @@ func (u *UserController) DeleteUser(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent)
}
-
-func (u *UserController) GetUserTags(c *fiber.Ctx) error {
- tags, err := u.userService.GetUserTags(c.Params("uid"))
- if err != nil {
- return err.FiberError(c)
- }
- return c.Status(fiber.StatusOK).JSON(&tags)
-}
-
-func (u *UserController) CreateUserTags(c *fiber.Ctx) error {
- var requestBody models.CreateUserTagsBody
- if err := c.BodyParser(&requestBody); err != nil {
- return errors.FailedToParseRequestBody.FiberError(c)
- }
-
- tags, err := u.userService.CreateUserTags(c.Params("uid"), requestBody)
- if err != nil {
- return err.FiberError(c)
- }
-
- return c.Status(fiber.StatusCreated).JSON(&tags)
-}
diff --git a/backend/src/controllers/user_tag.go b/backend/src/controllers/user_tag.go
new file mode 100644
index 000000000..eb1f7bd0b
--- /dev/null
+++ b/backend/src/controllers/user_tag.go
@@ -0,0 +1,38 @@
+package controllers
+
+import (
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+type UserTagController struct {
+ userTagService services.UserTagServiceInterface
+}
+
+func NewUserTagController(userTagService services.UserTagServiceInterface) *UserTagController {
+ return &UserTagController{userTagService: userTagService}
+}
+
+func (u *UserTagController) GetUserTags(c *fiber.Ctx) error {
+ tags, err := u.userTagService.GetUserTags(c.Params("userID"))
+ if err != nil {
+ return err.FiberError(c)
+ }
+ return c.Status(fiber.StatusOK).JSON(&tags)
+}
+
+func (u *UserTagController) CreateUserTags(c *fiber.Ctx) error {
+ var requestBody models.CreateUserTagsBody
+ if err := c.BodyParser(&requestBody); err != nil {
+ return errors.FailedToParseRequestBody.FiberError(c)
+ }
+
+ tags, err := u.userTagService.CreateUserTags(c.Params("userID"), requestBody)
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ return c.Status(fiber.StatusCreated).JSON(&tags)
+}
diff --git a/backend/src/errors/tag.go b/backend/src/errors/tag.go
index d872d8105..9e67a0a7c 100644
--- a/backend/src/errors/tag.go
+++ b/backend/src/errors/tag.go
@@ -11,6 +11,10 @@ var (
StatusCode: fiber.StatusInternalServerError,
Message: "failed to create tag",
}
+ FailedToGetTags = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "failed to get tags",
+ }
FailedToGetTag = Error{
StatusCode: fiber.StatusInternalServerError,
Message: "failed to get tag",
diff --git a/backend/src/models/tag.go b/backend/src/models/tag.go
index 15aac5b04..36c71381a 100644
--- a/backend/src/models/tag.go
+++ b/backend/src/models/tag.go
@@ -15,5 +15,6 @@ type Tag struct {
}
type TagRequestBody struct {
- Name string `json:"name" validate:"required,max=255"`
+ Name string `json:"name" validate:"required,max=255"`
+ CategoryID uuid.UUID `json:"category_id" validate:"required,uuid4"`
}
diff --git a/backend/src/server/server.go b/backend/src/server/server.go
index dc5a956a1..3b892d65f 100644
--- a/backend/src/server/server.go
+++ b/backend/src/server/server.go
@@ -36,10 +36,12 @@ func Init(db *gorm.DB, settings config.Settings) *fiber.App {
utilityRoutes(app)
authRoutes(apiv1, services.NewAuthService(db, validate), settings.Auth)
- userRoutes(apiv1, services.NewUserService(db, validate), middlewareService)
+ userRouter := userRoutes(apiv1, services.NewUserService(db, validate), middlewareService)
+ userTagRouter(userRouter, services.NewUserTagService(db, validate))
clubRoutes(apiv1, services.NewClubService(db, validate), middlewareService)
categoryRouter := categoryRoutes(apiv1, services.NewCategoryService(db, validate))
- tagRoutes(categoryRouter, services.NewTagService(db, validate))
+ tagRoutes(apiv1, services.NewTagService(db, validate))
+ categoryTagRoutes(categoryRouter, services.NewCategoryTagService(db, validate))
return app
}
@@ -69,7 +71,7 @@ func utilityRoutes(router fiber.Router) {
})
}
-func userRoutes(router fiber.Router, userService services.UserServiceInterface, middlewareService middleware.MiddlewareInterface) {
+func userRoutes(router fiber.Router, userService services.UserServiceInterface, middlewareService middleware.MiddlewareInterface) fiber.Router {
userController := controllers.NewUserController(userService)
// api/v1/users/*
@@ -85,17 +87,21 @@ func userRoutes(router fiber.Router, userService services.UserServiceInterface,
usersID.Patch("/", middlewareService.Authorize(types.UserWrite), userController.UpdateUser)
usersID.Delete("/", middlewareService.Authorize(types.UserDelete), userController.DeleteUser)
- usersID.Post("/tags", userController.CreateUserTags)
- usersID.Get("/tags", userController.GetUserTags)
users.Get("/", userController.GetUsers)
users.Get("/:id", userController.GetUser)
users.Patch("/:id", userController.UpdateUser)
users.Delete("/:id", userController.DeleteUser)
- userTags := users.Group("/:uid/tags")
+ return users
+}
+
+func userTagRouter(router fiber.Router, userTagService services.UserTagServiceInterface) {
+ userTagController := controllers.NewUserTagController(userTagService)
- userTags.Post("/", userController.CreateUserTags)
- userTags.Get("/", userController.GetUserTags)
+ userTags := router.Group("/:userID/tags")
+
+ userTags.Post("/", userTagController.CreateUserTags)
+ userTags.Get("/", userTagController.GetUserTags)
}
func clubRoutes(router fiber.Router, clubService services.ClubServiceInterface, middlewareService middleware.MiddlewareInterface) {
@@ -143,10 +149,19 @@ func categoryRoutes(router fiber.Router, categoryService services.CategoryServic
func tagRoutes(router fiber.Router, tagService services.TagServiceInterface) {
tagController := controllers.NewTagController(tagService)
- tags := router.Group("/:categoryID/tags")
+ tags := router.Group("/tags")
tags.Get("/:tagID", tagController.GetTag)
tags.Post("/", tagController.CreateTag)
tags.Patch("/:tagID", tagController.UpdateTag)
tags.Delete("/:tagID", tagController.DeleteTag)
}
+
+func categoryTagRoutes(router fiber.Router, categoryTagService services.CategoryTagServiceInterface) {
+ categoryTagController := controllers.NewCategoryTagController(categoryTagService)
+
+ categoryTags := router.Group("/:categoryID/tags")
+
+ categoryTags.Get("/", categoryTagController.GetTagsByCategory)
+ categoryTags.Get("/:tagID", categoryTagController.GetTagByCategory)
+}
diff --git a/backend/src/services/category_tag.go b/backend/src/services/category_tag.go
new file mode 100644
index 000000000..a16286687
--- /dev/null
+++ b/backend/src/services/category_tag.go
@@ -0,0 +1,61 @@
+package services
+
+import (
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/GenerateNU/sac/backend/src/transactions"
+ "github.com/GenerateNU/sac/backend/src/utilities"
+ "github.com/go-playground/validator/v10"
+ "gorm.io/gorm"
+)
+
+type CategoryTagServiceInterface interface {
+ GetTagsByCategory(categoryID string, limit string, page string) ([]models.Tag, *errors.Error)
+ GetTagByCategory(categoryID string, tagID string) (*models.Tag, *errors.Error)
+}
+
+type CategoryTagService struct {
+ DB *gorm.DB
+ Validate *validator.Validate
+}
+
+func NewCategoryTagService(db *gorm.DB, validate *validator.Validate) *CategoryTagService {
+ return &CategoryTagService{DB: db, Validate: validate}
+}
+
+func (t *CategoryTagService) GetTagsByCategory(categoryID string, limit string, page string) ([]models.Tag, *errors.Error) {
+ categoryIDAsUUID, err := utilities.ValidateID(categoryID)
+ if err != nil {
+ return nil, err
+ }
+
+ limitAsInt, err := utilities.ValidateNonNegative(limit)
+ if err != nil {
+ return nil, &errors.FailedToValidateLimit
+ }
+
+ pageAsInt, err := utilities.ValidateNonNegative(page)
+ if err != nil {
+ return nil, &errors.FailedToValidatePage
+ }
+
+ offset := (*pageAsInt - 1) * *limitAsInt
+
+ return transactions.GetTagsByCategory(t.DB, *categoryIDAsUUID, *limitAsInt, offset)
+}
+
+func (t *CategoryTagService) GetTagByCategory(categoryID string, tagID string) (*models.Tag, *errors.Error) {
+ categoryIDAsUUID, idErr := utilities.ValidateID(categoryID)
+
+ if idErr != nil {
+ return nil, idErr
+ }
+
+ tagIDAsUUID, idErr := utilities.ValidateID(tagID)
+
+ if idErr != nil {
+ return nil, idErr
+ }
+
+ return transactions.GetTagByCategory(t.DB, *categoryIDAsUUID, *tagIDAsUUID)
+}
diff --git a/backend/src/services/tag.go b/backend/src/services/tag.go
index 8fda17d26..a7f7523d5 100644
--- a/backend/src/services/tag.go
+++ b/backend/src/services/tag.go
@@ -10,10 +10,10 @@ import (
)
type TagServiceInterface interface {
- CreateTag(categoryID string, tagBody models.TagRequestBody) (*models.Tag, *errors.Error)
- GetTag(categoryID string, tagID string) (*models.Tag, *errors.Error)
- UpdateTag(categoryID string, tagID string, tagBody models.TagRequestBody) (*models.Tag, *errors.Error)
- DeleteTag(categoryID string, tagID string) *errors.Error
+ CreateTag(tagBody models.TagRequestBody) (*models.Tag, *errors.Error)
+ GetTag(tagID string) (*models.Tag, *errors.Error)
+ UpdateTag(tagID string, tagBody models.TagRequestBody) (*models.Tag, *errors.Error)
+ DeleteTag(tagID string) *errors.Error
}
type TagService struct {
@@ -25,13 +25,7 @@ func NewTagService(db *gorm.DB, validate *validator.Validate) *TagService {
return &TagService{DB: db, Validate: validate}
}
-func (t *TagService) CreateTag(categoryID string, tagBody models.TagRequestBody) (*models.Tag, *errors.Error) {
- categoryIDAsUUID, idErr := utilities.ValidateID(categoryID)
-
- if idErr != nil {
- return nil, idErr
- }
-
+func (t *TagService) CreateTag(tagBody models.TagRequestBody) (*models.Tag, *errors.Error) {
if err := t.Validate.Struct(tagBody); err != nil {
return nil, &errors.FailedToValidateTag
}
@@ -41,34 +35,20 @@ func (t *TagService) CreateTag(categoryID string, tagBody models.TagRequestBody)
return nil, &errors.FailedToMapRequestToModel
}
- tag.CategoryID = *categoryIDAsUUID
-
return transactions.CreateTag(t.DB, *tag)
}
-func (t *TagService) GetTag(categoryID string, tagID string) (*models.Tag, *errors.Error) {
- categoryIDAsUUID, idErr := utilities.ValidateID(categoryID)
-
- if idErr != nil {
- return nil, idErr
- }
-
+func (t *TagService) GetTag(tagID string) (*models.Tag, *errors.Error) {
tagIDAsUUID, idErr := utilities.ValidateID(tagID)
if idErr != nil {
return nil, idErr
}
- return transactions.GetTag(t.DB, *categoryIDAsUUID, *tagIDAsUUID)
+ return transactions.GetTag(t.DB, *tagIDAsUUID)
}
-func (t *TagService) UpdateTag(categoryID string, tagID string, tagBody models.TagRequestBody) (*models.Tag, *errors.Error) {
- categoryIDAsUUID, idErr := utilities.ValidateID(categoryID)
-
- if idErr != nil {
- return nil, idErr
- }
-
+func (t *TagService) UpdateTag(tagID string, tagBody models.TagRequestBody) (*models.Tag, *errors.Error) {
tagIDAsUUID, idErr := utilities.ValidateID(tagID)
if idErr != nil {
@@ -84,23 +64,15 @@ func (t *TagService) UpdateTag(categoryID string, tagID string, tagBody models.T
return nil, &errors.FailedToMapRequestToModel
}
- tag.CategoryID = *categoryIDAsUUID
-
return transactions.UpdateTag(t.DB, *tagIDAsUUID, *tag)
}
-func (t *TagService) DeleteTag(categoryID string, tagID string) *errors.Error {
- categoryIDAsUUID, idErr := utilities.ValidateID(categoryID)
-
- if idErr != nil {
- return idErr
- }
-
+func (t *TagService) DeleteTag(tagID string) *errors.Error {
tagIDAsUUID, idErr := utilities.ValidateID(tagID)
if idErr != nil {
return idErr
}
- return transactions.DeleteTag(t.DB, *categoryIDAsUUID, *tagIDAsUUID)
+ return transactions.DeleteTag(t.DB, *tagIDAsUUID)
}
diff --git a/backend/src/services/user.go b/backend/src/services/user.go
index 4a985332b..646976e09 100644
--- a/backend/src/services/user.go
+++ b/backend/src/services/user.go
@@ -19,8 +19,6 @@ type UserServiceInterface interface {
GetUser(id string) (*models.User, *errors.Error)
UpdateUser(id string, userBody models.UpdateUserRequestBody) (*models.User, *errors.Error)
DeleteUser(id string) *errors.Error
- GetUserTags(id string) ([]models.Tag, *errors.Error)
- CreateUserTags(id string, tagIDs models.CreateUserTagsBody) ([]models.Tag, *errors.Error)
}
type UserService struct {
@@ -111,33 +109,3 @@ func (u *UserService) DeleteUser(id string) *errors.Error {
return transactions.DeleteUser(u.DB, *idAsUUID)
}
-
-func (u *UserService) GetUserTags(id string) ([]models.Tag, *errors.Error) {
- idAsUUID, err := utilities.ValidateID(id)
- if err != nil {
- return nil, err
- }
-
- return transactions.GetUserTags(u.DB, *idAsUUID)
-}
-
-func (u *UserService) CreateUserTags(id string, tagIDs models.CreateUserTagsBody) ([]models.Tag, *errors.Error) {
- // Validate the id:
- idAsUUID, err := utilities.ValidateID(id)
- if err != nil {
- return nil, err
- }
-
- if err := u.Validate.Struct(tagIDs); err != nil {
- return nil, &errors.FailedToValidateUserTags
- }
-
- // Retrieve a list of valid tags from the ids:
- tags, err := transactions.GetTagsByIDs(u.DB, tagIDs.Tags)
- if err != nil {
- return nil, err
- }
-
- // Update the user to reflect the new tags:
- return transactions.CreateUserTags(u.DB, *idAsUUID, tags)
-}
diff --git a/backend/src/services/user_tag.go b/backend/src/services/user_tag.go
new file mode 100644
index 000000000..27f979f9d
--- /dev/null
+++ b/backend/src/services/user_tag.go
@@ -0,0 +1,54 @@
+package services
+
+import (
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/GenerateNU/sac/backend/src/transactions"
+ "github.com/GenerateNU/sac/backend/src/utilities"
+ "github.com/go-playground/validator/v10"
+ "gorm.io/gorm"
+)
+
+type UserTagServiceInterface interface {
+ GetUserTags(id string) ([]models.Tag, *errors.Error)
+ CreateUserTags(id string, tagIDs models.CreateUserTagsBody) ([]models.Tag, *errors.Error)
+}
+
+type UserTagService struct {
+ DB *gorm.DB
+ Validate *validator.Validate
+}
+
+func NewUserTagService(db *gorm.DB, validate *validator.Validate) *UserTagService {
+ return &UserTagService{DB: db, Validate: validate}
+}
+
+func (u *UserTagService) GetUserTags(id string) ([]models.Tag, *errors.Error) {
+ idAsUUID, err := utilities.ValidateID(id)
+ if err != nil {
+ return nil, err
+ }
+
+ return transactions.GetUserTags(u.DB, *idAsUUID)
+}
+
+func (u *UserTagService) CreateUserTags(id string, tagIDs models.CreateUserTagsBody) ([]models.Tag, *errors.Error) {
+ // Validate the id:
+ idAsUUID, err := utilities.ValidateID(id)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := u.Validate.Struct(tagIDs); err != nil {
+ return nil, &errors.FailedToValidateUserTags
+ }
+
+ // Retrieve a list of valid tags from the ids:
+ tags, err := transactions.GetTagsByIDs(u.DB, tagIDs.Tags)
+ if err != nil {
+ return nil, err
+ }
+
+ // Update the user to reflect the new tags:
+ return transactions.CreateUserTags(u.DB, *idAsUUID, tags)
+}
diff --git a/backend/src/transactions/category_tag.go b/backend/src/transactions/category_tag.go
new file mode 100644
index 000000000..7ef3129a7
--- /dev/null
+++ b/backend/src/transactions/category_tag.go
@@ -0,0 +1,34 @@
+package transactions
+
+import (
+ stdliberrors "errors"
+
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/google/uuid"
+
+ "github.com/GenerateNU/sac/backend/src/models"
+
+ "gorm.io/gorm"
+)
+
+func GetTagsByCategory(db *gorm.DB, categoryID uuid.UUID, limit int, offset int) ([]models.Tag, *errors.Error) {
+ var tags []models.Tag
+ if err := db.Where("category_id = ?", categoryID).Limit(limit).Offset(offset).Find(&tags).Error; err != nil {
+ return nil, &errors.FailedToGetTags
+ }
+
+ return tags, nil
+}
+
+func GetTagByCategory(db *gorm.DB, categoryID uuid.UUID, tagID uuid.UUID) (*models.Tag, *errors.Error) {
+ var tag models.Tag
+ if err := db.Where("category_id = ? AND id = ?", categoryID, tagID).First(&tag).Error; err != nil {
+ if stdliberrors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, &errors.TagNotFound
+ } else {
+ return nil, &errors.FailedToGetTag
+ }
+ }
+
+ return &tag, nil
+}
diff --git a/backend/src/transactions/tag.go b/backend/src/transactions/tag.go
index e2ad6c689..503468f0c 100644
--- a/backend/src/transactions/tag.go
+++ b/backend/src/transactions/tag.go
@@ -35,9 +35,9 @@ func CreateTag(db *gorm.DB, tag models.Tag) (*models.Tag, *errors.Error) {
return &tag, nil
}
-func GetTag(db *gorm.DB, categoryID uuid.UUID, tagID uuid.UUID) (*models.Tag, *errors.Error) {
+func GetTag(db *gorm.DB, tagID uuid.UUID) (*models.Tag, *errors.Error) {
var tag models.Tag
- if err := db.Where("category_id = ? AND id = ?", categoryID, tagID).First(&tag).Error; err != nil {
+ if err := db.Where("id = ?", tagID).First(&tag).Error; err != nil {
if stdliberrors.Is(err, gorm.ErrRecordNotFound) {
return nil, &errors.TagNotFound
} else {
@@ -60,8 +60,8 @@ func UpdateTag(db *gorm.DB, id uuid.UUID, tag models.Tag) (*models.Tag, *errors.
return &tag, nil
}
-func DeleteTag(db *gorm.DB, categoryID uuid.UUID, tagID uuid.UUID) *errors.Error {
- if result := db.Where("category_id = ? AND id = ?", categoryID, tagID).Delete(&models.Tag{}); result.RowsAffected == 0 {
+func DeleteTag(db *gorm.DB, tagID uuid.UUID) *errors.Error {
+ if result := db.Where("id = ?", tagID).Delete(&models.Tag{}); result.RowsAffected == 0 {
if result.Error == nil {
return &errors.TagNotFound
} else {
diff --git a/backend/src/transactions/user.go b/backend/src/transactions/user.go
index 73d9f07c2..14d52e2f5 100644
--- a/backend/src/transactions/user.go
+++ b/backend/src/transactions/user.go
@@ -85,30 +85,3 @@ func DeleteUser(db *gorm.DB, id uuid.UUID) *errors.Error {
}
return nil
}
-
-func GetUserTags(db *gorm.DB, id uuid.UUID) ([]models.Tag, *errors.Error) {
- var tags []models.Tag
-
- user, err := GetUser(db, id)
- if err != nil {
- return nil, &errors.UserNotFound
- }
-
- if err := db.Model(&user).Association("Tag").Find(&tags); err != nil {
- return nil, &errors.FailedToGetTag
- }
- return tags, nil
-}
-
-func CreateUserTags(db *gorm.DB, id uuid.UUID, tags []models.Tag) ([]models.Tag, *errors.Error) {
- user, err := GetUser(db, id)
- if err != nil {
- return nil, &errors.UserNotFound
- }
-
- if err := db.Model(&user).Association("Tag").Replace(tags); err != nil {
- return nil, &errors.FailedToUpdateUser
- }
-
- return tags, nil
-}
diff --git a/backend/src/transactions/user_tag.go b/backend/src/transactions/user_tag.go
new file mode 100644
index 000000000..a8c1dd01d
--- /dev/null
+++ b/backend/src/transactions/user_tag.go
@@ -0,0 +1,35 @@
+package transactions
+
+import (
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/google/uuid"
+ "gorm.io/gorm"
+)
+
+func GetUserTags(db *gorm.DB, id uuid.UUID) ([]models.Tag, *errors.Error) {
+ var tags []models.Tag
+
+ user, err := GetUser(db, id)
+ if err != nil {
+ return nil, &errors.UserNotFound
+ }
+
+ if err := db.Model(&user).Association("Tag").Find(&tags); err != nil {
+ return nil, &errors.FailedToGetTag
+ }
+ return tags, nil
+}
+
+func CreateUserTags(db *gorm.DB, id uuid.UUID, tags []models.Tag) ([]models.Tag, *errors.Error) {
+ user, err := GetUser(db, id)
+ if err != nil {
+ return nil, &errors.UserNotFound
+ }
+
+ if err := db.Model(&user).Association("Tag").Replace(tags); err != nil {
+ return nil, &errors.FailedToUpdateUser
+ }
+
+ return tags, nil
+}
diff --git a/backend/tests/api/category_tag_test.go b/backend/tests/api/category_tag_test.go
new file mode 100644
index 000000000..123e7bb26
--- /dev/null
+++ b/backend/tests/api/category_tag_test.go
@@ -0,0 +1,182 @@
+package tests
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/goccy/go-json"
+ "github.com/gofiber/fiber/v2"
+ "github.com/google/uuid"
+ "github.com/huandu/go-assert"
+)
+
+func AssertTagsWithBodyRespDB(app TestApp, assert *assert.A, resp *http.Response, body *[]map[string]interface{}) []uuid.UUID {
+ var respTags []models.Tag
+
+ err := json.NewDecoder(resp.Body).Decode(&respTags)
+
+ assert.NilError(err)
+
+ var dbTags []models.Tag
+
+ err = app.Conn.Find(&dbTags).Error
+
+ assert.NilError(err)
+
+ assert.Equal(len(dbTags), len(respTags))
+
+ for i, dbTag := range dbTags {
+ assert.Equal(dbTag.ID, respTags[i].ID)
+ assert.Equal(dbTag.Name, respTags[i].Name)
+ assert.Equal(dbTag.CategoryID, respTags[i].CategoryID)
+ }
+
+ tagIDs := make([]uuid.UUID, len(dbTags))
+ for i, dbTag := range dbTags {
+ tagIDs[i] = dbTag.ID
+ }
+
+ return tagIDs
+}
+
+func TestGetCategoryTagsWorks(t *testing.T) {
+ appAssert, categoryUUID, tagID := CreateSampleTag(t)
+
+ body := SampleTagFactory(categoryUUID)
+ (*body)["id"] = tagID
+
+ TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags", categoryUUID),
+ }.TestOnStatusAndDB(t, &appAssert,
+ DBTesterWithStatus{
+ Status: fiber.StatusOK,
+ DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertTagsWithBodyRespDB(app, assert, resp, &[]map[string]interface{}{*body})
+ },
+ },
+ ).Close()
+}
+
+func TestGetCategoryTagsFailsCategoryBadRequest(t *testing.T) {
+ appAssert, _ := CreateSampleCategory(t, nil)
+
+ badRequests := []string{
+ "0",
+ "-1",
+ "1.1",
+ "foo",
+ "null",
+ }
+
+ for _, badRequest := range badRequests {
+ TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags", badRequest),
+ }.TestOnError(t, &appAssert, errors.FailedToValidateID)
+ }
+
+ appAssert.Close()
+}
+
+func TestGetCategoryTagsFailsCategoryNotFound(t *testing.T) {
+ appAssert, _ := CreateSampleCategory(t, nil)
+
+ uuid := uuid.New()
+
+ TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags", uuid),
+ }.TestOnErrorAndDB(t, &appAssert, ErrorWithDBTester{
+ Error: errors.CategoryNotFound,
+ DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ var category models.Category
+ err := app.Conn.Where("id = ?", uuid).First(&category).Error
+ assert.Error(err)
+
+ var respBody []map[string]interface{}
+ err = json.NewDecoder(resp.Body).Decode(&respBody)
+ assert.NilError(err)
+ assert.Equal(0, len(respBody))
+ },
+ }).Close()
+}
+
+func TestGetCategoryTagWorks(t *testing.T) {
+ existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(t)
+
+ TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, tagUUID),
+ }.TestOnStatusAndDB(t, &existingAppAssert,
+ DBTesterWithStatus{
+ Status: fiber.StatusOK,
+ DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory(categoryUUID))
+ },
+ },
+ ).Close()
+}
+
+func TestGetCategoryTagFailsCategoryBadRequest(t *testing.T) {
+ appAssert, _, tagUUID := CreateSampleTag(t)
+
+ badRequests := []string{
+ "0",
+ "-1",
+ "1.1",
+ "foo",
+ "null",
+ }
+
+ for _, badRequest := range badRequests {
+ TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", badRequest, tagUUID),
+ }.TestOnError(t, &appAssert, errors.FailedToValidateID)
+ }
+
+ appAssert.Close()
+}
+
+func TestGetCategoryTagFailsTagBadRequest(t *testing.T) {
+ appAssert, categoryUUID := CreateSampleCategory(t, nil)
+
+ badRequests := []string{
+ "0",
+ "-1",
+ "1.1",
+ "foo",
+ "null",
+ }
+
+ for _, badRequest := range badRequests {
+ TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, badRequest),
+ }.TestOnError(t, &appAssert, errors.FailedToValidateID)
+ }
+
+ appAssert.Close()
+}
+
+func TestGetCategoryTagFailsCategoryNotFound(t *testing.T) {
+ appAssert, _, tagUUID := CreateSampleTag(t)
+
+ TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", uuid.New(), tagUUID),
+ }.TestOnError(t, &appAssert, errors.TagNotFound).Close()
+}
+
+func TestGetCategoryTagFailsTagNotFound(t *testing.T) {
+ appAssert, categoryUUID := CreateSampleCategory(t, nil)
+
+ TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, uuid.New()),
+ }.TestOnError(t, &appAssert, errors.TagNotFound).Close()
+}
diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go
index 17f4f8b1b..eb9174431 100644
--- a/backend/tests/api/tag_test.go
+++ b/backend/tests/api/tag_test.go
@@ -14,9 +14,10 @@ import (
"github.com/goccy/go-json"
)
-func SampleTagFactory() *map[string]interface{} {
+func SampleTagFactory(categoryID uuid.UUID) *map[string]interface{} {
return &map[string]interface{}{
- "name": "Generate",
+ "name": "Generate",
+ "category_id": categoryID,
}
}
@@ -38,29 +39,30 @@ func AssertTagWithBodyRespDB(app TestApp, assert *assert.A, resp *http.Response,
assert.Equal(dbTag.CategoryID, respTag.CategoryID)
assert.Equal((*body)["name"].(string), dbTag.Name)
+ assert.Equal((*body)["category_id"].(uuid.UUID), dbTag.CategoryID)
return dbTag.ID
}
func AssertSampleTagBodyRespDB(t *testing.T, app TestApp, assert *assert.A, resp *http.Response) uuid.UUID {
- appAssert, _ := CreateSampleCategory(t, &ExistingAppAssert{
+ appAssert, uuid := CreateSampleCategory(t, &ExistingAppAssert{
App: app,
Assert: assert,
})
- return AssertTagWithBodyRespDB(appAssert.App, appAssert.Assert, resp, SampleTagFactory())
+ return AssertTagWithBodyRespDB(appAssert.App, appAssert.Assert, resp, SampleTagFactory(uuid))
}
func CreateSampleTag(t *testing.T) (appAssert ExistingAppAssert, categoryUUID uuid.UUID, tagUUID uuid.UUID) {
appAssert, categoryUUID = CreateSampleCategory(t, nil)
AssertSampleTagBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
- tagUUID = AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory())
+ tagUUID = AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory(categoryUUID))
}
TestRequest{
Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags", categoryUUID),
- Body: SampleTagFactory(),
+ Path: "/api/v1/tags/",
+ Body: SampleTagFactory(categoryUUID),
}.TestOnStatusAndDB(t, &appAssert,
DBTesterWithStatus{
Status: fiber.StatusCreated,
@@ -95,65 +97,54 @@ func Assert1Tag(app TestApp, assert *assert.A, resp *http.Response) {
}
func TestCreateTagFailsBadRequest(t *testing.T) {
- appAssert, categoryUUID := CreateSampleCategory(t, nil)
-
badBodys := []map[string]interface{}{
{
- "name": 1,
+ "name": "Generate",
+ "category_id": "1",
+ },
+ {
+ "name": 1,
+ "category_id": 1,
},
}
for _, badBody := range badBodys {
TestRequest{
Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags", categoryUUID),
+ Path: "/api/v1/tags/",
Body: &badBody,
- }.TestOnErrorAndDB(t, &appAssert,
+ }.TestOnErrorAndDB(t, nil,
ErrorWithDBTester{
Error: errors.FailedToParseRequestBody,
DBTester: AssertNoTags,
},
- )
+ ).Close()
}
-
- appAssert.Close()
-}
-
-func TestCreateTagFailsCategoryNotFound(t *testing.T) {
- uuid := uuid.New()
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags", uuid),
- Body: SampleTagFactory(),
- }.TestOnErrorAndDB(t, nil,
- ErrorWithDBTester{
- Error: errors.CategoryNotFound,
- DBTester: AssertNoTags,
- },
- ).Close()
}
func TestCreateTagFailsValidation(t *testing.T) {
- appAssert, categoryUUID := CreateSampleCategory(t, nil)
-
badBodys := []map[string]interface{}{
+ {
+ "name": "Generate",
+ },
+ {
+ "category_id": uuid.New(),
+ },
{},
}
for _, badBody := range badBodys {
TestRequest{
Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags", categoryUUID),
+ Path: "/api/v1/tags/",
Body: &badBody,
- }.TestOnErrorAndDB(t, &appAssert,
+ }.TestOnErrorAndDB(t, nil,
ErrorWithDBTester{
Error: errors.FailedToValidateTag,
DBTester: AssertNoTags,
},
- )
+ ).Close()
}
-
- appAssert.Close()
}
func TestGetTagWorks(t *testing.T) {
@@ -161,41 +152,18 @@ func TestGetTagWorks(t *testing.T) {
TestRequest{
Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, tagUUID),
+ Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
}.TestOnStatusAndDB(t, &existingAppAssert,
DBTesterWithStatus{
Status: fiber.StatusOK,
DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
- AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory())
+ AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory(categoryUUID))
},
},
).Close()
}
-func TestGetTagFailsCategoryBadRequest(t *testing.T) {
- appAssert, _, tagUUID := CreateSampleTag(t)
-
- badRequests := []string{
- "0",
- "-1",
- "1.1",
- "foo",
- "null",
- }
-
- for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", badRequest, tagUUID),
- }.TestOnError(t, &appAssert, errors.FailedToValidateID)
- }
-
- appAssert.Close()
-}
-
-func TestGetTagFailsTagBadRequest(t *testing.T) {
- appAssert, categoryUUID := CreateSampleCategory(t, nil)
-
+func TestGetTagFailsBadRequest(t *testing.T) {
badRequests := []string{
"0",
"-1",
@@ -207,35 +175,22 @@ func TestGetTagFailsTagBadRequest(t *testing.T) {
for _, badRequest := range badRequests {
TestRequest{
Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, badRequest),
- }.TestOnError(t, &appAssert, errors.FailedToValidateID)
+ Path: fmt.Sprintf("/api/v1/tags/%s", badRequest),
+ }.TestOnError(t, nil, errors.FailedToValidateID).Close()
}
-
- appAssert.Close()
}
-func TestGetTagFailsCategoryNotFound(t *testing.T) {
- appAssert, _, tagUUID := CreateSampleTag(t)
-
+func TestGetTagFailsNotFound(t *testing.T) {
TestRequest{
Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", uuid.New(), tagUUID),
- }.TestOnError(t, &appAssert, errors.TagNotFound).Close()
-}
-
-func TestGetTagFailsTagNotFound(t *testing.T) {
- appAssert, categoryUUID := CreateSampleCategory(t, nil)
-
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, uuid.New()),
- }.TestOnError(t, &appAssert, errors.TagNotFound).Close()
+ Path: fmt.Sprintf("/api/v1/tags/%s", uuid.New()),
+ }.TestOnError(t, nil, errors.TagNotFound).Close()
}
func TestUpdateTagWorksUpdateName(t *testing.T) {
existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(t)
- generateNUTag := *SampleTagFactory()
+ generateNUTag := *SampleTagFactory(categoryUUID)
generateNUTag["name"] = "GenerateNU"
AssertUpdatedTagBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
@@ -244,7 +199,7 @@ func TestUpdateTagWorksUpdateName(t *testing.T) {
TestRequest{
Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, tagUUID),
+ Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
Body: &generateNUTag,
}.TestOnStatusAndDB(t, &existingAppAssert,
DBTesterWithStatus{
@@ -255,13 +210,15 @@ func TestUpdateTagWorksUpdateName(t *testing.T) {
}
func TestUpdateTagWorksUpdateCategory(t *testing.T) {
- existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(t)
+ existingAppAssert, _, tagUUID := CreateSampleTag(t)
technologyCategory := *SampleCategoryFactory()
technologyCategory["name"] = "Technology"
+ var technologyCategoryUUID uuid.UUID
+
AssertNewCategoryBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
- AssertCategoryWithBodyRespDBMostRecent(app, assert, resp, &technologyCategory)
+ technologyCategoryUUID = AssertCategoryWithBodyRespDBMostRecent(app, assert, resp, &technologyCategory)
}
TestRequest{
@@ -275,7 +232,7 @@ func TestUpdateTagWorksUpdateCategory(t *testing.T) {
},
)
- technologyTag := *SampleTagFactory()
+ technologyTag := *SampleTagFactory(technologyCategoryUUID)
AssertUpdatedTagBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
AssertTagWithBodyRespDB(app, assert, resp, &technologyTag)
@@ -283,7 +240,7 @@ func TestUpdateTagWorksUpdateCategory(t *testing.T) {
TestRequest{
Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, tagUUID),
+ Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
Body: &technologyTag,
}.TestOnStatusAndDB(t, &existingAppAssert,
DBTesterWithStatus{
@@ -298,42 +255,20 @@ func TestUpdateTagWorksWithSameDetails(t *testing.T) {
TestRequest{
Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, tagUUID),
- Body: SampleTagFactory(),
+ Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
+ Body: SampleTagFactory(categoryUUID),
}.TestOnStatusAndDB(t, &existingAppAssert,
DBTesterWithStatus{
Status: fiber.StatusOK,
DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
- AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory())
+ AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory(categoryUUID))
},
},
).Close()
}
-func TestUpdateTagFailsCategoryBadRequest(t *testing.T) {
- appAssert, _, tagUUID := CreateSampleTag(t)
-
- badRequests := []string{
- "0",
- "-1",
- "1.1",
- "foo",
- "null",
- }
-
- for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", badRequest, tagUUID),
- Body: SampleTagFactory(),
- }.TestOnError(t, &appAssert, errors.FailedToValidateID)
- }
-
- appAssert.Close()
-}
-
-func TestUpdateTagFailsTagBadRequest(t *testing.T) {
- appAssert, categoryUUID := CreateSampleCategory(t, nil)
+func TestUpdateTagFailsBadRequest(t *testing.T) {
+ appAssert, uuid := CreateSampleCategory(t, nil)
badRequests := []string{
"0",
@@ -346,20 +281,18 @@ func TestUpdateTagFailsTagBadRequest(t *testing.T) {
for _, badRequest := range badRequests {
TestRequest{
Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, badRequest),
- Body: SampleTagFactory(),
- }.TestOnError(t, &appAssert, errors.FailedToValidateID)
+ Path: fmt.Sprintf("/api/v1/tags/%s", badRequest),
+ Body: SampleTagFactory(uuid),
+ }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close()
}
-
- appAssert.Close()
}
func TestDeleteTagWorks(t *testing.T) {
- existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(t)
+ existingAppAssert, _, tagUUID := CreateSampleTag(t)
TestRequest{
Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, tagUUID),
+ Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
}.TestOnStatusAndDB(t, &existingAppAssert,
DBTesterWithStatus{
Status: fiber.StatusNoContent,
@@ -368,34 +301,8 @@ func TestDeleteTagWorks(t *testing.T) {
).Close()
}
-func TestDeleteTagFailsCategoryBadRequest(t *testing.T) {
- appAssert, _, tagUUID := CreateSampleTag(t)
-
- badRequests := []string{
- "0",
- "-1",
- "1.1",
- "foo",
- "null",
- }
-
- for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", badRequest, tagUUID),
- }.TestOnErrorAndDB(t, &appAssert,
- ErrorWithDBTester{
- Error: errors.FailedToValidateID,
- DBTester: Assert1Tag,
- },
- )
- }
-
- appAssert.Close()
-}
-
-func TestDeleteTagFailsTagBadRequest(t *testing.T) {
- appAssert, categoryUUID, _ := CreateSampleTag(t)
+func TestDeleteTagFailsBadRequest(t *testing.T) {
+ appAssert, _, _ := CreateSampleTag(t)
badRequests := []string{
"0",
@@ -408,7 +315,7 @@ func TestDeleteTagFailsTagBadRequest(t *testing.T) {
for _, badRequest := range badRequests {
TestRequest{
Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, badRequest),
+ Path: fmt.Sprintf("/api/v1/tags/%s", badRequest),
}.TestOnErrorAndDB(t, &appAssert,
ErrorWithDBTester{
Error: errors.FailedToValidateID,
@@ -420,26 +327,12 @@ func TestDeleteTagFailsTagBadRequest(t *testing.T) {
appAssert.Close()
}
-func TestDeleteTagFailsCategoryNotFound(t *testing.T) {
- appAssert, _, tagUUID := CreateSampleTag(t)
-
- TestRequest{
- Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", uuid.New(), tagUUID),
- }.TestOnErrorAndDB(t, &appAssert,
- ErrorWithDBTester{
- Error: errors.TagNotFound,
- DBTester: Assert1Tag,
- },
- ).Close()
-}
-
-func TestDeleteTagFailsTagNotFound(t *testing.T) {
- appAssert, categoryUUID, _ := CreateSampleTag(t)
+func TestDeleteTagFailsNotFound(t *testing.T) {
+ appAssert, _, _ := CreateSampleTag(t)
TestRequest{
Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, uuid.New()),
+ Path: fmt.Sprintf("/api/v1/tags/%s", uuid.New()),
}.TestOnErrorAndDB(t, &appAssert,
ErrorWithDBTester{
Error: errors.TagNotFound,
diff --git a/backend/tests/api/user_tag_test.go b/backend/tests/api/user_tag_test.go
new file mode 100644
index 000000000..a67022a56
--- /dev/null
+++ b/backend/tests/api/user_tag_test.go
@@ -0,0 +1,322 @@
+package tests
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/goccy/go-json"
+ "github.com/gofiber/fiber/v2"
+ "github.com/google/uuid"
+ "github.com/huandu/go-assert"
+)
+
+func SampleCategoriesFactory() *[]map[string]interface{} {
+ return &[]map[string]interface{}{
+ {
+ "name": "Business",
+ },
+ {
+ "name": "STEM",
+ },
+ }
+}
+
+func SampleTagsFactory(categoryIDs []uuid.UUID) *[]map[string]interface{} {
+ lenOfIDs := len(categoryIDs)
+
+ return &[]map[string]interface{}{
+ {
+ "name": "Computer Science",
+ "category_id": categoryIDs[1%lenOfIDs],
+ },
+ {
+ "name": "Mechanical Engineering",
+ "category_id": categoryIDs[1%lenOfIDs],
+ },
+ {
+ "name": "Finance",
+ "category_id": categoryIDs[0%lenOfIDs],
+ },
+ }
+}
+
+func SampleTagIDsFactory(tagIDs *[]uuid.UUID) *map[string]interface{} {
+ tags := tagIDs
+
+ if tags == nil {
+ tags = &[]uuid.UUID{uuid.New()}
+ }
+
+ return &map[string]interface{}{
+ "tags": tags,
+ }
+}
+
+func CreateSetOfTags(t *testing.T, appAssert ExistingAppAssert) []uuid.UUID {
+ categories := SampleCategoriesFactory()
+
+ categoryIDs := []uuid.UUID{}
+ for _, category := range *categories {
+ TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/categories/",
+ Body: &category,
+ }.TestOnStatusAndDB(t, &appAssert,
+ DBTesterWithStatus{
+ Status: fiber.StatusCreated,
+ DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ var respCategory models.Category
+
+ err := json.NewDecoder(resp.Body).Decode(&respCategory)
+
+ assert.NilError(err)
+
+ categoryIDs = append(categoryIDs, respCategory.ID)
+ },
+ },
+ )
+ }
+
+ tags := SampleTagsFactory(categoryIDs)
+
+ tagIDs := []uuid.UUID{}
+ for _, tag := range *tags {
+ TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/tags/",
+ Body: &tag,
+ }.TestOnStatusAndDB(t, &appAssert,
+ DBTesterWithStatus{
+ Status: fiber.StatusCreated,
+ DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ var respTag models.Tag
+
+ err := json.NewDecoder(resp.Body).Decode(&respTag)
+
+ assert.NilError(err)
+
+ tagIDs = append(tagIDs, respTag.ID)
+ },
+ },
+ )
+ }
+
+ return tagIDs
+}
+
+func AssertUserTagsRespDB(app TestApp, assert *assert.A, resp *http.Response, id uuid.UUID) {
+ var respTags []models.Tag
+
+ // Retrieve the tags from the response:
+ err := json.NewDecoder(resp.Body).Decode(&respTags)
+
+ assert.NilError(err)
+
+ // Retrieve the user connected to the tags:
+ var dbUser models.User
+ err = app.Conn.First(&dbUser, id).Error
+
+ assert.NilError(err)
+
+ // Retrieve the tags in the bridge table associated with the user:
+ var dbTags []models.Tag
+ err = app.Conn.Model(&dbUser).Association("Tag").Find(&dbTags)
+
+ assert.NilError(err)
+
+ // Confirm all the resp tags are equal to the db tags:
+ for i, respTag := range respTags {
+ assert.Equal(respTag.ID, dbTags[i].ID)
+ assert.Equal(respTag.Name, dbTags[i].Name)
+ assert.Equal(respTag.CategoryID, dbTags[i].CategoryID)
+ }
+}
+
+func AssertSampleUserTagsRespDB(app TestApp, assert *assert.A, resp *http.Response, uuid uuid.UUID) {
+ AssertUserTagsRespDB(app, assert, resp, uuid)
+}
+
+func TestCreateUserTagsFailsOnInvalidDataType(t *testing.T) {
+ // Invalid tag data types:
+ invalidTags := []interface{}{
+ []string{"1", "2", "34"},
+ []models.Tag{{Name: "Test", CategoryID: uuid.UUID{}}, {Name: "Test2", CategoryID: uuid.UUID{}}},
+ []float32{1.32, 23.5, 35.1},
+ }
+
+ // Test each of the invalid tags:
+ for _, tag := range invalidTags {
+ malformedTag := *SampleTagIDsFactory(nil)
+ malformedTag["tags"] = tag
+
+ TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/users/1/tags/",
+ Body: &malformedTag,
+ }.TestOnError(t, nil, errors.FailedToParseRequestBody).Close()
+ }
+}
+
+func TestCreateUserTagsFailsOnInvalidUserID(t *testing.T) {
+ badRequests := []string{
+ "0",
+ "-1",
+ "1.1",
+ "foo",
+ "null",
+ }
+
+ for _, badRequest := range badRequests {
+ TestRequest{
+ Method: fiber.MethodPost,
+ Path: fmt.Sprintf("/api/v1/users/%s/tags", badRequest),
+ Body: SampleTagIDsFactory(nil),
+ }.TestOnError(t, nil, errors.FailedToValidateID).Close()
+ }
+}
+
+type UUIDSlice []uuid.UUID
+
+var testUUID = uuid.New()
+
+func TestCreateUserTagsFailsOnInvalidKey(t *testing.T) {
+ appAssert, uuid := CreateSampleUser(t, nil)
+
+ invalidBody := []map[string]interface{}{
+ {
+ "tag": UUIDSlice{testUUID, testUUID},
+ },
+ {
+ "tagIDs": []uint{1, 2, 3},
+ },
+ }
+
+ for _, body := range invalidBody {
+ TestRequest{
+ Method: fiber.MethodPost,
+ Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
+ Body: &body,
+ }.TestOnError(t, &appAssert, errors.FailedToValidateUserTags)
+ }
+
+ appAssert.Close()
+}
+
+func TestCreateUserTagsFailsOnNonExistentUser(t *testing.T) {
+ TestRequest{
+ Method: fiber.MethodPost,
+ Path: fmt.Sprintf("/api/v1/users/%s/tags", uuid.New()),
+ Body: SampleTagIDsFactory(nil),
+ }.TestOnError(t, nil, errors.UserNotFound).Close()
+}
+
+func TestCreateUserTagsWorks(t *testing.T) {
+ appAssert, uuid := CreateSampleUser(t, nil)
+
+ // Create a set of tags:
+ tagUUIDs := CreateSetOfTags(t, appAssert)
+
+ // Confirm adding real tags adds them to the user:
+ TestRequest{
+ Method: fiber.MethodPost,
+ Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
+ Body: SampleTagIDsFactory(&tagUUIDs),
+ }.TestOnStatusAndDB(t, &appAssert,
+ DBTesterWithStatus{
+ Status: fiber.StatusCreated,
+ DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertSampleUserTagsRespDB(app, assert, resp, uuid)
+ },
+ },
+ )
+
+ appAssert.Close()
+}
+
+func TestCreateUserTagsNoneAddedIfInvalid(t *testing.T) {
+ appAssert, uuid := CreateSampleUser(t, nil)
+
+ TestRequest{
+ Method: fiber.MethodPost,
+ Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
+ Body: SampleTagIDsFactory(nil),
+ }.TestOnStatusAndDB(t, &appAssert,
+ DBTesterWithStatus{
+ Status: fiber.StatusCreated,
+ DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ var respTags []models.Tag
+
+ err := json.NewDecoder(resp.Body).Decode(&respTags)
+
+ assert.NilError(err)
+
+ assert.Equal(len(respTags), 0)
+ },
+ },
+ )
+
+ appAssert.Close()
+}
+
+func TestGetUserTagsFailsOnNonExistentUser(t *testing.T) {
+ TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid.New()),
+ }.TestOnError(t, nil, errors.UserNotFound).Close()
+}
+
+func TestGetUserTagsReturnsEmptyListWhenNoneAdded(t *testing.T) {
+ appAssert, uuid := CreateSampleUser(t, nil)
+
+ TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
+ }.TestOnStatusAndDB(t, &appAssert,
+ DBTesterWithStatus{
+ Status: 200,
+ DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ var respTags []models.Tag
+
+ err := json.NewDecoder(resp.Body).Decode(&respTags)
+
+ assert.NilError(err)
+
+ assert.Equal(len(respTags), 0)
+ },
+ },
+ )
+
+ appAssert.Close()
+}
+
+func TestGetUserTagsReturnsCorrectList(t *testing.T) {
+ appAssert, uuid := CreateSampleUser(t, nil)
+
+ // Create a set of tags:
+ tagUUIDs := CreateSetOfTags(t, appAssert)
+
+ // Add the tags:
+ TestRequest{
+ Method: fiber.MethodPost,
+ Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
+ Body: SampleTagIDsFactory(&tagUUIDs),
+ }.TestOnStatus(t, &appAssert, fiber.StatusCreated)
+
+ // Get the tags:
+ TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
+ }.TestOnStatusAndDB(t, &appAssert,
+ DBTesterWithStatus{
+ Status: fiber.StatusOK,
+ DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertSampleUserTagsRespDB(app, assert, resp, uuid)
+ },
+ },
+ )
+
+ appAssert.Close()
+}
diff --git a/backend/tests/api/user_test.go b/backend/tests/api/user_test.go
index acd94abec..a1f74a8c8 100644
--- a/backend/tests/api/user_test.go
+++ b/backend/tests/api/user_test.go
@@ -550,309 +550,3 @@ func TestCreateUserFailsOnMissingFields(t *testing.T) {
}
appAssert.Close()
}
-
-func SampleCategoriesFactory() *[]map[string]interface{} {
- return &[]map[string]interface{}{
- {
- "name": "Business",
- },
- {
- "name": "STEM",
- },
- }
-}
-
-func SampleTagsFactory(categoryUUIDs []uuid.UUID) *[]map[string]interface{} {
- lenOfUUIDs := len(categoryUUIDs)
-
- return &[]map[string]interface{}{
- {
- "name": "Computer Science",
- "category_id": categoryUUIDs[1%lenOfUUIDs],
- },
- {
- "name": "Mechanical Engineering",
- "category_id": categoryUUIDs[1%lenOfUUIDs],
- },
- {
- "name": "Finance",
- "category_id": categoryUUIDs[0%lenOfUUIDs],
- },
- }
-}
-
-func SampleTagUUIDsFactory(tagUUIDs *[]uuid.UUID) *map[string]interface{} {
- tags := tagUUIDs
-
- if tags == nil {
- tags = &[]uuid.UUID{uuid.New()}
- }
-
- return &map[string]interface{}{
- "tags": tags,
- }
-}
-
-func CreateSetOfTags(t *testing.T, appAssert ExistingAppAssert) []uuid.UUID {
- categories := SampleCategoriesFactory()
-
- categoryUUIDs := []uuid.UUID{}
- for _, category := range *categories {
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/categories/",
- Body: &category,
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
- Status: fiber.StatusCreated,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
- var respCategory models.Category
-
- err := json.NewDecoder(resp.Body).Decode(&respCategory)
-
- assert.NilError(err)
-
- categoryUUIDs = append(categoryUUIDs, respCategory.ID)
- },
- },
- )
- }
-
- tags := SampleTagsFactory(categoryUUIDs)
-
- tagUUIDs := []uuid.UUID{}
-
- for _, categoryUUID := range categoryUUIDs {
- for _, tag := range *tags {
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/", categoryUUID),
- Body: &tag,
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
- Status: fiber.StatusCreated,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
- var respTag models.Tag
-
- err := json.NewDecoder(resp.Body).Decode(&respTag)
-
- assert.NilError(err)
-
- tagUUIDs = append(tagUUIDs, respTag.ID)
- },
- },
- )
- }
- }
-
- return tagUUIDs
-}
-
-func AssertUserTagsRespDB(app TestApp, assert *assert.A, resp *http.Response, id uuid.UUID) {
- var respTags []models.Tag
-
- // Retrieve the tags from the response:
- err := json.NewDecoder(resp.Body).Decode(&respTags)
-
- assert.NilError(err)
-
- // Retrieve the user connected to the tags:
- var dbUser models.User
- err = app.Conn.First(&dbUser, id).Error
-
- assert.NilError(err)
-
- // Retrieve the tags in the bridge table associated with the user:
- var dbTags []models.Tag
- err = app.Conn.Model(&dbUser).Association("Tag").Find(&dbTags)
-
- assert.NilError(err)
-
- // Confirm all the resp tags are equal to the db tags:
- for i, respTag := range respTags {
- assert.Equal(respTag.ID, dbTags[i].ID)
- assert.Equal(respTag.Name, dbTags[i].Name)
- assert.Equal(respTag.CategoryID, dbTags[i].CategoryID)
- }
-}
-
-func AssertSampleUserTagsRespDB(app TestApp, assert *assert.A, resp *http.Response, uuid uuid.UUID) {
- AssertUserTagsRespDB(app, assert, resp, uuid)
-}
-
-func TestCreateUserTagsFailsOnInvalidDataType(t *testing.T) {
- // Invalid tag data types:
- invalidTags := []interface{}{
- []string{"1", "2", "34"},
- []models.Tag{{Name: "Test", CategoryID: uuid.UUID{}}, {Name: "Test2", CategoryID: uuid.UUID{}}},
- []float32{1.32, 23.5, 35.1},
- }
-
- // Test each of the invalid tags:
- for _, tag := range invalidTags {
- malformedTag := *SampleTagUUIDsFactory(nil)
- malformedTag["tags"] = tag
-
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/users/1/tags/",
- Body: &malformedTag,
- }.TestOnError(t, nil, errors.FailedToParseRequestBody).Close()
- }
-}
-
-func TestCreateUserTagsFailsOnInvalidUserID(t *testing.T) {
- badRequests := []string{
- "0",
- "-1",
- "1.1",
- "foo",
- "null",
- }
-
- for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/users/%s/tags", badRequest),
- Body: SampleTagUUIDsFactory(nil),
- }.TestOnError(t, nil, errors.FailedToValidateID).Close()
- }
-}
-
-type UUIDSlice []uuid.UUID
-
-var testUUID = uuid.New()
-
-func TestCreateUserTagsFailsOnInvalidKey(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
-
- invalidBody := []map[string]interface{}{
- {
- "tag": UUIDSlice{testUUID, testUUID},
- },
- {
- "tagIDs": []uint{1, 2, 3},
- },
- }
-
- for _, body := range invalidBody {
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
- Body: &body,
- }.TestOnError(t, &appAssert, errors.FailedToValidateUserTags)
- }
-
- appAssert.Close()
-}
-
-func TestCreateUserTagsFailsOnNonExistentUser(t *testing.T) {
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/users/%s/tags", uuid.New()),
- Body: SampleTagUUIDsFactory(nil),
- }.TestOnError(t, nil, errors.UserNotFound).Close()
-}
-
-func TestCreateUserTagsWorks(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
-
- tagUUIDs := CreateSetOfTags(t, appAssert)
-
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
- Body: SampleTagUUIDsFactory(&tagUUIDs),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
- Status: fiber.StatusCreated,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
- AssertSampleUserTagsRespDB(app, assert, resp, uuid)
- },
- },
- )
-
- appAssert.Close()
-}
-
-func TestCreateUserTagsNoneAddedIfInvalid(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
-
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
- Body: SampleTagUUIDsFactory(nil),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
- Status: fiber.StatusCreated,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
- var respTags []models.Tag
-
- err := json.NewDecoder(resp.Body).Decode(&respTags)
-
- assert.NilError(err)
-
- assert.Equal(len(respTags), 0)
- },
- },
- )
-
- appAssert.Close()
-}
-
-func TestGetUserTagsFailsOnNonExistentUser(t *testing.T) {
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid.New()),
- }.TestOnError(t, nil, errors.UserNotFound).Close()
-}
-
-func TestGetUserTagsReturnsEmptyListWhenNoneAdded(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
-
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
- Status: 200,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
- var respTags []models.Tag
-
- err := json.NewDecoder(resp.Body).Decode(&respTags)
-
- assert.NilError(err)
-
- assert.Equal(len(respTags), 0)
- },
- },
- )
-
- appAssert.Close()
-}
-
-func TestGetUserTagsReturnsCorrectList(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
-
- tagUUIDs := CreateSetOfTags(t, appAssert)
-
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
- Body: SampleTagUUIDsFactory(&tagUUIDs),
- }.TestOnStatus(t, &appAssert, fiber.StatusCreated)
-
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
- Status: fiber.StatusOK,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
- AssertSampleUserTagsRespDB(app, assert, resp, uuid)
- },
- },
- )
-
- appAssert.Close()
-}
From 60e360bbf8cd4eaee4b9334d2c6a00ac59e9668d Mon Sep 17 00:00:00 2001
From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
Date: Sun, 4 Feb 2024 19:35:36 -0500
Subject: [PATCH 10/33] Handle Auth in Tests (#143)
Co-authored-by: David Oduneye
---
.github/workflows/backend.yml | 4 +-
.gitignore | 2 +
backend/src/auth/tokens.go | 30 +-
backend/src/controllers/auth.go | 17 +-
backend/src/controllers/category.go | 6 +-
backend/src/controllers/club.go | 20 +-
backend/src/controllers/user.go | 6 +-
backend/src/database/db.go | 66 +--
backend/src/database/super.go | 43 ++
backend/src/errors/auth.go | 14 +
backend/src/errors/common.go | 4 -
backend/src/main.go | 12 +-
backend/src/middleware/auth.go | 32 +-
backend/src/middleware/club.go | 6 +-
backend/src/middleware/middleware.go | 13 +-
backend/src/middleware/user.go | 8 +-
backend/src/models/club.go | 3 +-
backend/src/models/user.go | 4 +-
backend/src/server/routes/auth.go | 20 +
backend/src/server/routes/category.go | 21 +
backend/src/server/routes/category_tag.go | 16 +
backend/src/server/routes/club.go | 26 ++
backend/src/server/routes/tag.go | 18 +
backend/src/server/routes/user.go | 28 ++
backend/src/server/routes/user_tag.go | 16 +
backend/src/server/routes/utility.go | 13 +
backend/src/server/server.go | 129 +-----
backend/src/services/user.go | 7 -
backend/src/transactions/category_tag.go | 9 +
backend/src/types/custom_claims.go | 20 +-
backend/src/types/permissions.go | 2 +-
backend/tests/README.md | 4 +-
backend/tests/api/category_tag_test.go | 134 +++---
backend/tests/api/category_test.go | 302 ++++++++------
backend/tests/api/club_test.go | 215 +++++-----
backend/tests/api/health_test.go | 10 +-
backend/tests/api/helpers.go | 262 ------------
backend/tests/api/helpers/app.go | 64 +++
backend/tests/api/helpers/auth.go | 166 ++++++++
backend/tests/api/helpers/database.go | 46 +++
backend/tests/api/helpers/requests.go | 164 ++++++++
backend/tests/api/helpers/test.go | 19 +
backend/tests/api/helpers/utilities.go | 45 ++
.../utilities_test.go} | 2 +-
backend/tests/api/tag_test.go | 298 +++++++------
backend/tests/api/user_tag_test.go | 243 ++++++-----
backend/tests/api/user_test.go | 391 ++++++++++--------
frontend/node_modules/.yarn-integrity | 10 -
frontend/yarn.lock | 4 -
49 files changed, 1801 insertions(+), 1193 deletions(-)
create mode 100644 backend/src/database/super.go
create mode 100644 backend/src/errors/auth.go
create mode 100644 backend/src/server/routes/auth.go
create mode 100644 backend/src/server/routes/category.go
create mode 100644 backend/src/server/routes/category_tag.go
create mode 100644 backend/src/server/routes/club.go
create mode 100644 backend/src/server/routes/tag.go
create mode 100644 backend/src/server/routes/user.go
create mode 100644 backend/src/server/routes/user_tag.go
create mode 100644 backend/src/server/routes/utility.go
delete mode 100644 backend/tests/api/helpers.go
create mode 100644 backend/tests/api/helpers/app.go
create mode 100644 backend/tests/api/helpers/auth.go
create mode 100644 backend/tests/api/helpers/database.go
create mode 100644 backend/tests/api/helpers/requests.go
create mode 100644 backend/tests/api/helpers/test.go
create mode 100644 backend/tests/api/helpers/utilities.go
rename backend/tests/api/{helpers_test.go => helpers/utilities_test.go} (96%)
delete mode 100644 frontend/node_modules/.yarn-integrity
delete mode 100644 frontend/yarn.lock
diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml
index b370f8a1e..26801fd58 100644
--- a/.github/workflows/backend.yml
+++ b/.github/workflows/backend.yml
@@ -96,9 +96,7 @@ jobs:
docker exec $CONTAINER_ID cat /var/lib/postgresql/data/postgresql.conf | grep max_connections
- name: Restart PostgreSQL Container
run: docker restart $(docker ps --filter "publish=5432" --format "{{.ID}}")
- - name: Migrate DB
- run: cd ./backend/src && go run main.go --only-migrate
- name: Run Tests with Coverage
- run: cd ./backend/ && go test -benchmem -race -coverprofile=coverage.txt ./...
+ run: cd ./backend/ && go test -bench=. -benchmem -race -coverprofile=coverage.txt ./...
- name: Print Coverage
run: cd ./backend/ && go tool cover -func=coverage.txt
diff --git a/.gitignore b/.gitignore
index df5f21020..46329301c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
.DS_Store
.env
sac-cli
+.vscode
+.trunk
\ No newline at end of file
diff --git a/backend/src/auth/tokens.go b/backend/src/auth/tokens.go
index 255b2a5be..c065f7340 100644
--- a/backend/src/auth/tokens.go
+++ b/backend/src/auth/tokens.go
@@ -1,7 +1,6 @@
package auth
import (
- "fmt"
"time"
"github.com/GenerateNU/sac/backend/src/config"
@@ -71,7 +70,6 @@ func CreateRefreshToken(id string, refreshExpiresAfter uint, refreshTokenSecret
func SignToken(token *jwt.Token, secret string) (*string, *errors.Error) {
if token == nil || secret == "" {
- fmt.Println(token)
return nil, &errors.FailedToSignToken
}
@@ -103,9 +101,9 @@ func ExpireCookie(name string) *fiber.Cookie {
}
// RefreshAccessToken refreshes the access token
-func RefreshAccessToken(refreshCookie string, role string, accessExpiresAfter uint, accessTokenSecret string) (*string, *errors.Error) {
+func RefreshAccessToken(refreshCookie string, role string, refreshTokenSecret string, accessExpiresAfter uint, accessTokenSecret string) (*string, *errors.Error) {
// Parse the refresh token
- refreshToken, err := ParseRefreshToken(refreshCookie)
+ refreshToken, err := ParseRefreshToken(refreshCookie, refreshTokenSecret)
if err != nil {
return nil, &errors.FailedToParseRefreshToken
}
@@ -126,26 +124,22 @@ func RefreshAccessToken(refreshCookie string, role string, accessExpiresAfter ui
}
// ParseAccessToken parses the access token
-func ParseAccessToken(cookie string) (*jwt.Token, error) {
- var settings config.Settings
-
+func ParseAccessToken(cookie string, accessTokenSecret string) (*jwt.Token, error) {
return jwt.ParseWithClaims(cookie, &types.CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
- return []byte(settings.Auth.AccessToken), nil
+ return []byte(accessTokenSecret), nil
})
}
// ParseRefreshToken parses the refresh token
-func ParseRefreshToken(cookie string) (*jwt.Token, error) {
- var settings config.Settings
-
+func ParseRefreshToken(cookie string, refreshTokenSecret string) (*jwt.Token, error) {
return jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
- return []byte(settings.Auth.RefreshToken), nil
+ return []byte(refreshTokenSecret), nil
})
}
// GetRoleFromToken gets the role from the custom claims
-func GetRoleFromToken(tokenString string) (*string, error) {
- token, err := ParseAccessToken(tokenString)
+func GetRoleFromToken(tokenString string, accessTokenSecret string) (*string, error) {
+ token, err := ParseAccessToken(tokenString, accessTokenSecret)
if err != nil {
return nil, err
}
@@ -159,8 +153,8 @@ func GetRoleFromToken(tokenString string) (*string, error) {
}
// ExtractClaims extracts the claims from the token
-func ExtractAccessClaims(tokenString string) (*types.CustomClaims, *errors.Error) {
- token, err := ParseAccessToken(tokenString)
+func ExtractAccessClaims(tokenString string, accessTokenSecret string) (*types.CustomClaims, *errors.Error) {
+ token, err := ParseAccessToken(tokenString, accessTokenSecret)
if err != nil {
return nil, &errors.FailedToParseAccessToken
}
@@ -174,8 +168,8 @@ func ExtractAccessClaims(tokenString string) (*types.CustomClaims, *errors.Error
}
// ExtractClaims extracts the claims from the token
-func ExtractRefreshClaims(tokenString string) (*jwt.StandardClaims, *errors.Error) {
- token, err := ParseRefreshToken(tokenString)
+func ExtractRefreshClaims(tokenString string, refreshTokenSecret string) (*jwt.StandardClaims, *errors.Error) {
+ token, err := ParseRefreshToken(tokenString, refreshTokenSecret)
if err != nil {
return nil, &errors.FailedToParseRefreshToken
}
diff --git a/backend/src/controllers/auth.go b/backend/src/controllers/auth.go
index d0a4c40e6..546f407e8 100644
--- a/backend/src/controllers/auth.go
+++ b/backend/src/controllers/auth.go
@@ -8,6 +8,7 @@ import (
"github.com/GenerateNU/sac/backend/src/errors"
"github.com/GenerateNU/sac/backend/src/models"
"github.com/GenerateNU/sac/backend/src/services"
+ "github.com/GenerateNU/sac/backend/src/types"
"github.com/GenerateNU/sac/backend/src/utilities"
"github.com/gofiber/fiber/v2"
)
@@ -33,14 +34,10 @@ func NewAuthController(authService services.AuthServiceInterface, authSettings c
// @Failure 401 {string} string "failed to get current user"
// @Router /api/v1/auth/me [get]
func (a *AuthController) Me(c *fiber.Ctx) error {
- // Extract token values from cookies
- accessTokenValue := c.Cookies("access_token")
-
- claims, err := auth.ExtractAccessClaims(accessTokenValue)
+ claims, err := types.From(c)
if err != nil {
return err.FiberError(c)
}
-
user, err := a.authService.Me(claims.Issuer)
if err != nil {
return err.FiberError(c)
@@ -66,7 +63,7 @@ func (a *AuthController) Login(c *fiber.Ctx) error {
var userBody models.LoginUserResponseBody
if err := c.BodyParser(&userBody); err != nil {
- errors.FailedToParseRequestBody.FiberError(c)
+ return errors.FailedToParseRequestBody.FiberError(c)
}
user, err := a.authService.Login(userBody)
@@ -80,8 +77,8 @@ func (a *AuthController) Login(c *fiber.Ctx) error {
}
// Set the tokens in the response
- c.Cookie(auth.CreateCookie("access_token", *accessToken, time.Now().Add(time.Minute*60)))
- c.Cookie(auth.CreateCookie("refresh_token", *refreshToken, time.Now().Add(time.Hour*24*30)))
+ c.Cookie(auth.CreateCookie("access_token", *accessToken, time.Now().Add(time.Minute*time.Duration(a.AuthSettings.AccessTokenExpiry))))
+ c.Cookie(auth.CreateCookie("refresh_token", *refreshToken, time.Now().Add(time.Hour*time.Duration(a.AuthSettings.RefreshTokenExpiry))))
return utilities.FiberMessage(c, fiber.StatusOK, "success")
}
@@ -102,7 +99,7 @@ func (a *AuthController) Refresh(c *fiber.Ctx) error {
refreshTokenValue := c.Cookies("refresh_token")
// Extract id from refresh token
- claims, err := auth.ExtractRefreshClaims(refreshTokenValue)
+ claims, err := auth.ExtractRefreshClaims(refreshTokenValue, a.AuthSettings.RefreshToken)
if err != nil {
return err.FiberError(c)
}
@@ -112,7 +109,7 @@ func (a *AuthController) Refresh(c *fiber.Ctx) error {
return err.FiberError(c)
}
- accessToken, err := auth.RefreshAccessToken(refreshTokenValue, string(*role), a.AuthSettings.AccessTokenExpiry, a.AuthSettings.AccessToken)
+ accessToken, err := auth.RefreshAccessToken(refreshTokenValue, string(*role), a.AuthSettings.RefreshToken, a.AuthSettings.AccessTokenExpiry, a.AuthSettings.AccessToken)
if err != nil {
return err.FiberError(c)
}
diff --git a/backend/src/controllers/category.go b/backend/src/controllers/category.go
index 317e312bc..c06eef82d 100644
--- a/backend/src/controllers/category.go
+++ b/backend/src/controllers/category.go
@@ -81,7 +81,7 @@ func (t *CategoryController) GetCategories(c *fiber.Ctx) error {
// @Failure 500 {string} string "failed to retrieve category"
// @Router /api/v1/category/{id} [get]
func (t *CategoryController) GetCategory(c *fiber.Ctx) error {
- category, err := t.categoryService.GetCategory(c.Params("id"))
+ category, err := t.categoryService.GetCategory(c.Params("categoryID"))
if err != nil {
return err.FiberError(c)
}
@@ -102,7 +102,7 @@ func (t *CategoryController) GetCategory(c *fiber.Ctx) error {
// @Failure 500 {string} string "failed to delete category"
// @Router /api/v1/category/{id} [delete]
func (t *CategoryController) DeleteCategory(c *fiber.Ctx) error {
- if err := t.categoryService.DeleteCategory(c.Params("id")); err != nil {
+ if err := t.categoryService.DeleteCategory(c.Params("categoryID")); err != nil {
return err.FiberError(c)
}
@@ -128,7 +128,7 @@ func (t *CategoryController) UpdateCategory(c *fiber.Ctx) error {
return errors.FailedToValidateCategory.FiberError(c)
}
- updatedCategory, err := t.categoryService.UpdateCategory(c.Params("id"), category)
+ updatedCategory, err := t.categoryService.UpdateCategory(c.Params("categoryID"), category)
if err != nil {
return err.FiberError(c)
}
diff --git a/backend/src/controllers/club.go b/backend/src/controllers/club.go
index 4eab57c55..e9b3d433d 100644
--- a/backend/src/controllers/club.go
+++ b/backend/src/controllers/club.go
@@ -17,11 +17,11 @@ func NewClubController(clubService services.ClubServiceInterface) *ClubControlle
return &ClubController{clubService: clubService}
}
-func (l *ClubController) GetAllClubs(c *fiber.Ctx) error {
+func (cl *ClubController) GetAllClubs(c *fiber.Ctx) error {
defaultLimit := 10
defaultPage := 1
- clubs, err := l.clubService.GetClubs(c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage)))
+ clubs, err := cl.clubService.GetClubs(c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage)))
if err != nil {
return err.FiberError(c)
}
@@ -29,13 +29,13 @@ func (l *ClubController) GetAllClubs(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(clubs)
}
-func (l *ClubController) CreateClub(c *fiber.Ctx) error {
+func (cl *ClubController) CreateClub(c *fiber.Ctx) error {
var clubBody models.CreateClubRequestBody
if err := c.BodyParser(&clubBody); err != nil {
return errors.FailedToParseRequestBody.FiberError(c)
}
- club, err := l.clubService.CreateClub(clubBody)
+ club, err := cl.clubService.CreateClub(clubBody)
if err != nil {
return err.FiberError(c)
}
@@ -43,8 +43,8 @@ func (l *ClubController) CreateClub(c *fiber.Ctx) error {
return c.Status(fiber.StatusCreated).JSON(club)
}
-func (l *ClubController) GetClub(c *fiber.Ctx) error {
- club, err := l.clubService.GetClub(c.Params("id"))
+func (cl *ClubController) GetClub(c *fiber.Ctx) error {
+ club, err := cl.clubService.GetClub(c.Params("clubID"))
if err != nil {
return err.FiberError(c)
}
@@ -52,14 +52,14 @@ func (l *ClubController) GetClub(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(club)
}
-func (l *ClubController) UpdateClub(c *fiber.Ctx) error {
+func (cl *ClubController) UpdateClub(c *fiber.Ctx) error {
var clubBody models.UpdateClubRequestBody
if err := c.BodyParser(&clubBody); err != nil {
return errors.FailedToParseRequestBody.FiberError(c)
}
- updatedClub, err := l.clubService.UpdateClub(c.Params("id"), clubBody)
+ updatedClub, err := cl.clubService.UpdateClub(c.Params("clubID"), clubBody)
if err != nil {
return err.FiberError(c)
}
@@ -67,8 +67,8 @@ func (l *ClubController) UpdateClub(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(updatedClub)
}
-func (l *ClubController) DeleteClub(c *fiber.Ctx) error {
- err := l.clubService.DeleteClub(c.Params("id"))
+func (cl *ClubController) DeleteClub(c *fiber.Ctx) error {
+ err := cl.clubService.DeleteClub(c.Params("clubID"))
if err != nil {
return err.FiberError(c)
}
diff --git a/backend/src/controllers/user.go b/backend/src/controllers/user.go
index 1b509bf3d..60746d3dd 100644
--- a/backend/src/controllers/user.go
+++ b/backend/src/controllers/user.go
@@ -81,7 +81,7 @@ func (u *UserController) GetUsers(c *fiber.Ctx) error {
// @Failure 500 {string} string "failed to get user"
// @Router /api/v1/users/:id [get]
func (u *UserController) GetUser(c *fiber.Ctx) error {
- user, err := u.userService.GetUser(c.Params("id"))
+ user, err := u.userService.GetUser(c.Params("userID"))
if err != nil {
return err.FiberError(c)
}
@@ -110,7 +110,7 @@ func (u *UserController) UpdateUser(c *fiber.Ctx) error {
return errors.FailedToParseRequestBody.FiberError(c)
}
- updatedUser, err := u.userService.UpdateUser(c.Params("id"), user)
+ updatedUser, err := u.userService.UpdateUser(c.Params("userID"), user)
if err != nil {
return err.FiberError(c)
}
@@ -130,7 +130,7 @@ func (u *UserController) UpdateUser(c *fiber.Ctx) error {
// @Failure 500 {string} string "failed to get all users"
// @Router /api/v1/users/:id [delete]
func (u *UserController) DeleteUser(c *fiber.Ctx) error {
- err := u.userService.DeleteUser(c.Params("id"))
+ err := u.userService.DeleteUser(c.Params("userID"))
if err != nil {
return err.FiberError(c)
}
diff --git a/backend/src/database/db.go b/backend/src/database/db.go
index 32014e066..35ad1d868 100644
--- a/backend/src/database/db.go
+++ b/backend/src/database/db.go
@@ -1,7 +1,6 @@
package database
import (
- "github.com/GenerateNU/sac/backend/src/auth"
"github.com/GenerateNU/sac/backend/src/config"
"github.com/GenerateNU/sac/backend/src/models"
@@ -11,21 +10,44 @@ import (
)
func ConfigureDB(settings config.Settings) (*gorm.DB, error) {
- db, err := gorm.Open(postgres.Open(settings.Database.WithDb()), &gorm.Config{
- Logger: logger.Default.LogMode(logger.Info),
- SkipDefaultTransaction: true,
- TranslateError: true,
- })
+ db, err := EstablishConn(settings.Database.WithDb(), WithLoggerInfo())
if err != nil {
return nil, err
}
- err = db.Exec("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"").Error
+ if err := MigrateDB(settings, db); err != nil {
+ return nil, err
+ }
+
+ return db, nil
+}
+
+type OptionalFunc func(gorm.Config) gorm.Config
+
+func WithLoggerInfo() OptionalFunc {
+ return func(gormConfig gorm.Config) gorm.Config {
+ gormConfig.Logger = logger.Default.LogMode(logger.Info)
+ return gormConfig
+ }
+}
+
+func EstablishConn(dsn string, opts ...OptionalFunc) (*gorm.DB, error) {
+ rootConfig := gorm.Config{
+ SkipDefaultTransaction: true,
+ TranslateError: true,
+ }
+
+ for _, opt := range opts {
+ rootConfig = opt(rootConfig)
+ }
+
+ db, err := gorm.Open(postgres.Open(dsn), &rootConfig)
if err != nil {
return nil, err
}
- if err := MigrateDB(settings, db); err != nil {
+ err = db.Exec("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"").Error
+ if err != nil {
return nil, err
}
@@ -78,22 +100,12 @@ func createSuperUser(settings config.Settings, db *gorm.DB) error {
return err
}
- passwordHash, err := auth.ComputePasswordHash(settings.SuperUser.Password)
+ superUser, err := SuperUser(settings.SuperUser)
if err != nil {
+ tx.Rollback()
return err
}
- superUser := models.User{
- Role: models.Super,
- NUID: "000000000",
- Email: "generatesac@gmail.com",
- PasswordHash: *passwordHash,
- FirstName: "SAC",
- LastName: "Super",
- College: models.KCCS,
- Year: models.First,
- }
-
var user models.User
if err := db.Where("nuid = ?", superUser.NUID).First(&user).Error; err != nil {
@@ -108,17 +120,9 @@ func createSuperUser(settings config.Settings, db *gorm.DB) error {
return err
}
- superClub := models.Club{
- Name: "SAC",
- Preview: "SAC",
- Description: "SAC",
- NumMembers: 0,
- IsRecruiting: true,
- RecruitmentCycle: models.RecruitmentCycle(models.Always),
- RecruitmentType: models.Application,
- ApplicationLink: "https://generatenu.com/apply",
- Logo: "https://aws.amazon.com/s3",
- }
+ SuperUserUUID = superUser.ID
+
+ superClub := SuperClub()
if err := tx.Create(&superClub).Error; err != nil {
tx.Rollback()
diff --git a/backend/src/database/super.go b/backend/src/database/super.go
new file mode 100644
index 000000000..6ad3f930f
--- /dev/null
+++ b/backend/src/database/super.go
@@ -0,0 +1,43 @@
+package database
+
+import (
+ "github.com/GenerateNU/sac/backend/src/auth"
+ "github.com/GenerateNU/sac/backend/src/config"
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/google/uuid"
+)
+
+var SuperUserUUID uuid.UUID
+
+func SuperUser(superUserSettings config.SuperUserSettings) (*models.User, *errors.Error) {
+ passwordHash, err := auth.ComputePasswordHash(superUserSettings.Password)
+ if err != nil {
+ return nil, &errors.FailedToComputePasswordHash
+ }
+
+ return &models.User{
+ Role: models.Super,
+ NUID: "000000000",
+ Email: "generatesac@gmail.com",
+ PasswordHash: *passwordHash,
+ FirstName: "SAC",
+ LastName: "Super",
+ College: models.KCCS,
+ Year: models.First,
+ }, nil
+}
+
+func SuperClub() models.Club {
+ return models.Club{
+ Name: "SAC",
+ Preview: "SAC",
+ Description: "SAC",
+ NumMembers: 0,
+ IsRecruiting: true,
+ RecruitmentCycle: models.RecruitmentCycle(models.Always),
+ RecruitmentType: models.Application,
+ ApplicationLink: "https://generatenu.com/apply",
+ Logo: "https://aws.amazon.com/s3",
+ }
+}
diff --git a/backend/src/errors/auth.go b/backend/src/errors/auth.go
new file mode 100644
index 000000000..e53e5f03d
--- /dev/null
+++ b/backend/src/errors/auth.go
@@ -0,0 +1,14 @@
+package errors
+
+import "github.com/gofiber/fiber/v2"
+
+var (
+ PassedAuthenticateMiddlewareButNilClaims = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "passed authenticate middleware but claims is nil",
+ }
+ FailedToCastToCustomClaims = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "failed to cast to custom claims",
+ }
+)
diff --git a/backend/src/errors/common.go b/backend/src/errors/common.go
index a42f9b4fd..343594d5b 100644
--- a/backend/src/errors/common.go
+++ b/backend/src/errors/common.go
@@ -63,8 +63,4 @@ var (
StatusCode: fiber.StatusUnauthorized,
Message: "failed to validate access token",
}
- FailedToParseUUID = Error{
- StatusCode: fiber.StatusBadRequest,
- Message: "failed to parse uuid",
- }
)
diff --git a/backend/src/main.go b/backend/src/main.go
index a165ca95d..9ace36db9 100644
--- a/backend/src/main.go
+++ b/backend/src/main.go
@@ -1,7 +1,6 @@
package main
import (
- "flag"
"fmt"
"path/filepath"
@@ -19,12 +18,7 @@ import (
// @host 127.0.0.1:8080
// @BasePath /api/v1
func main() {
- onlyMigrate := flag.Bool("only-migrate", false, "Specify if you want to only perform the database migration")
- configPath := flag.String("config", filepath.Join("..", "..", "config"), "Specify the path to the config directory")
-
- flag.Parse()
-
- config, err := config.GetConfiguration(*configPath)
+ config, err := config.GetConfiguration(filepath.Join("..", "..", "config"))
if err != nil {
panic(fmt.Sprintf("Error getting configuration: %s", err.Error()))
}
@@ -34,10 +28,6 @@ func main() {
panic(fmt.Sprintf("Error configuring database: %s", err.Error()))
}
- if *onlyMigrate {
- return
- }
-
err = database.ConnPooling(db)
if err != nil {
panic(err)
diff --git a/backend/src/middleware/auth.go b/backend/src/middleware/auth.go
index 0900eeb1e..07fd76a61 100644
--- a/backend/src/middleware/auth.go
+++ b/backend/src/middleware/auth.go
@@ -9,6 +9,7 @@ import (
"github.com/GenerateNU/sac/backend/src/types"
"github.com/gofiber/fiber/v2"
+ "github.com/gofiber/fiber/v2/middleware/skip"
)
var paths = []string{
@@ -18,17 +19,31 @@ var paths = []string{
"/api/v1/auth/logout",
}
+func SuperSkipper(h fiber.Handler) fiber.Handler {
+ return skip.New(h, func(c *fiber.Ctx) bool {
+ claims, err := types.From(c)
+ if err != nil {
+ err.FiberError(c)
+ return false
+ }
+ if claims == nil {
+ return false
+ }
+ return claims.Role == string(models.Super)
+ })
+}
+
func (m *MiddlewareService) Authenticate(c *fiber.Ctx) error {
if slices.Contains(paths, c.Path()) {
return c.Next()
}
- token, err := auth.ParseAccessToken(c.Cookies("access_token"))
+ token, err := auth.ParseAccessToken(c.Cookies("access_token"), m.AuthSettings.AccessToken)
if err != nil {
return errors.FailedToParseAccessToken.FiberError(c)
}
- _, ok := token.Claims.(*types.CustomClaims)
+ claims, ok := token.Claims.(*types.CustomClaims)
if !ok || !token.Valid {
return errors.FailedToValidateAccessToken.FiberError(c)
}
@@ -37,12 +52,23 @@ func (m *MiddlewareService) Authenticate(c *fiber.Ctx) error {
return errors.Unauthorized.FiberError(c)
}
+ c.Locals("claims", claims)
+
return c.Next()
}
func (m *MiddlewareService) Authorize(requiredPermissions ...types.Permission) func(c *fiber.Ctx) error {
return func(c *fiber.Ctx) error {
- role, err := auth.GetRoleFromToken(c.Cookies("access_token"))
+ claims, fromErr := types.From(c)
+ if fromErr != nil {
+ return fromErr.FiberError(c)
+ }
+
+ if claims != nil && claims.Role == string(models.Super) {
+ return c.Next()
+ }
+
+ role, err := auth.GetRoleFromToken(c.Cookies("access_token"), m.AuthSettings.AccessToken)
if err != nil {
return errors.FailedToParseAccessToken.FiberError(c)
}
diff --git a/backend/src/middleware/club.go b/backend/src/middleware/club.go
index c8529415e..c64fa7068 100644
--- a/backend/src/middleware/club.go
+++ b/backend/src/middleware/club.go
@@ -12,12 +12,12 @@ import (
)
func (m *MiddlewareService) ClubAuthorizeById(c *fiber.Ctx) error {
- clubUUID, err := utilities.ValidateID(c.Params("id"))
+ clubUUID, err := utilities.ValidateID(c.Params("clubID"))
if err != nil {
- return errors.FailedToParseUUID.FiberError(c)
+ return errors.FailedToValidateID.FiberError(c)
}
- token, tokenErr := auth.ParseAccessToken(c.Cookies("access_token"))
+ token, tokenErr := auth.ParseAccessToken(c.Cookies("access_token"), m.AuthSettings.AccessToken)
if tokenErr != nil {
return errors.FailedToParseAccessToken.FiberError(c)
}
diff --git a/backend/src/middleware/middleware.go b/backend/src/middleware/middleware.go
index 97107b9bb..3f1169083 100644
--- a/backend/src/middleware/middleware.go
+++ b/backend/src/middleware/middleware.go
@@ -1,6 +1,7 @@
package middleware
import (
+ "github.com/GenerateNU/sac/backend/src/config"
"github.com/GenerateNU/sac/backend/src/types"
"github.com/go-playground/validator/v10"
"github.com/gofiber/fiber/v2"
@@ -15,13 +16,15 @@ type MiddlewareInterface interface {
}
type MiddlewareService struct {
- DB *gorm.DB
- Validate *validator.Validate
+ DB *gorm.DB
+ Validate *validator.Validate
+ AuthSettings config.AuthSettings
}
-func NewMiddlewareService(db *gorm.DB, validate *validator.Validate) *MiddlewareService {
+func NewMiddlewareService(db *gorm.DB, validate *validator.Validate, authSettings config.AuthSettings) *MiddlewareService {
return &MiddlewareService{
- DB: db,
- Validate: validate,
+ DB: db,
+ Validate: validate,
+ AuthSettings: authSettings,
}
}
diff --git a/backend/src/middleware/user.go b/backend/src/middleware/user.go
index 38ee0e3e0..308372a4f 100644
--- a/backend/src/middleware/user.go
+++ b/backend/src/middleware/user.go
@@ -9,12 +9,12 @@ import (
)
func (m *MiddlewareService) UserAuthorizeById(c *fiber.Ctx) error {
- idAsUUID, err := utilities.ValidateID(c.Params("id"))
+ idAsUUID, err := utilities.ValidateID(c.Params("userID"))
if err != nil {
- return errors.FailedToParseUUID.FiberError(c)
+ return errors.FailedToValidateID.FiberError(c)
}
- token, tokenErr := auth.ParseAccessToken(c.Cookies("access_token"))
+ token, tokenErr := auth.ParseAccessToken(c.Cookies("access_token"), m.AuthSettings.AccessToken)
if tokenErr != nil {
return err
}
@@ -26,7 +26,7 @@ func (m *MiddlewareService) UserAuthorizeById(c *fiber.Ctx) error {
issuerIDAsUUID, err := utilities.ValidateID(claims.Issuer)
if err != nil {
- return errors.FailedToParseUUID.FiberError(c)
+ return errors.FailedToValidateID.FiberError(c)
}
if issuerIDAsUUID.String() == idAsUUID.String() {
diff --git a/backend/src/models/club.go b/backend/src/models/club.go
index 919d30fb6..3c56614d6 100644
--- a/backend/src/models/club.go
+++ b/backend/src/models/club.go
@@ -40,6 +40,7 @@ type Club struct {
Parent *uuid.UUID `gorm:"foreignKey:Parent" json:"-" validate:"uuid4"`
Tag []Tag `gorm:"many2many:club_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
// User
+ Admin []User `gorm:"many2many:user_club_admins;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"required"`
Member []User `gorm:"many2many:user_club_members;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"required"`
Follower []User `gorm:"many2many:user_club_followers;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
IntendedApplicant []User `gorm:"many2many:user_club_intended_applicants;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
@@ -56,7 +57,6 @@ type CreateClubRequestBody struct {
Name string `json:"name" validate:"required,max=255"`
Preview string `json:"preview" validate:"required,max=255"`
Description string `json:"description" validate:"required,http_url,mongo_url,max=255"` // MongoDB URL
- NumMembers int `json:"num_members" validate:"required,min=1"`
IsRecruiting bool `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"`
@@ -68,7 +68,6 @@ type UpdateClubRequestBody struct {
Name string `json:"name" validate:"omitempty,max=255"`
Preview string `json:"preview" validate:"omitempty,max=255"`
Description string `json:"description" validate:"omitempty,http_url,mongo_url,max=255"` // MongoDB URL
- NumMembers int `json:"num_members" validate:"omitempty,min=1"`
IsRecruiting bool `json:"is_recruiting" validate:"omitempty"`
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"`
diff --git a/backend/src/models/user.go b/backend/src/models/user.go
index 46eee1311..fefd911b6 100644
--- a/backend/src/models/user.go
+++ b/backend/src/models/user.go
@@ -4,7 +4,7 @@ import "github.com/google/uuid"
type UserRole string
-const (
+var (
Super UserRole = "super"
Student UserRole = "student"
)
@@ -47,6 +47,7 @@ type User struct {
Year Year `gorm:"type:smallint" json:"year" validate:"required,min=1,max=6"`
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:"-"`
Member []Club `gorm:"many2many:user_club_members;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
Follower []Club `gorm:"many2many:user_club_followers;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
IntendedApplicant []Club `gorm:"many2many:user_club_intended_applicants;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
@@ -71,7 +72,6 @@ type UpdateUserRequestBody struct {
FirstName string `json:"first_name" validate:"omitempty,max=255"`
LastName string `json:"last_name" validate:"omitempty,max=255"`
Email string `json:"email" validate:"omitempty,email,neu_email,max=255"`
- Password string `json:"password" validate:"omitempty,password"`
College College `json:"college" validate:"omitempty,oneof=CAMD DMSB KCCS CE BCHS SL CPS CS CSSH"`
Year Year `json:"year" validate:"omitempty,min=1,max=6"`
}
diff --git a/backend/src/server/routes/auth.go b/backend/src/server/routes/auth.go
new file mode 100644
index 000000000..7efe8d127
--- /dev/null
+++ b/backend/src/server/routes/auth.go
@@ -0,0 +1,20 @@
+package routes
+
+import (
+ "github.com/GenerateNU/sac/backend/src/config"
+ "github.com/GenerateNU/sac/backend/src/controllers"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+func Auth(router fiber.Router, authService services.AuthServiceInterface, authSettings config.AuthSettings) {
+ authController := controllers.NewAuthController(authService, authSettings)
+
+ // api/v1/auth/*
+ auth := router.Group("/auth")
+
+ auth.Post("/login", authController.Login)
+ auth.Get("/logout", authController.Logout)
+ auth.Get("/refresh", authController.Refresh)
+ auth.Get("/me", authController.Me)
+}
diff --git a/backend/src/server/routes/category.go b/backend/src/server/routes/category.go
new file mode 100644
index 000000000..1bffb59f2
--- /dev/null
+++ b/backend/src/server/routes/category.go
@@ -0,0 +1,21 @@
+package routes
+
+import (
+ "github.com/GenerateNU/sac/backend/src/controllers"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+func Category(router fiber.Router, categoryService services.CategoryServiceInterface) fiber.Router {
+ categoryController := controllers.NewCategoryController(categoryService)
+
+ categories := router.Group("/categories")
+
+ categories.Post("/", categoryController.CreateCategory)
+ categories.Get("/", categoryController.GetCategories)
+ categories.Get("/:categoryID", categoryController.GetCategory)
+ categories.Delete("/:categoryID", categoryController.DeleteCategory)
+ categories.Patch("/:categoryID", categoryController.UpdateCategory)
+
+ return categories
+}
diff --git a/backend/src/server/routes/category_tag.go b/backend/src/server/routes/category_tag.go
new file mode 100644
index 000000000..7720e7bb5
--- /dev/null
+++ b/backend/src/server/routes/category_tag.go
@@ -0,0 +1,16 @@
+package routes
+
+import (
+ "github.com/GenerateNU/sac/backend/src/controllers"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+func CategoryTag(router fiber.Router, categoryTagService services.CategoryTagServiceInterface) {
+ categoryTagController := controllers.NewCategoryTagController(categoryTagService)
+
+ categoryTags := router.Group("/:categoryID/tags")
+
+ categoryTags.Get("/", categoryTagController.GetTagsByCategory)
+ categoryTags.Get("/:tagID", categoryTagController.GetTagByCategory)
+}
diff --git a/backend/src/server/routes/club.go b/backend/src/server/routes/club.go
new file mode 100644
index 000000000..91d2e440a
--- /dev/null
+++ b/backend/src/server/routes/club.go
@@ -0,0 +1,26 @@
+package routes
+
+import (
+ "github.com/GenerateNU/sac/backend/src/controllers"
+ "github.com/GenerateNU/sac/backend/src/middleware"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/GenerateNU/sac/backend/src/types"
+ "github.com/gofiber/fiber/v2"
+)
+
+func Club(router fiber.Router, clubService services.ClubServiceInterface, middlewareService middleware.MiddlewareInterface) {
+ clubController := controllers.NewClubController(clubService)
+
+ clubs := router.Group("/clubs")
+
+ clubs.Get("/", middlewareService.Authorize(types.ClubReadAll), clubController.GetAllClubs)
+ clubs.Post("/", clubController.CreateClub)
+
+ // api/v1/clubs/:clubID/*
+ clubsID := clubs.Group("/:clubID")
+ clubsID.Use(middleware.SuperSkipper(middlewareService.UserAuthorizeById))
+
+ clubsID.Get("/", clubController.GetClub)
+ clubsID.Patch("/", middlewareService.Authorize(types.ClubWrite), clubController.UpdateClub)
+ clubsID.Delete("/", middleware.SuperSkipper(middlewareService.Authorize(types.ClubDelete)), clubController.DeleteClub)
+}
diff --git a/backend/src/server/routes/tag.go b/backend/src/server/routes/tag.go
new file mode 100644
index 000000000..6bd9bf8b8
--- /dev/null
+++ b/backend/src/server/routes/tag.go
@@ -0,0 +1,18 @@
+package routes
+
+import (
+ "github.com/GenerateNU/sac/backend/src/controllers"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+func Tag(router fiber.Router, tagService services.TagServiceInterface) {
+ tagController := controllers.NewTagController(tagService)
+
+ tags := router.Group("/tags")
+
+ tags.Get("/:tagID", tagController.GetTag)
+ tags.Post("/", tagController.CreateTag)
+ tags.Patch("/:tagID", tagController.UpdateTag)
+ tags.Delete("/:tagID", tagController.DeleteTag)
+}
diff --git a/backend/src/server/routes/user.go b/backend/src/server/routes/user.go
new file mode 100644
index 000000000..2926e875c
--- /dev/null
+++ b/backend/src/server/routes/user.go
@@ -0,0 +1,28 @@
+package routes
+
+import (
+ "github.com/GenerateNU/sac/backend/src/controllers"
+ "github.com/GenerateNU/sac/backend/src/middleware"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/GenerateNU/sac/backend/src/types"
+ "github.com/gofiber/fiber/v2"
+)
+
+func User(router fiber.Router, userService services.UserServiceInterface, middlewareService middleware.MiddlewareInterface) fiber.Router {
+ userController := controllers.NewUserController(userService)
+
+ // api/v1/users/*
+ users := router.Group("/users")
+ users.Post("/", userController.CreateUser)
+ users.Get("/", middleware.SuperSkipper(middlewareService.Authorize(types.UserReadAll)), userController.GetUsers)
+
+ // api/v1/users/:userID/*
+ usersID := users.Group("/:userID")
+ usersID.Use(middleware.SuperSkipper(middlewareService.UserAuthorizeById))
+
+ usersID.Get("/", userController.GetUser)
+ usersID.Patch("/", userController.UpdateUser)
+ usersID.Delete("/", userController.DeleteUser)
+
+ return users
+}
diff --git a/backend/src/server/routes/user_tag.go b/backend/src/server/routes/user_tag.go
new file mode 100644
index 000000000..1b777ff47
--- /dev/null
+++ b/backend/src/server/routes/user_tag.go
@@ -0,0 +1,16 @@
+package routes
+
+import (
+ "github.com/GenerateNU/sac/backend/src/controllers"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+func UserTag(router fiber.Router, userTagService services.UserTagServiceInterface) {
+ userTagController := controllers.NewUserTagController(userTagService)
+
+ userTags := router.Group("/:userID/tags")
+
+ userTags.Post("/", userTagController.CreateUserTags)
+ userTags.Get("/", userTagController.GetUserTags)
+}
diff --git a/backend/src/server/routes/utility.go b/backend/src/server/routes/utility.go
new file mode 100644
index 000000000..5730a021d
--- /dev/null
+++ b/backend/src/server/routes/utility.go
@@ -0,0 +1,13 @@
+package routes
+
+import (
+ "github.com/gofiber/fiber/v2"
+ "github.com/gofiber/swagger"
+)
+
+func Utility(router fiber.Router) {
+ router.Get("/swagger/*", swagger.HandlerDefault)
+ router.Get("/health", func(c *fiber.Ctx) error {
+ return c.SendStatus(200)
+ })
+}
diff --git a/backend/src/server/server.go b/backend/src/server/server.go
index 3b892d65f..f65b70c23 100644
--- a/backend/src/server/server.go
+++ b/backend/src/server/server.go
@@ -2,10 +2,9 @@ package server
import (
"github.com/GenerateNU/sac/backend/src/config"
- "github.com/GenerateNU/sac/backend/src/controllers"
"github.com/GenerateNU/sac/backend/src/middleware"
+ "github.com/GenerateNU/sac/backend/src/server/routes"
"github.com/GenerateNU/sac/backend/src/services"
- "github.com/GenerateNU/sac/backend/src/types"
"github.com/GenerateNU/sac/backend/src/utilities"
"github.com/goccy/go-json"
@@ -13,7 +12,6 @@ import (
"github.com/gofiber/fiber/v2/middleware/cors"
"github.com/gofiber/fiber/v2/middleware/logger"
"github.com/gofiber/fiber/v2/middleware/requestid"
- "github.com/gofiber/swagger"
"gorm.io/gorm"
)
@@ -29,19 +27,24 @@ func Init(db *gorm.DB, settings config.Settings) *fiber.App {
app := newFiberApp()
validate := utilities.RegisterCustomValidators()
- middlewareService := middleware.NewMiddlewareService(db, validate)
+ middlewareService := middleware.NewMiddlewareService(db, validate, settings.Auth)
apiv1 := app.Group("/api/v1")
apiv1.Use(middlewareService.Authenticate)
- utilityRoutes(app)
- authRoutes(apiv1, services.NewAuthService(db, validate), settings.Auth)
- userRouter := userRoutes(apiv1, services.NewUserService(db, validate), middlewareService)
- userTagRouter(userRouter, services.NewUserTagService(db, validate))
- clubRoutes(apiv1, services.NewClubService(db, validate), middlewareService)
- categoryRouter := categoryRoutes(apiv1, services.NewCategoryService(db, validate))
- tagRoutes(apiv1, services.NewTagService(db, validate))
- categoryTagRoutes(categoryRouter, services.NewCategoryTagService(db, validate))
+ routes.Utility(app)
+
+ routes.Auth(apiv1, services.NewAuthService(db, validate), settings.Auth)
+
+ userRouter := routes.User(apiv1, services.NewUserService(db, validate), middlewareService)
+ routes.UserTag(userRouter, services.NewUserTagService(db, validate))
+
+ routes.Club(apiv1, services.NewClubService(db, validate), middlewareService)
+
+ routes.Tag(apiv1, services.NewTagService(db, validate))
+
+ categoryRouter := routes.Category(apiv1, services.NewCategoryService(db, validate))
+ routes.CategoryTag(categoryRouter, services.NewCategoryTagService(db, validate))
return app
}
@@ -63,105 +66,3 @@ func newFiberApp() *fiber.App {
return app
}
-
-func utilityRoutes(router fiber.Router) {
- router.Get("/swagger/*", swagger.HandlerDefault)
- router.Get("/health", func(c *fiber.Ctx) error {
- return c.SendStatus(200)
- })
-}
-
-func userRoutes(router fiber.Router, userService services.UserServiceInterface, middlewareService middleware.MiddlewareInterface) fiber.Router {
- userController := controllers.NewUserController(userService)
-
- // api/v1/users/*
- users := router.Group("/users")
- users.Post("/", userController.CreateUser)
- users.Get("/", middlewareService.Authorize(types.UserReadAll), userController.GetUsers)
-
- // api/v1/users/:id/*
- usersID := users.Group("/:id")
- usersID.Use(middlewareService.UserAuthorizeById)
-
- usersID.Get("/", middlewareService.Authorize(types.UserRead), userController.GetUser)
- usersID.Patch("/", middlewareService.Authorize(types.UserWrite), userController.UpdateUser)
- usersID.Delete("/", middlewareService.Authorize(types.UserDelete), userController.DeleteUser)
-
- users.Get("/", userController.GetUsers)
- users.Get("/:id", userController.GetUser)
- users.Patch("/:id", userController.UpdateUser)
- users.Delete("/:id", userController.DeleteUser)
-
- return users
-}
-
-func userTagRouter(router fiber.Router, userTagService services.UserTagServiceInterface) {
- userTagController := controllers.NewUserTagController(userTagService)
-
- userTags := router.Group("/:userID/tags")
-
- userTags.Post("/", userTagController.CreateUserTags)
- userTags.Get("/", userTagController.GetUserTags)
-}
-
-func clubRoutes(router fiber.Router, clubService services.ClubServiceInterface, middlewareService middleware.MiddlewareInterface) {
- clubController := controllers.NewClubController(clubService)
-
- clubs := router.Group("/clubs")
-
- clubs.Get("/", middlewareService.Authorize(types.ClubReadAll), clubController.GetAllClubs)
- clubs.Post("/", clubController.CreateClub)
-
- // api/v1/clubs/:id/*
- clubsID := clubs.Group("/:id")
- clubsID.Use(middlewareService.ClubAuthorizeById)
-
- clubsID.Get("/", clubController.GetClub)
- clubsID.Patch("/", middlewareService.Authorize(types.ClubWrite), clubController.UpdateClub)
- clubsID.Delete("/", middlewareService.Authorize(types.ClubDelete), clubController.DeleteClub)
-}
-
-func authRoutes(router fiber.Router, authService services.AuthServiceInterface, authSettings config.AuthSettings) {
- authController := controllers.NewAuthController(authService, authSettings)
-
- // api/v1/auth/*
- auth := router.Group("/auth")
- auth.Post("/login", authController.Login)
- auth.Get("/logout", authController.Logout)
- auth.Get("/refresh", authController.Refresh)
- auth.Get("/me", authController.Me)
-}
-
-func categoryRoutes(router fiber.Router, categoryService services.CategoryServiceInterface) fiber.Router {
- categoryController := controllers.NewCategoryController(categoryService)
-
- categories := router.Group("/categories")
-
- categories.Post("/", categoryController.CreateCategory)
- categories.Get("/", categoryController.GetCategories)
- categories.Get("/:id", categoryController.GetCategory)
- categories.Delete("/:id", categoryController.DeleteCategory)
- categories.Patch("/:id", categoryController.UpdateCategory)
-
- return categories
-}
-
-func tagRoutes(router fiber.Router, tagService services.TagServiceInterface) {
- tagController := controllers.NewTagController(tagService)
-
- tags := router.Group("/tags")
-
- tags.Get("/:tagID", tagController.GetTag)
- tags.Post("/", tagController.CreateTag)
- tags.Patch("/:tagID", tagController.UpdateTag)
- tags.Delete("/:tagID", tagController.DeleteTag)
-}
-
-func categoryTagRoutes(router fiber.Router, categoryTagService services.CategoryTagServiceInterface) {
- categoryTagController := controllers.NewCategoryTagController(categoryTagService)
-
- categoryTags := router.Group("/:categoryID/tags")
-
- categoryTags.Get("/", categoryTagController.GetTagsByCategory)
- categoryTags.Get("/:tagID", categoryTagController.GetTagByCategory)
-}
diff --git a/backend/src/services/user.go b/backend/src/services/user.go
index 646976e09..a940a6b33 100644
--- a/backend/src/services/user.go
+++ b/backend/src/services/user.go
@@ -86,18 +86,11 @@ func (u *UserService) UpdateUser(id string, userBody models.UpdateUserRequestBod
return nil, &errors.FailedToValidateUser
}
- passwordHash, err := auth.ComputePasswordHash(userBody.Password)
- if err != nil {
- return nil, &errors.FailedToComputePasswordHash
- }
-
user, err := utilities.MapRequestToModel(userBody, &models.User{})
if err != nil {
return nil, &errors.FailedToMapRequestToModel
}
- user.PasswordHash = *passwordHash
-
return transactions.UpdateUser(u.DB, *idAsUUID, *user)
}
diff --git a/backend/src/transactions/category_tag.go b/backend/src/transactions/category_tag.go
index 7ef3129a7..f827fb557 100644
--- a/backend/src/transactions/category_tag.go
+++ b/backend/src/transactions/category_tag.go
@@ -12,6 +12,15 @@ import (
)
func GetTagsByCategory(db *gorm.DB, categoryID uuid.UUID, limit int, offset int) ([]models.Tag, *errors.Error) {
+ var category models.Category
+
+ if err := db.Where("id = ?", categoryID).First(&category).Error; err != nil {
+ if stdliberrors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, &errors.CategoryNotFound
+ }
+ return nil, &errors.FailedToGetCategory
+ }
+
var tags []models.Tag
if err := db.Where("category_id = ?", categoryID).Limit(limit).Offset(offset).Find(&tags).Error; err != nil {
return nil, &errors.FailedToGetTags
diff --git a/backend/src/types/custom_claims.go b/backend/src/types/custom_claims.go
index b53da552e..c0351e0a0 100644
--- a/backend/src/types/custom_claims.go
+++ b/backend/src/types/custom_claims.go
@@ -1,8 +1,26 @@
package types
-import "github.com/golang-jwt/jwt"
+import (
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/gofiber/fiber/v2"
+ "github.com/golang-jwt/jwt"
+)
type CustomClaims struct {
jwt.StandardClaims
Role string `json:"role"`
}
+
+func From(c *fiber.Ctx) (*CustomClaims, *errors.Error) {
+ rawClaims := c.Locals("claims")
+ if rawClaims == nil {
+ return nil, nil
+ }
+
+ claims, ok := rawClaims.(*CustomClaims)
+ if !ok {
+ return nil, &errors.FailedToCastToCustomClaims
+ }
+
+ return claims, nil
+}
diff --git a/backend/src/types/permissions.go b/backend/src/types/permissions.go
index f99fff06f..8361f42eb 100644
--- a/backend/src/types/permissions.go
+++ b/backend/src/types/permissions.go
@@ -61,7 +61,7 @@ const (
var rolePermissions = map[models.UserRole][]Permission{
models.Super: {
- UserRead, UserWrite, UserDelete,
+ UserRead, UserReadAll, UserWrite, UserDelete,
TagRead, TagCreate, TagWrite, TagDelete,
ClubRead, ClubCreate, ClubWrite, ClubDelete,
PointOfContactRead, PointOfContactCreate, PointOfContactWrite, PointOfContactDelete,
diff --git a/backend/tests/README.md b/backend/tests/README.md
index fe7b41759..b2b9c431a 100644
--- a/backend/tests/README.md
+++ b/backend/tests/README.md
@@ -53,7 +53,7 @@ TestRequest{
Path: "/api/v1/categories/",
Body: SampleCategoryFactory(),
}.TestOnStatusAndDB(t, nil,
- DBTesterWithStatus{
+ TesterWithStatus{
Status: fiber.StatusCreated,
DBTester: AssertSampleCategoryBodyRespDB,
},
@@ -62,7 +62,7 @@ TestRequest{
### DBTesters
-Often times there are common assertions you want to make about the database, for example, if the object in the response is the same as the object in the database. We can create a lambda function that takes in the `TestApp`, `*assert.A`, and `*http.Response` and makes the assertions we want. We can then pass this function to the `DBTesterWithStatus` struct.
+Often times there are common assertions you want to make about the database, for example, if the object in the response is the same as the object in the database. We can create a lambda function that takes in the `TestApp`, `*assert.A`, and `*http.Response` and makes the assertions we want. We can then pass this function to the `TesterWithStatus` struct.
```go
func AssertSampleCategoryBodyRespDB(app TestApp, assert *assert.A, resp *http.Response) {
diff --git a/backend/tests/api/category_tag_test.go b/backend/tests/api/category_tag_test.go
index 123e7bb26..cdba4b6c9 100644
--- a/backend/tests/api/category_tag_test.go
+++ b/backend/tests/api/category_tag_test.go
@@ -7,13 +7,15 @@ import (
"github.com/GenerateNU/sac/backend/src/errors"
"github.com/GenerateNU/sac/backend/src/models"
+ h "github.com/GenerateNU/sac/backend/tests/api/helpers"
+
"github.com/goccy/go-json"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"github.com/huandu/go-assert"
)
-func AssertTagsWithBodyRespDB(app TestApp, assert *assert.A, resp *http.Response, body *[]map[string]interface{}) []uuid.UUID {
+func AssertTagsWithBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, body *[]map[string]interface{}) []uuid.UUID {
var respTags []models.Tag
err := json.NewDecoder(resp.Body).Decode(&respTags)
@@ -43,18 +45,20 @@ func AssertTagsWithBodyRespDB(app TestApp, assert *assert.A, resp *http.Response
}
func TestGetCategoryTagsWorks(t *testing.T) {
- appAssert, categoryUUID, tagID := CreateSampleTag(t)
+ appAssert, categoryUUID, tagID := CreateSampleTag(h.InitTest(t))
body := SampleTagFactory(categoryUUID)
(*body)["id"] = tagID
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags", categoryUUID),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
+ appAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags", categoryUUID),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
Status: fiber.StatusOK,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertTagsWithBodyRespDB(app, assert, resp, &[]map[string]interface{}{*body})
},
},
@@ -62,7 +66,7 @@ func TestGetCategoryTagsWorks(t *testing.T) {
}
func TestGetCategoryTagsFailsCategoryBadRequest(t *testing.T) {
- appAssert, _ := CreateSampleCategory(t, nil)
+ appAssert, _ := CreateSampleCategory(h.InitTest(t))
badRequests := []string{
"0",
@@ -73,48 +77,52 @@ func TestGetCategoryTagsFailsCategoryBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags", badRequest),
- }.TestOnError(t, &appAssert, errors.FailedToValidateID)
+ appAssert.TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags", badRequest),
+ Role: &models.Super,
+ },
+ errors.FailedToValidateID,
+ )
}
appAssert.Close()
}
func TestGetCategoryTagsFailsCategoryNotFound(t *testing.T) {
- appAssert, _ := CreateSampleCategory(t, nil)
+ appAssert, _ := CreateSampleCategory(h.InitTest(t))
uuid := uuid.New()
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags", uuid),
- }.TestOnErrorAndDB(t, &appAssert, ErrorWithDBTester{
- Error: errors.CategoryNotFound,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
- var category models.Category
- err := app.Conn.Where("id = ?", uuid).First(&category).Error
- assert.Error(err)
-
- var respBody []map[string]interface{}
- err = json.NewDecoder(resp.Body).Decode(&respBody)
- assert.NilError(err)
- assert.Equal(0, len(respBody))
+ appAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags", uuid),
+ Role: &models.Super,
+ }, h.ErrorWithTester{
+ Error: errors.CategoryNotFound,
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ var category models.Category
+ err := app.Conn.Where("id = ?", uuid).First(&category).Error
+ assert.Assert(err != nil)
+ },
},
- }).Close()
+ ).Close()
}
func TestGetCategoryTagWorks(t *testing.T) {
- existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(t)
+ existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(h.InitTest(t))
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, tagUUID),
- }.TestOnStatusAndDB(t, &existingAppAssert,
- DBTesterWithStatus{
+ existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, tagUUID),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
Status: fiber.StatusOK,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory(categoryUUID))
},
},
@@ -122,7 +130,7 @@ func TestGetCategoryTagWorks(t *testing.T) {
}
func TestGetCategoryTagFailsCategoryBadRequest(t *testing.T) {
- appAssert, _, tagUUID := CreateSampleTag(t)
+ appAssert, _, tagUUID := CreateSampleTag(h.InitTest(t))
badRequests := []string{
"0",
@@ -133,17 +141,20 @@ func TestGetCategoryTagFailsCategoryBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", badRequest, tagUUID),
- }.TestOnError(t, &appAssert, errors.FailedToValidateID)
+ appAssert.TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", badRequest, tagUUID),
+ Role: &models.Super,
+ }, errors.FailedToValidateID,
+ )
}
appAssert.Close()
}
func TestGetCategoryTagFailsTagBadRequest(t *testing.T) {
- appAssert, categoryUUID := CreateSampleCategory(t, nil)
+ appAssert, categoryUUID := CreateSampleCategory(h.InitTest(t))
badRequests := []string{
"0",
@@ -154,29 +165,40 @@ func TestGetCategoryTagFailsTagBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, badRequest),
- }.TestOnError(t, &appAssert, errors.FailedToValidateID)
+ appAssert.TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, badRequest),
+ Role: &models.Super,
+ },
+ errors.FailedToValidateID)
}
appAssert.Close()
}
func TestGetCategoryTagFailsCategoryNotFound(t *testing.T) {
- appAssert, _, tagUUID := CreateSampleTag(t)
+ appAssert, _, tagUUID := CreateSampleTag(h.InitTest(t))
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", uuid.New(), tagUUID),
- }.TestOnError(t, &appAssert, errors.TagNotFound).Close()
+ appAssert.TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", uuid.New(), tagUUID),
+ Role: &models.Super,
+ },
+ errors.TagNotFound,
+ ).Close()
}
func TestGetCategoryTagFailsTagNotFound(t *testing.T) {
- appAssert, categoryUUID := CreateSampleCategory(t, nil)
+ appAssert, categoryUUID := CreateSampleCategory(h.InitTest(t))
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, uuid.New()),
- }.TestOnError(t, &appAssert, errors.TagNotFound).Close()
+ appAssert.TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, uuid.New()),
+ Role: &models.Super,
+ },
+ errors.TagNotFound,
+ ).Close()
}
diff --git a/backend/tests/api/category_test.go b/backend/tests/api/category_test.go
index 61fbe6018..736f92844 100644
--- a/backend/tests/api/category_test.go
+++ b/backend/tests/api/category_test.go
@@ -7,6 +7,7 @@ import (
"github.com/GenerateNU/sac/backend/src/errors"
"github.com/GenerateNU/sac/backend/src/models"
+ h "github.com/GenerateNU/sac/backend/tests/api/helpers"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"github.com/huandu/go-assert"
@@ -20,7 +21,7 @@ func SampleCategoryFactory() *map[string]interface{} {
}
}
-func AssertCategoryBodyRespDB(app TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
+func AssertCategoryBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
var respCategory models.Category
err := json.NewDecoder(resp.Body).Decode(&respCategory)
@@ -45,7 +46,7 @@ func AssertCategoryBodyRespDB(app TestApp, assert *assert.A, resp *http.Response
return dbCategory.ID
}
-func AssertCategoryWithBodyRespDBMostRecent(app TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
+func AssertCategoryWithBodyRespDBMostRecent(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
var respCategory models.Category
err := json.NewDecoder(resp.Body).Decode(&respCategory)
@@ -66,65 +67,65 @@ func AssertCategoryWithBodyRespDBMostRecent(app TestApp, assert *assert.A, resp
return dbCategory.ID
}
-func AssertSampleCategoryBodyRespDB(app TestApp, assert *assert.A, resp *http.Response) uuid.UUID {
+func AssertSampleCategoryBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response) uuid.UUID {
return AssertCategoryBodyRespDB(app, assert, resp, SampleCategoryFactory())
}
-func CreateSampleCategory(t *testing.T, existingAppAssert *ExistingAppAssert) (ExistingAppAssert, uuid.UUID) {
+func CreateSampleCategory(existingAppAssert h.ExistingAppAssert) (h.ExistingAppAssert, uuid.UUID) {
var sampleCategoryUUID uuid.UUID
- newAppAssert := TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/categories/",
- Body: SampleCategoryFactory(),
- }.TestOnStatusAndDB(t, existingAppAssert,
- DBTesterWithStatus{
+ existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/categories/",
+ Body: SampleCategoryFactory(),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
Status: fiber.StatusCreated,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
sampleCategoryUUID = AssertSampleCategoryBodyRespDB(app, assert, resp)
},
},
)
- if existingAppAssert == nil {
- return newAppAssert, sampleCategoryUUID
- } else {
- return *existingAppAssert, sampleCategoryUUID
- }
+ return existingAppAssert, sampleCategoryUUID
}
func TestCreateCategoryWorks(t *testing.T) {
- existingAppAssert, _ := CreateSampleCategory(t, nil)
+ existingAppAssert, _ := CreateSampleCategory(h.InitTest(t))
existingAppAssert.Close()
}
func TestCreateCategoryIgnoresid(t *testing.T) {
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/categories/",
- Body: &map[string]interface{}{
- "id": 12,
- "name": "Foo",
+ h.InitTest(t).TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/categories/",
+ Body: &map[string]interface{}{
+ "id": 12,
+ "name": "Foo",
+ },
+ Role: &models.Super,
},
- }.TestOnStatusAndDB(t, nil,
- DBTesterWithStatus{
+ h.TesterWithStatus{
Status: fiber.StatusCreated,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertSampleCategoryBodyRespDB(app, assert, resp)
},
},
).Close()
}
-func Assert1Category(app TestApp, assert *assert.A, resp *http.Response) {
+func Assert1Category(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertNumCategoriesRemainsAtN(app, assert, resp, 1)
}
-func AssertNoCategories(app TestApp, assert *assert.A, resp *http.Response) {
+func AssertNoCategories(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertNumCategoriesRemainsAtN(app, assert, resp, 0)
}
-func AssertNumCategoriesRemainsAtN(app TestApp, assert *assert.A, resp *http.Response, n int) {
+func AssertNumCategoriesRemainsAtN(app h.TestApp, assert *assert.A, resp *http.Response, n int) {
var categories []models.Category
err := app.Conn.Find(&categories).Error
@@ -135,52 +136,58 @@ func AssertNumCategoriesRemainsAtN(app TestApp, assert *assert.A, resp *http.Res
}
func TestCreateCategoryFailsIfNameIsNotString(t *testing.T) {
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/categories/",
- Body: &map[string]interface{}{
- "name": 1231,
+ h.InitTest(t).TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/categories/",
+ Body: &map[string]interface{}{
+ "name": 1231,
+ },
+ Role: &models.Super,
},
- }.TestOnErrorAndDB(t, nil,
- ErrorWithDBTester{
- Error: errors.FailedToParseRequestBody,
- DBTester: AssertNoCategories,
+ h.ErrorWithTester{
+ Error: errors.FailedToParseRequestBody,
+ Tester: AssertNoCategories,
},
).Close()
}
func TestCreateCategoryFailsIfNameIsMissing(t *testing.T) {
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/categories/",
- Body: &map[string]interface{}{},
- }.TestOnErrorAndDB(t, nil,
- ErrorWithDBTester{
- Error: errors.FailedToValidateCategory,
- DBTester: AssertNoCategories,
+ h.InitTest(t).TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/categories/",
+ Body: &map[string]interface{}{},
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.FailedToValidateCategory,
+ Tester: AssertNoCategories,
},
).Close()
}
func TestCreateCategoryFailsIfCategoryWithThatNameAlreadyExists(t *testing.T) {
- existingAppAssert, _ := CreateSampleCategory(t, nil)
+ existingAppAssert, _ := CreateSampleCategory(h.InitTest(t))
- TestNumCategoriesRemainsAt1 := func(app TestApp, assert *assert.A, resp *http.Response) {
+ TestNumCategoriesRemainsAt1 := func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertNumCategoriesRemainsAtN(app, assert, resp, 1)
}
- for _, permutation := range AllCasingPermutations((*SampleCategoryFactory())["name"].(string)) {
+ for _, permutation := range h.AllCasingPermutations((*SampleCategoryFactory())["name"].(string)) {
modifiedSampleCategoryBody := *SampleCategoryFactory()
modifiedSampleCategoryBody["name"] = permutation
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/categories/",
- Body: &modifiedSampleCategoryBody,
- }.TestOnErrorAndDB(t, &existingAppAssert,
- ErrorWithDBTester{
- Error: errors.CategoryAlreadyExists,
- DBTester: TestNumCategoriesRemainsAt1,
+ existingAppAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/categories/",
+ Body: &modifiedSampleCategoryBody,
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.CategoryAlreadyExists,
+ Tester: TestNumCategoriesRemainsAt1,
},
)
}
@@ -189,15 +196,17 @@ func TestCreateCategoryFailsIfCategoryWithThatNameAlreadyExists(t *testing.T) {
}
func TestGetCategoryWorks(t *testing.T) {
- existingAppAssert, uuid := CreateSampleCategory(t, nil)
+ existingAppAssert, uuid := CreateSampleCategory(h.InitTest(t))
- TestRequest{
- Method: "GET",
- Path: fmt.Sprintf("/api/v1/categories/%s", uuid),
- }.TestOnStatusAndDB(t, &existingAppAssert,
- DBTesterWithStatus{
+ existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s", uuid),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
Status: fiber.StatusOK,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertSampleCategoryBodyRespDB(app, assert, resp)
},
},
@@ -205,6 +214,8 @@ func TestGetCategoryWorks(t *testing.T) {
}
func TestGetCategoryFailsBadRequest(t *testing.T) {
+ appAssert := h.InitTest(t)
+
badRequests := []string{
"0",
"-1",
@@ -214,30 +225,41 @@ func TestGetCategoryFailsBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- TestRequest{
- Method: "GET",
- Path: fmt.Sprintf("/api/v1/categories/%s", badRequest),
- }.TestOnError(t, nil, errors.FailedToValidateID).Close()
+ appAssert.TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s", badRequest),
+ Role: &models.Super,
+ },
+ errors.FailedToValidateID,
+ )
}
+
+ appAssert.Close()
}
func TestGetCategoryFailsNotFound(t *testing.T) {
- TestRequest{
- Method: "GET",
- Path: fmt.Sprintf("/api/v1/categories/%s", uuid.New()),
- }.TestOnError(t, nil, errors.CategoryNotFound).Close()
+ h.InitTest(t).TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/categories/%s", uuid.New()),
+ Role: &models.Super,
+ }, errors.CategoryNotFound,
+ ).Close()
}
func TestGetCategoriesWorks(t *testing.T) {
- existingAppAssert, _ := CreateSampleCategory(t, nil)
+ existingAppAssert, _ := CreateSampleCategory(h.InitTest(t))
- TestRequest{
- Method: "GET",
- Path: "/api/v1/categories/",
- }.TestOnStatusAndDB(t, &existingAppAssert,
- DBTesterWithStatus{
+ existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: "/api/v1/categories/",
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
Status: fiber.StatusOK,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var categories []models.Category
err := app.Conn.Find(&categories).Error
@@ -263,7 +285,7 @@ func TestGetCategoriesWorks(t *testing.T) {
).Close()
}
-func AssertUpdatedCategoryBodyRespDB(app TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) {
+func AssertUpdatedCategoryBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) {
var respCategory models.Category
err := json.NewDecoder(resp.Body).Decode(&respCategory)
@@ -288,52 +310,58 @@ func AssertUpdatedCategoryBodyRespDB(app TestApp, assert *assert.A, resp *http.R
}
func TestUpdateCategoryWorks(t *testing.T) {
- existingAppAssert, uuid := CreateSampleCategory(t, nil)
+ existingAppAssert, uuid := CreateSampleCategory(h.InitTest(t))
category := map[string]interface{}{
"id": uuid,
"name": "Arts & Crafts",
}
- AssertUpdatedCategoryBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertUpdatedCategoryBodyRespDB := func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertUpdatedCategoryBodyRespDB(app, assert, resp, &category)
}
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/categories/%s", uuid),
- Body: &category,
- }.TestOnStatusAndDB(t, &existingAppAssert,
- DBTesterWithStatus{
- Status: fiber.StatusOK,
- DBTester: AssertUpdatedCategoryBodyRespDB,
+ existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: fmt.Sprintf("/api/v1/categories/%s", uuid),
+ Body: &category,
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusOK,
+ Tester: AssertUpdatedCategoryBodyRespDB,
},
).Close()
}
func TestUpdateCategoryWorksWithSameDetails(t *testing.T) {
- existingAppAssert, uuid := CreateSampleCategory(t, nil)
+ existingAppAssert, uuid := CreateSampleCategory(h.InitTest(t))
category := *SampleCategoryFactory()
category["id"] = uuid
- AssertSampleCategoryBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertSampleCategoryBodyRespDB := func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertUpdatedCategoryBodyRespDB(app, assert, resp, &category)
}
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/categories/%s", uuid),
- Body: &category,
- }.TestOnStatusAndDB(t, &existingAppAssert,
- DBTesterWithStatus{
- Status: fiber.StatusOK,
- DBTester: AssertSampleCategoryBodyRespDB,
+ existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: fmt.Sprintf("/api/v1/categories/%s", uuid),
+ Body: &category,
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusOK,
+ Tester: AssertSampleCategoryBodyRespDB,
},
).Close()
}
func TestUpdateCategoryFailsBadRequest(t *testing.T) {
+ appAssert := h.InitTest(t)
+
badRequests := []string{
"0",
"-1",
@@ -343,30 +371,38 @@ func TestUpdateCategoryFailsBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/categories/%s", badRequest),
- Body: SampleCategoryFactory(),
- }.TestOnError(t, nil, errors.FailedToValidateID).Close()
+ appAssert.TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: fmt.Sprintf("/api/v1/categories/%s", badRequest),
+ Body: SampleCategoryFactory(),
+ Role: &models.Super,
+ },
+ errors.FailedToValidateID,
+ )
}
+
+ appAssert.Close()
}
func TestDeleteCategoryWorks(t *testing.T) {
- existingAppAssert, uuid := CreateSampleCategory(t, nil)
-
- TestRequest{
- Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/categories/%s", uuid),
- }.TestOnStatusAndDB(t, &existingAppAssert,
- DBTesterWithStatus{
- Status: fiber.StatusNoContent,
- DBTester: AssertNoCategories,
+ existingAppAssert, uuid := CreateSampleCategory(h.InitTest(t))
+
+ existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodDelete,
+ Path: fmt.Sprintf("/api/v1/categories/%s", uuid),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusNoContent,
+ Tester: AssertNoCategories,
},
).Close()
}
func TestDeleteCategoryFailsBadRequest(t *testing.T) {
- existingAppAssert, _ := CreateSampleCategory(t, nil)
+ existingAppAssert, _ := CreateSampleCategory(h.InitTest(t))
badRequests := []string{
"0",
@@ -377,13 +413,15 @@ func TestDeleteCategoryFailsBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/categories/%s", badRequest),
- }.TestOnErrorAndDB(t, &existingAppAssert,
- ErrorWithDBTester{
- Error: errors.FailedToValidateID,
- DBTester: Assert1Category,
+ existingAppAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodDelete,
+ Path: fmt.Sprintf("/api/v1/categories/%s", badRequest),
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.FailedToValidateID,
+ Tester: Assert1Category,
},
)
}
@@ -392,15 +430,17 @@ func TestDeleteCategoryFailsBadRequest(t *testing.T) {
}
func TestDeleteCategoryFailsNotFound(t *testing.T) {
- existingAppAssert, _ := CreateSampleCategory(t, nil)
-
- TestRequest{
- Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/categories/%s", uuid.New()),
- }.TestOnErrorAndDB(t, &existingAppAssert,
- ErrorWithDBTester{
- Error: errors.CategoryNotFound,
- DBTester: Assert1Category,
+ existingAppAssert, _ := CreateSampleCategory(h.InitTest(t))
+
+ existingAppAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodDelete,
+ Path: fmt.Sprintf("/api/v1/categories/%s", uuid.New()),
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.CategoryNotFound,
+ Tester: Assert1Category,
},
).Close()
}
diff --git a/backend/tests/api/club_test.go b/backend/tests/api/club_test.go
index b24f1ac7d..bdce0892e 100644
--- a/backend/tests/api/club_test.go
+++ b/backend/tests/api/club_test.go
@@ -8,6 +8,7 @@ import (
"github.com/GenerateNU/sac/backend/src/errors"
"github.com/GenerateNU/sac/backend/src/models"
+ h "github.com/GenerateNU/sac/backend/tests/api/helpers"
"github.com/goccy/go-json"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
@@ -15,13 +16,12 @@ import (
"gorm.io/gorm"
)
-func SampleClubFactory(userID uuid.UUID) *map[string]interface{} {
+func SampleClubFactory(userID *uuid.UUID) *map[string]interface{} {
return &map[string]interface{}{
"user_id": userID,
"name": "Generate",
"preview": "Generate is Northeastern's premier student-led product development studio.",
"description": "https://mongodb.com",
- "num_members": 1,
"is_recruiting": true,
"recruitment_cycle": "always",
"recruitment_type": "application",
@@ -30,7 +30,7 @@ func SampleClubFactory(userID uuid.UUID) *map[string]interface{} {
}
}
-func AssertClubBodyRespDB(app TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
+func AssertClubBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
var respClub models.Club
err := json.NewDecoder(resp.Body).Decode(&respClub)
@@ -66,11 +66,10 @@ func AssertClubBodyRespDB(app TestApp, assert *assert.A, resp *http.Response, bo
assert.Equal(1, len(dbAdmins))
- assert.Equal((*body)["user_id"].(uuid.UUID), dbAdmins[0].ID)
+ assert.Equal(*(*body)["user_id"].(*uuid.UUID), dbAdmins[0].ID)
assert.Equal((*body)["name"].(string), dbClub.Name)
assert.Equal((*body)["preview"].(string), dbClub.Preview)
assert.Equal((*body)["description"].(string), dbClub.Description)
- assert.Equal((*body)["num_members"].(int), dbClub.NumMembers)
assert.Equal((*body)["is_recruiting"].(bool), dbClub.IsRecruiting)
assert.Equal(models.RecruitmentCycle((*body)["recruitment_cycle"].(string)), dbClub.RecruitmentCycle)
assert.Equal(models.RecruitmentType((*body)["recruitment_type"].(string)), dbClub.RecruitmentType)
@@ -80,7 +79,7 @@ func AssertClubBodyRespDB(app TestApp, assert *assert.A, resp *http.Response, bo
return dbClub.ID
}
-func AssertClubWithBodyRespDBMostRecent(app TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
+func AssertClubWithBodyRespDBMostRecent(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
var respClub models.Club
err := json.NewDecoder(resp.Body).Decode(&respClub)
@@ -128,48 +127,49 @@ func AssertClubWithBodyRespDBMostRecent(app TestApp, assert *assert.A, resp *htt
return dbClub.ID
}
-func AssertSampleClubBodyRespDB(app TestApp, assert *assert.A, resp *http.Response, userID uuid.UUID) uuid.UUID {
- return AssertClubBodyRespDB(app, assert, resp, SampleClubFactory(userID))
-}
+func AssertSampleClubBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, userID uuid.UUID) uuid.UUID {
+ sampleClub := SampleClubFactory(&userID)
+ (*sampleClub)["num_members"] = 1
-func CreateSampleClub(t *testing.T, existingAppAssert *ExistingAppAssert) (eaa ExistingAppAssert, userUUID uuid.UUID, clubUUID uuid.UUID) {
- appAssert, userID := CreateSampleUser(t, existingAppAssert)
+ return AssertClubBodyRespDB(app, assert, resp, sampleClub)
+}
+func CreateSampleClub(existingAppAssert h.ExistingAppAssert) (eaa h.ExistingAppAssert, studentUUID uuid.UUID, clubUUID uuid.UUID) {
var sampleClubUUID uuid.UUID
- newAppAssert := TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/clubs/",
- Body: SampleClubFactory(userID),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
+ newAppAssert := existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/clubs/",
+ Body: SampleClubFactory(nil),
+ Role: &models.Super,
+ TestUserIDReplaces: h.StringToPointer("user_id"),
+ },
+ h.TesterWithStatus{
Status: fiber.StatusCreated,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
- sampleClubUUID = AssertSampleClubBodyRespDB(app, assert, resp, userID)
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ sampleClubUUID = AssertSampleClubBodyRespDB(app, assert, resp, app.TestUser.UUID)
},
},
)
- if existingAppAssert == nil {
- return newAppAssert, userID, sampleClubUUID
- } else {
- return *existingAppAssert, userID, sampleClubUUID
- }
+ return existingAppAssert, newAppAssert.App.TestUser.UUID, sampleClubUUID
}
func TestCreateClubWorks(t *testing.T) {
- existingAppAssert, _, _ := CreateSampleClub(t, nil)
+ existingAppAssert, _, _ := CreateSampleClub(h.InitTest(t))
existingAppAssert.Close()
}
func TestGetClubsWorks(t *testing.T) {
- TestRequest{
+ h.InitTest(t).TestOnStatusAndDB(h.TestRequest{
Method: fiber.MethodGet,
Path: "/api/v1/clubs/",
- }.TestOnStatusAndDB(t, nil,
- DBTesterWithStatus{
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
Status: fiber.StatusOK,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var respClubs []models.Club
err := json.NewDecoder(resp.Body).Decode(&respClubs)
@@ -215,7 +215,7 @@ func TestGetClubsWorks(t *testing.T) {
).Close()
}
-func AssertNumClubsRemainsAtN(app TestApp, assert *assert.A, resp *http.Response, n int) {
+func AssertNumClubsRemainsAtN(app h.TestApp, assert *assert.A, resp *http.Response, n int) {
var dbClubs []models.Club
err := app.Conn.Order("created_at desc").Find(&dbClubs).Error
@@ -225,25 +225,27 @@ func AssertNumClubsRemainsAtN(app TestApp, assert *assert.A, resp *http.Response
assert.Equal(n, len(dbClubs))
}
-var TestNumClubsRemainsAt1 = func(app TestApp, assert *assert.A, resp *http.Response) {
+var TestNumClubsRemainsAt1 = func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertNumClubsRemainsAtN(app, assert, resp, 1)
}
func AssertCreateBadClubDataFails(t *testing.T, jsonKey string, badValues []interface{}) {
- appAssert, uuid := CreateSampleUser(t, nil)
+ appAssert, uuid, _ := CreateSampleStudent(t, nil)
for _, badValue := range badValues {
- sampleClubPermutation := *SampleClubFactory(uuid)
+ sampleClubPermutation := *SampleClubFactory(&uuid)
sampleClubPermutation[jsonKey] = badValue
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/clubs/",
- Body: &sampleClubPermutation,
- }.TestOnErrorAndDB(t, &appAssert,
- ErrorWithDBTester{
- Error: errors.FailedToValidateClub,
- DBTester: TestNumClubsRemainsAt1,
+ appAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/clubs/",
+ Body: &sampleClubPermutation,
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.FailedToValidateClub,
+ Tester: TestNumClubsRemainsAt1,
},
)
}
@@ -307,20 +309,22 @@ func TestCreateClubFailsOnInvalidLogo(t *testing.T) {
}
func TestUpdateClubWorks(t *testing.T) {
- appAssert, userUUID, clubUUID := CreateSampleClub(t, nil)
+ appAssert, studentUUID, clubUUID := CreateSampleClub(h.InitTest(t))
- updatedClub := SampleClubFactory(userUUID)
+ updatedClub := SampleClubFactory(&studentUUID)
(*updatedClub)["name"] = "Updated Name"
(*updatedClub)["preview"] = "Updated Preview"
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/clubs/%s", clubUUID),
- Body: updatedClub,
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
+ appAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: fmt.Sprintf("/api/v1/clubs/%s", clubUUID),
+ Body: updatedClub,
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
Status: fiber.StatusOK,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertClubBodyRespDB(app, assert, resp, updatedClub)
},
},
@@ -328,9 +332,9 @@ func TestUpdateClubWorks(t *testing.T) {
}
func TestUpdateClubFailsOnInvalidBody(t *testing.T) {
- appAssert, userUUID, clubUUID := CreateSampleClub(t, nil)
+ appAssert, studentUUID, clubUUID := CreateSampleClub(h.InitTest(t))
- body := SampleClubFactory(userUUID)
+ body := SampleClubFactory(&studentUUID)
for _, invalidData := range []map[string]interface{}{
{"description": "Not a URL"},
@@ -339,14 +343,16 @@ func TestUpdateClubFailsOnInvalidBody(t *testing.T) {
{"application_link": "Not an URL"},
{"logo": "@12394X_2"},
} {
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/clubs/%s", clubUUID),
- Body: &invalidData,
- }.TestOnErrorAndDB(t, &appAssert,
- ErrorWithDBTester{
+ appAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: fmt.Sprintf("/api/v1/clubs/%s", clubUUID),
+ Body: &invalidData,
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
Error: errors.FailedToValidateClub,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var dbClubs []models.Club
err := app.Conn.Order("created_at desc").Find(&dbClubs).Error
@@ -365,11 +371,10 @@ func TestUpdateClubFailsOnInvalidBody(t *testing.T) {
assert.Equal(1, len(dbAdmins))
- assert.Equal((*body)["user_id"].(uuid.UUID), dbAdmins[0].ID)
+ assert.Equal(*(*body)["user_id"].(*uuid.UUID), dbAdmins[0].ID)
assert.Equal((*body)["name"].(string), dbClub.Name)
assert.Equal((*body)["preview"].(string), dbClub.Preview)
assert.Equal((*body)["description"].(string), dbClub.Description)
- assert.Equal((*body)["num_members"].(int), dbClub.NumMembers)
assert.Equal((*body)["is_recruiting"].(bool), dbClub.IsRecruiting)
assert.Equal(models.RecruitmentCycle((*body)["recruitment_cycle"].(string)), dbClub.RecruitmentCycle)
assert.Equal(models.RecruitmentType((*body)["recruitment_type"].(string)), dbClub.RecruitmentType)
@@ -383,6 +388,8 @@ func TestUpdateClubFailsOnInvalidBody(t *testing.T) {
}
func TestUpdateClubFailsBadRequest(t *testing.T) {
+ appAssert := h.InitTest(t)
+
badRequests := []string{
"0",
"-1",
@@ -391,28 +398,36 @@ func TestUpdateClubFailsBadRequest(t *testing.T) {
"null",
}
+ sampleStudent, rawPassword := h.SampleStudentFactory()
+
for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/clubs/%s", badRequest),
- Body: SampleUserFactory(),
- }.TestOnError(t, nil, errors.FailedToValidateID).Close()
+ appAssert.TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: fmt.Sprintf("/api/v1/clubs/%s", badRequest),
+ Body: h.SampleStudentJSONFactory(sampleStudent, rawPassword),
+ Role: &models.Super,
+ },
+ errors.FailedToValidateID,
+ )
}
+
+ appAssert.Close()
}
func TestUpdateClubFailsOnClubIdNotExist(t *testing.T) {
- appAssert, userUUID := CreateSampleUser(t, nil)
-
uuid := uuid.New()
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/clubs/%s", uuid),
- Body: SampleClubFactory(userUUID),
- }.TestOnErrorAndDB(t, &appAssert,
- ErrorWithDBTester{
+ h.InitTest(t).TestOnErrorAndDB(h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: fmt.Sprintf("/api/v1/clubs/%s", uuid),
+ Body: SampleClubFactory(nil),
+ Role: &models.Super,
+ TestUserIDReplaces: h.StringToPointer("user_id"),
+ },
+ h.ErrorWithTester{
Error: errors.ClubNotFound,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var club models.Club
err := app.Conn.Where("id = ?", uuid).First(&club).Error
@@ -424,28 +439,32 @@ func TestUpdateClubFailsOnClubIdNotExist(t *testing.T) {
}
func TestDeleteClubWorks(t *testing.T) {
- appAssert, _, clubUUID := CreateSampleClub(t, nil)
-
- TestRequest{
- Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/clubs/%s", clubUUID),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
- Status: fiber.StatusNoContent,
- DBTester: TestNumClubsRemainsAt1,
+ appAssert, _, clubUUID := CreateSampleClub(h.InitTest(t))
+
+ appAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodDelete,
+ Path: fmt.Sprintf("/api/v1/clubs/%s", clubUUID),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusNoContent,
+ Tester: TestNumClubsRemainsAt1,
},
).Close()
}
func TestDeleteClubNotExist(t *testing.T) {
uuid := uuid.New()
- TestRequest{
- Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/clubs/%s", uuid),
- }.TestOnErrorAndDB(t, nil,
- ErrorWithDBTester{
+ h.InitTest(t).TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodDelete,
+ Path: fmt.Sprintf("/api/v1/clubs/%s", uuid),
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
Error: errors.ClubNotFound,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var club models.Club
err := app.Conn.Where("id = ?", uuid).First(&club).Error
@@ -459,6 +478,8 @@ func TestDeleteClubNotExist(t *testing.T) {
}
func TestDeleteClubBadRequest(t *testing.T) {
+ appAssert := h.InitTest(t)
+
badRequests := []string{
"0",
"-1",
@@ -468,9 +489,15 @@ func TestDeleteClubBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/clubs/%s", badRequest),
- }.TestOnError(t, nil, errors.FailedToValidateID).Close()
+ appAssert.TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodDelete,
+ Path: fmt.Sprintf("/api/v1/clubs/%s", badRequest),
+ Role: &models.Super,
+ },
+ errors.FailedToValidateID,
+ )
}
+
+ appAssert.Close()
}
diff --git a/backend/tests/api/health_test.go b/backend/tests/api/health_test.go
index c128ad320..52e51fe9d 100644
--- a/backend/tests/api/health_test.go
+++ b/backend/tests/api/health_test.go
@@ -3,14 +3,16 @@ package tests
import (
"testing"
+ h "github.com/GenerateNU/sac/backend/tests/api/helpers"
"github.com/gofiber/fiber/v2"
)
func TestHealthWorks(t *testing.T) {
- TestRequest{
- Method: fiber.MethodGet,
- Path: "/health",
- }.TestOnStatus(t, nil,
+ h.InitTest(t).TestOnStatus(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: "/health",
+ },
fiber.StatusOK,
).Close()
}
diff --git a/backend/tests/api/helpers.go b/backend/tests/api/helpers.go
deleted file mode 100644
index c08fc95d0..000000000
--- a/backend/tests/api/helpers.go
+++ /dev/null
@@ -1,262 +0,0 @@
-package tests
-
-import (
- "bytes"
- crand "crypto/rand"
- "fmt"
- "math/big"
- "net"
- "net/http"
- "net/http/httptest"
- "path/filepath"
- "strings"
- "testing"
-
- "github.com/GenerateNU/sac/backend/src/config"
- "github.com/GenerateNU/sac/backend/src/database"
- "github.com/GenerateNU/sac/backend/src/errors"
- "github.com/GenerateNU/sac/backend/src/server"
-
- "github.com/goccy/go-json"
-
- "github.com/gofiber/fiber/v2"
- "github.com/huandu/go-assert"
- gormPostgres "gorm.io/driver/postgres"
- "gorm.io/gorm"
-)
-
-func InitTest(t *testing.T) (TestApp, *assert.A) {
- assert := assert.New(t)
- app, err := spawnApp()
-
- assert.NilError(err)
-
- return app, assert
-}
-
-type TestApp struct {
- App *fiber.App
- Address string
- Conn *gorm.DB
- Settings config.Settings
- InitialDBName string
-}
-
-func spawnApp() (TestApp, error) {
- listener, err := net.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- return TestApp{}, err
- }
-
- configuration, err := config.GetConfiguration(filepath.Join("..", "..", "..", "config"))
- if err != nil {
- return TestApp{}, err
- }
-
- initialDBName := configuration.Database.DatabaseName
-
- configuration.Database.DatabaseName = generateRandomDBName()
-
- connectionWithDB, err := configureDatabase(configuration)
- if err != nil {
- return TestApp{}, err
- }
-
- return TestApp{
- App: server.Init(connectionWithDB, configuration),
- Address: fmt.Sprintf("http://%s", listener.Addr().String()),
- Conn: connectionWithDB,
- Settings: configuration,
- InitialDBName: initialDBName,
- }, nil
-}
-
-func generateRandomInt(max int64) int64 {
- randInt, _ := crand.Int(crand.Reader, big.NewInt(max))
- return randInt.Int64()
-}
-
-func generateRandomDBName() string {
- prefix := "sac_test_"
- letterBytes := "abcdefghijklmnopqrstuvwxyz"
- length := len(prefix) + 36
- result := make([]byte, length)
- for i := 0; i < length; i++ {
- result[i] = letterBytes[generateRandomInt(int64(len(letterBytes)))]
- }
-
- return fmt.Sprintf("%s%s", prefix, string(result))
-}
-
-func configureDatabase(config config.Settings) (*gorm.DB, error) {
- dsnWithoutDB := config.Database.WithoutDb()
- dbWithoutDB, err := gorm.Open(gormPostgres.Open(dsnWithoutDB), &gorm.Config{SkipDefaultTransaction: true, TranslateError: true})
- if err != nil {
- return nil, err
- }
-
- err = dbWithoutDB.Exec(fmt.Sprintf("CREATE DATABASE %s;", config.Database.DatabaseName)).Error
- if err != nil {
- return nil, err
- }
-
- dsnWithDB := config.Database.WithDb()
- dbWithDB, err := gorm.Open(gormPostgres.Open(dsnWithDB), &gorm.Config{SkipDefaultTransaction: true, TranslateError: true})
- if err != nil {
- return nil, err
- }
-
- err = dbWithDB.Exec("CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\"").Error
- if err != nil {
- return nil, err
- }
- err = database.MigrateDB(config, dbWithDB)
- if err != nil {
- return nil, err
- }
-
- return dbWithDB, nil
-}
-
-type ExistingAppAssert struct {
- App TestApp
- Assert *assert.A
-}
-
-func (eaa ExistingAppAssert) Close() {
- db, err := eaa.App.Conn.DB()
- if err != nil {
- panic(err)
- }
-
- err = db.Close()
- if err != nil {
- panic(err)
- }
-}
-
-type TestRequest struct {
- Method string
- Path string
- Body *map[string]interface{}
- Headers *map[string]string
-}
-
-func (request TestRequest) Test(t *testing.T, existingAppAssert *ExistingAppAssert) (ExistingAppAssert, *http.Response) {
- var app TestApp
- var assert *assert.A
-
- if existingAppAssert == nil {
- app, assert = InitTest(t)
- } else {
- app, assert = existingAppAssert.App, existingAppAssert.Assert
- }
-
- address := fmt.Sprintf("%s%s", app.Address, request.Path)
-
- var req *http.Request
-
- if request.Body == nil {
- req = httptest.NewRequest(request.Method, address, nil)
- } else {
- bodyBytes, err := json.Marshal(request.Body)
-
- assert.NilError(err)
-
- req = httptest.NewRequest(request.Method, address, bytes.NewBuffer(bodyBytes))
-
- if request.Headers == nil {
- request.Headers = &map[string]string{}
- }
-
- if _, ok := (*request.Headers)["Content-Type"]; !ok {
- (*request.Headers)["Content-Type"] = "application/json"
- }
- }
-
- if request.Headers != nil {
- for key, value := range *request.Headers {
- req.Header.Add(key, value)
- }
- }
-
- resp, err := app.App.Test(req)
-
- assert.NilError(err)
-
- return ExistingAppAssert{
- App: app,
- Assert: assert,
- }, resp
-}
-
-func (request TestRequest) TestOnStatus(t *testing.T, existingAppAssert *ExistingAppAssert, status int) ExistingAppAssert {
- appAssert, resp := request.Test(t, existingAppAssert)
-
- _, assert := appAssert.App, appAssert.Assert
-
- assert.Equal(status, resp.StatusCode)
-
- return appAssert
-}
-
-func (request TestRequest) TestOnError(t *testing.T, existingAppAssert *ExistingAppAssert, expectedError errors.Error) ExistingAppAssert {
- appAssert, resp := request.Test(t, existingAppAssert)
- assert := appAssert.Assert
-
- var respBody map[string]interface{}
-
- err := json.NewDecoder(resp.Body).Decode(&respBody)
-
- assert.NilError(err)
- assert.Equal(expectedError.Message, respBody["error"].(string))
-
- assert.Equal(expectedError.StatusCode, resp.StatusCode)
-
- return appAssert
-}
-
-type ErrorWithDBTester struct {
- Error errors.Error
- DBTester DBTester
-}
-
-func (request TestRequest) TestOnErrorAndDB(t *testing.T, existingAppAssert *ExistingAppAssert, errorWithDBTester ErrorWithDBTester) ExistingAppAssert {
- appAssert := request.TestOnError(t, existingAppAssert, errorWithDBTester.Error)
- errorWithDBTester.DBTester(appAssert.App, appAssert.Assert, nil)
- return appAssert
-}
-
-type DBTester func(app TestApp, assert *assert.A, resp *http.Response)
-
-type DBTesterWithStatus struct {
- Status int
- DBTester
-}
-
-func (request TestRequest) TestOnStatusAndDB(t *testing.T, existingAppAssert *ExistingAppAssert, dbTesterStatus DBTesterWithStatus) ExistingAppAssert {
- appAssert, resp := request.Test(t, existingAppAssert)
- app, assert := appAssert.App, appAssert.Assert
-
- assert.Equal(dbTesterStatus.Status, resp.StatusCode)
-
- dbTesterStatus.DBTester(app, assert, resp)
-
- return appAssert
-}
-
-func generateCasingPermutations(word string, currentPermutation string, index int, results *[]string) {
- if index == len(word) {
- *results = append(*results, currentPermutation)
- return
- }
-
- generateCasingPermutations(word, currentPermutation+strings.ToLower(string(word[index])), index+1, results)
- generateCasingPermutations(word, currentPermutation+strings.ToUpper(string(word[index])), index+1, results)
-}
-
-func AllCasingPermutations(word string) []string {
- results := make([]string, 0)
- generateCasingPermutations(word, "", 0, &results)
- return results
-}
diff --git a/backend/tests/api/helpers/app.go b/backend/tests/api/helpers/app.go
new file mode 100644
index 000000000..cb1689e72
--- /dev/null
+++ b/backend/tests/api/helpers/app.go
@@ -0,0 +1,64 @@
+package helpers
+
+import (
+ "fmt"
+ "net"
+ "path/filepath"
+
+ "github.com/GenerateNU/sac/backend/src/config"
+ "github.com/GenerateNU/sac/backend/src/server"
+ "github.com/gofiber/fiber/v2"
+ "github.com/huandu/go-assert"
+ "gorm.io/gorm"
+)
+
+type TestApp struct {
+ App *fiber.App
+ Address string
+ Conn *gorm.DB
+ Settings config.Settings
+ TestUser *TestUser
+}
+
+func spawnApp() (*TestApp, error) {
+ listener, err := net.Listen("tcp", "127.0.0.1:0")
+ if err != nil {
+ return nil, err
+ }
+
+ configuration, err := config.GetConfiguration(filepath.Join("..", "..", "..", "config"))
+ if err != nil {
+ return nil, err
+ }
+
+ configuration.Database.DatabaseName = generateRandomDBName()
+
+ connectionWithDB, err := configureDatabase(configuration)
+ if err != nil {
+ return nil, err
+ }
+
+ return &TestApp{
+ App: server.Init(connectionWithDB, configuration),
+ Address: fmt.Sprintf("http://%s", listener.Addr().String()),
+ Conn: connectionWithDB,
+ Settings: configuration,
+ }, nil
+}
+
+type ExistingAppAssert struct {
+ App TestApp
+ Assert *assert.A
+}
+
+func (eaa ExistingAppAssert) Close() {
+ db, err := eaa.App.Conn.DB()
+ if err != nil {
+ panic(err)
+ }
+
+ err = db.Close()
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/backend/tests/api/helpers/auth.go b/backend/tests/api/helpers/auth.go
new file mode 100644
index 000000000..76fb3033a
--- /dev/null
+++ b/backend/tests/api/helpers/auth.go
@@ -0,0 +1,166 @@
+package helpers
+
+import (
+ "github.com/GenerateNU/sac/backend/src/auth"
+ "github.com/GenerateNU/sac/backend/src/database"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/goccy/go-json"
+ "github.com/gofiber/fiber/v2"
+ "github.com/google/uuid"
+)
+
+type TestUser struct {
+ UUID uuid.UUID
+ Email string
+ Password string
+ AccessToken string
+ RefreshToken string
+}
+
+func (app *TestApp) Auth(role models.UserRole) {
+ if role == models.Super {
+ app.authSuper()
+ } else if role == models.Student {
+ app.authStudent()
+ }
+ // unauthed -> do nothing
+}
+
+func (app *TestApp) authSuper() {
+ superUser, superUserErr := database.SuperUser(app.Settings.SuperUser)
+ if superUserErr != nil {
+ panic(superUserErr)
+ }
+
+ email := superUser.Email
+ password := app.Settings.SuperUser.Password
+
+ resp, err := app.Send(TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/auth/login",
+ Body: &map[string]interface{}{
+ "email": email,
+ "password": password,
+ },
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ var accessToken string
+ var refreshToken string
+
+ for _, cookie := range resp.Cookies() {
+ if cookie.Name == "access_token" {
+ accessToken = cookie.Value
+ } else if cookie.Name == "refresh_token" {
+ refreshToken = cookie.Value
+ }
+ }
+
+ if accessToken == "" || refreshToken == "" {
+ panic("Failed to authenticate super user")
+ }
+
+ app.TestUser = &TestUser{
+ UUID: database.SuperUserUUID,
+ Email: email,
+ Password: password,
+ AccessToken: accessToken,
+ RefreshToken: refreshToken,
+ }
+}
+
+func (app *TestApp) authStudent() {
+ studentUser, rawPassword := SampleStudentFactory()
+
+ resp, err := app.Send(TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/users/",
+ Body: SampleStudentJSONFactory(studentUser, rawPassword),
+ })
+ if err != nil {
+ panic(err)
+ }
+ var respBody map[string]interface{}
+
+ err = json.NewDecoder(resp.Body).Decode(&respBody)
+ if err != nil {
+ panic(err)
+ }
+
+ rawStudentUserUUID := respBody["id"].(string)
+ studentUserUUID, err := uuid.Parse(rawStudentUserUUID)
+ if err != nil {
+ panic(err)
+ }
+
+ resp, err = app.Send(TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/auth/login",
+ Body: &map[string]interface{}{
+ "email": studentUser.Email,
+ "password": rawPassword,
+ },
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ var accessToken string
+ var refreshToken string
+
+ for _, cookie := range resp.Cookies() {
+ if cookie.Name == "access_token" {
+ accessToken = cookie.Value
+ } else if cookie.Name == "refresh_token" {
+ refreshToken = cookie.Value
+ }
+ }
+
+ if accessToken == "" || refreshToken == "" {
+ panic("Failed to authenticate sample student user")
+ }
+
+ app.TestUser = &TestUser{
+ UUID: studentUserUUID,
+ Email: studentUser.Email,
+ Password: rawPassword,
+ AccessToken: accessToken,
+ RefreshToken: refreshToken,
+ }
+}
+
+func SampleStudentFactory() (models.User, string) {
+ password := "1234567890&"
+ hashedPassword, err := auth.ComputePasswordHash(password)
+ if err != nil {
+ panic(err)
+ }
+
+ return models.User{
+ Role: models.Student,
+ FirstName: "Jane",
+ LastName: "Doe",
+ Email: "doe.jane@northeastern.edu",
+ PasswordHash: *hashedPassword,
+ NUID: "001234567",
+ College: models.KCCS,
+ Year: models.Third,
+ }, password
+}
+
+func SampleStudentJSONFactory(sampleStudent models.User, rawPassword string) *map[string]interface{} {
+ if sampleStudent.Role != models.Student {
+ panic("User is not a student")
+ }
+ return &map[string]interface{}{
+ "first_name": sampleStudent.FirstName,
+ "last_name": sampleStudent.LastName,
+ "email": sampleStudent.Email,
+ "password": rawPassword,
+ "nuid": sampleStudent.NUID,
+ "college": string(sampleStudent.College),
+ "year": int(sampleStudent.Year),
+ }
+}
diff --git a/backend/tests/api/helpers/database.go b/backend/tests/api/helpers/database.go
new file mode 100644
index 000000000..dd51598e0
--- /dev/null
+++ b/backend/tests/api/helpers/database.go
@@ -0,0 +1,46 @@
+package helpers
+
+import (
+ "fmt"
+ "sync"
+
+ "github.com/GenerateNU/sac/backend/src/config"
+ "github.com/GenerateNU/sac/backend/src/database"
+ "gorm.io/gorm"
+)
+
+var (
+ rootConn *gorm.DB
+ once sync.Once
+)
+
+func RootConn(dbSettings config.DatabaseSettings) {
+ once.Do(func() {
+ var err error
+ rootConn, err = database.EstablishConn(dbSettings.WithoutDb())
+ if err != nil {
+ panic(err)
+ }
+ })
+}
+
+func configureDatabase(settings config.Settings) (*gorm.DB, error) {
+ RootConn(settings.Database)
+
+ err := rootConn.Exec(fmt.Sprintf("CREATE DATABASE %s", settings.Database.DatabaseName)).Error
+ if err != nil {
+ return nil, err
+ }
+
+ dbWithDB, err := database.EstablishConn(settings.Database.WithDb())
+ if err != nil {
+ return nil, err
+ }
+
+ err = database.MigrateDB(settings, dbWithDB)
+ if err != nil {
+ return nil, err
+ }
+
+ return dbWithDB, nil
+}
diff --git a/backend/tests/api/helpers/requests.go b/backend/tests/api/helpers/requests.go
new file mode 100644
index 000000000..85bf4fd44
--- /dev/null
+++ b/backend/tests/api/helpers/requests.go
@@ -0,0 +1,164 @@
+package helpers
+
+import (
+ "bytes"
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "strings"
+
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+
+ "github.com/goccy/go-json"
+
+ "github.com/huandu/go-assert"
+)
+
+type TestRequest struct {
+ Method string
+ Path string
+ Body *map[string]interface{}
+ Headers *map[string]string
+ Role *models.UserRole
+ TestUserIDReplaces *string
+}
+
+func (app TestApp) Send(request TestRequest) (*http.Response, error) {
+ address := fmt.Sprintf("%s%s", app.Address, request.Path)
+
+ var req *http.Request
+
+ if request.TestUserIDReplaces != nil {
+ if strings.Contains(request.Path, *request.TestUserIDReplaces) {
+ request.Path = strings.Replace(request.Path, *request.TestUserIDReplaces, app.TestUser.UUID.String(), 1)
+ address = fmt.Sprintf("%s%s", app.Address, request.Path)
+ }
+ if request.Body != nil {
+ if _, ok := (*request.Body)[*request.TestUserIDReplaces]; ok {
+ (*request.Body)[*request.TestUserIDReplaces] = app.TestUser.UUID.String()
+ }
+ }
+ }
+
+ if request.Body == nil {
+ req = httptest.NewRequest(request.Method, address, nil)
+ } else {
+ bodyBytes, err := json.Marshal(request.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ req = httptest.NewRequest(request.Method, address, bytes.NewBuffer(bodyBytes))
+
+ if request.Headers == nil {
+ request.Headers = &map[string]string{}
+ }
+
+ if _, ok := (*request.Headers)["Content-Type"]; !ok {
+ (*request.Headers)["Content-Type"] = "application/json"
+ }
+ }
+
+ if request.Headers != nil {
+ for key, value := range *request.Headers {
+ req.Header.Add(key, value)
+ }
+ }
+
+ if app.TestUser != nil {
+ req.AddCookie(&http.Cookie{
+ Name: "access_token",
+ Value: app.TestUser.AccessToken,
+ })
+ }
+
+ resp, err := app.App.Test(req)
+ if err != nil {
+ return nil, err
+ }
+
+ return resp, nil
+}
+
+func (request TestRequest) test(existingAppAssert ExistingAppAssert) (ExistingAppAssert, *http.Response) {
+ if existingAppAssert.App.TestUser == nil && request.Role != nil {
+ existingAppAssert.App.Auth(*request.Role)
+ }
+
+ resp, err := existingAppAssert.App.Send(request)
+
+ existingAppAssert.Assert.NilError(err)
+
+ return existingAppAssert, resp
+}
+
+func (existingAppAssert ExistingAppAssert) TestOnStatus(request TestRequest, status int) ExistingAppAssert {
+ appAssert, resp := request.test(existingAppAssert)
+
+ _, assert := appAssert.App, appAssert.Assert
+
+ assert.Equal(status, resp.StatusCode)
+
+ return appAssert
+}
+
+func (request *TestRequest) testOn(existingAppAssert ExistingAppAssert, status int, key string, value string) (ExistingAppAssert, *http.Response) {
+ appAssert, resp := request.test(existingAppAssert)
+ assert := appAssert.Assert
+
+ var respBody map[string]interface{}
+
+ err := json.NewDecoder(resp.Body).Decode(&respBody)
+
+ assert.NilError(err)
+ assert.Equal(value, respBody[key].(string))
+
+ assert.Equal(status, resp.StatusCode)
+ return appAssert, resp
+}
+
+func (existingAppAssert ExistingAppAssert) TestOnError(request TestRequest, expectedError errors.Error) ExistingAppAssert {
+ appAssert, _ := request.testOn(existingAppAssert, expectedError.StatusCode, "error", expectedError.Message)
+ return appAssert
+}
+
+type ErrorWithTester struct {
+ Error errors.Error
+ Tester Tester
+}
+
+func (existingAppAssert ExistingAppAssert) TestOnErrorAndDB(request TestRequest, errorWithDBTester ErrorWithTester) ExistingAppAssert {
+ appAssert, resp := request.testOn(existingAppAssert, errorWithDBTester.Error.StatusCode, "error", errorWithDBTester.Error.Message)
+ errorWithDBTester.Tester(appAssert.App, appAssert.Assert, resp)
+ return appAssert
+}
+
+func (existingAppAssert ExistingAppAssert) TestOnMessage(request TestRequest, status int, message string) ExistingAppAssert {
+ request.testOn(existingAppAssert, status, "message", message)
+ return existingAppAssert
+}
+
+func (existingAppAssert ExistingAppAssert) TestOnMessageAndDB(request TestRequest, status int, message string, dbTester Tester) ExistingAppAssert {
+ appAssert, resp := request.testOn(existingAppAssert, status, "message", message)
+ dbTester(appAssert.App, appAssert.Assert, resp)
+ return appAssert
+}
+
+type Tester func(app TestApp, assert *assert.A, resp *http.Response)
+
+type TesterWithStatus struct {
+ Status int
+ Tester
+}
+
+func (existingAppAssert ExistingAppAssert) TestOnStatusAndDB(request TestRequest, testerStatus TesterWithStatus) ExistingAppAssert {
+ appAssert, resp := request.test(existingAppAssert)
+ app, assert := appAssert.App, appAssert.Assert
+
+ assert.Equal(testerStatus.Status, resp.StatusCode)
+
+ testerStatus.Tester(app, assert, resp)
+
+ return appAssert
+}
diff --git a/backend/tests/api/helpers/test.go b/backend/tests/api/helpers/test.go
new file mode 100644
index 000000000..9cc54aba5
--- /dev/null
+++ b/backend/tests/api/helpers/test.go
@@ -0,0 +1,19 @@
+package helpers
+
+import (
+ "testing"
+
+ "github.com/huandu/go-assert"
+)
+
+func InitTest(t *testing.T) ExistingAppAssert {
+ assert := assert.New(t)
+ app, err := spawnApp()
+
+ assert.NilError(err)
+
+ return ExistingAppAssert{
+ App: *app,
+ Assert: assert,
+ }
+}
diff --git a/backend/tests/api/helpers/utilities.go b/backend/tests/api/helpers/utilities.go
new file mode 100644
index 000000000..037815e0f
--- /dev/null
+++ b/backend/tests/api/helpers/utilities.go
@@ -0,0 +1,45 @@
+package helpers
+
+import (
+ crand "crypto/rand"
+ "fmt"
+ "math/big"
+ "strings"
+)
+
+func generateRandomInt(max int64) int64 {
+ randInt, _ := crand.Int(crand.Reader, big.NewInt(max))
+ return randInt.Int64()
+}
+
+func generateRandomDBName() string {
+ prefix := "sac_test_"
+ letterBytes := "abcdefghijklmnopqrstuvwxyz"
+ length := len(prefix) + 36
+ result := make([]byte, length)
+ for i := 0; i < length; i++ {
+ result[i] = letterBytes[generateRandomInt(int64(len(letterBytes)))]
+ }
+
+ return fmt.Sprintf("%s%s", prefix, string(result))
+}
+
+func generateCasingPermutations(word string, currentPermutation string, index int, results *[]string) {
+ if index == len(word) {
+ *results = append(*results, currentPermutation)
+ return
+ }
+
+ generateCasingPermutations(word, fmt.Sprintf("%s%s", currentPermutation, strings.ToLower(string(word[index]))), index+1, results)
+ generateCasingPermutations(word, fmt.Sprintf("%s%s", currentPermutation, strings.ToUpper(string(word[index]))), index+1, results)
+}
+
+func AllCasingPermutations(word string) []string {
+ results := make([]string, 0)
+ generateCasingPermutations(word, "", 0, &results)
+ return results
+}
+
+func StringToPointer(s string) *string {
+ return &s
+}
diff --git a/backend/tests/api/helpers_test.go b/backend/tests/api/helpers/utilities_test.go
similarity index 96%
rename from backend/tests/api/helpers_test.go
rename to backend/tests/api/helpers/utilities_test.go
index f96eba563..e81adb70d 100644
--- a/backend/tests/api/helpers_test.go
+++ b/backend/tests/api/helpers/utilities_test.go
@@ -1,4 +1,4 @@
-package tests
+package helpers
import (
"slices"
diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go
index eb9174431..531993fcd 100644
--- a/backend/tests/api/tag_test.go
+++ b/backend/tests/api/tag_test.go
@@ -7,6 +7,7 @@ import (
"github.com/GenerateNU/sac/backend/src/errors"
"github.com/GenerateNU/sac/backend/src/models"
+ h "github.com/GenerateNU/sac/backend/tests/api/helpers"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"github.com/huandu/go-assert"
@@ -21,7 +22,7 @@ func SampleTagFactory(categoryID uuid.UUID) *map[string]interface{} {
}
}
-func AssertTagWithBodyRespDB(app TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
+func AssertTagWithBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
var respTag models.Tag
err := json.NewDecoder(resp.Body).Decode(&respTag)
@@ -44,29 +45,32 @@ func AssertTagWithBodyRespDB(app TestApp, assert *assert.A, resp *http.Response,
return dbTag.ID
}
-func AssertSampleTagBodyRespDB(t *testing.T, app TestApp, assert *assert.A, resp *http.Response) uuid.UUID {
- appAssert, uuid := CreateSampleCategory(t, &ExistingAppAssert{
- App: app,
- Assert: assert,
- })
+func AssertSampleTagBodyRespDB(t *testing.T, app h.TestApp, assert *assert.A, resp *http.Response) uuid.UUID {
+ appAssert, uuid := CreateSampleCategory(
+ h.ExistingAppAssert{
+ App: app,
+ Assert: assert,
+ })
return AssertTagWithBodyRespDB(appAssert.App, appAssert.Assert, resp, SampleTagFactory(uuid))
}
-func CreateSampleTag(t *testing.T) (appAssert ExistingAppAssert, categoryUUID uuid.UUID, tagUUID uuid.UUID) {
- appAssert, categoryUUID = CreateSampleCategory(t, nil)
+func CreateSampleTag(appAssert h.ExistingAppAssert) (existingAppAssert h.ExistingAppAssert, categoryUUID uuid.UUID, tagUUID uuid.UUID) {
+ appAssert, categoryUUID = CreateSampleCategory(appAssert)
- AssertSampleTagBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertSampleTagBodyRespDB := func(app h.TestApp, assert *assert.A, resp *http.Response) {
tagUUID = AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory(categoryUUID))
}
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/tags/",
- Body: SampleTagFactory(categoryUUID),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
- Status: fiber.StatusCreated,
- DBTester: AssertSampleTagBodyRespDB,
+ appAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/tags/",
+ Body: SampleTagFactory(categoryUUID),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusCreated,
+ Tester: AssertSampleTagBodyRespDB,
},
)
@@ -74,11 +78,11 @@ func CreateSampleTag(t *testing.T) (appAssert ExistingAppAssert, categoryUUID uu
}
func TestCreateTagWorks(t *testing.T) {
- appAssert, _, _ := CreateSampleTag(t)
+ appAssert, _, _ := CreateSampleTag(h.InitTest(t))
appAssert.Close()
}
-func AssertNumTagsRemainsAtN(app TestApp, assert *assert.A, resp *http.Response, n int) {
+func AssertNumTagsRemainsAtN(app h.TestApp, assert *assert.A, resp *http.Response, n int) {
var tags []models.Tag
err := app.Conn.Find(&tags).Error
@@ -88,15 +92,17 @@ func AssertNumTagsRemainsAtN(app TestApp, assert *assert.A, resp *http.Response,
assert.Equal(n, len(tags))
}
-func AssertNoTags(app TestApp, assert *assert.A, resp *http.Response) {
+func AssertNoTags(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertNumTagsRemainsAtN(app, assert, resp, 0)
}
-func Assert1Tag(app TestApp, assert *assert.A, resp *http.Response) {
+func Assert1Tag(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertNumTagsRemainsAtN(app, assert, resp, 1)
}
func TestCreateTagFailsBadRequest(t *testing.T) {
+ appAssert := h.InitTest(t)
+
badBodys := []map[string]interface{}{
{
"name": "Generate",
@@ -109,20 +115,26 @@ func TestCreateTagFailsBadRequest(t *testing.T) {
}
for _, badBody := range badBodys {
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/tags/",
- Body: &badBody,
- }.TestOnErrorAndDB(t, nil,
- ErrorWithDBTester{
- Error: errors.FailedToParseRequestBody,
- DBTester: AssertNoTags,
+ appAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/tags/",
+ Body: &badBody,
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.FailedToParseRequestBody,
+ Tester: AssertNoTags,
},
- ).Close()
+ )
}
+
+ appAssert.Close()
}
func TestCreateTagFailsValidation(t *testing.T) {
+ appAssert := h.InitTest(t)
+
badBodys := []map[string]interface{}{
{
"name": "Generate",
@@ -134,29 +146,35 @@ func TestCreateTagFailsValidation(t *testing.T) {
}
for _, badBody := range badBodys {
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/tags/",
- Body: &badBody,
- }.TestOnErrorAndDB(t, nil,
- ErrorWithDBTester{
- Error: errors.FailedToValidateTag,
- DBTester: AssertNoTags,
+ appAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/tags/",
+ Body: &badBody,
+ Role: &models.Super,
},
- ).Close()
+ h.ErrorWithTester{
+ Error: errors.FailedToValidateTag,
+ Tester: AssertNoTags,
+ },
+ )
}
+
+ appAssert.Close()
}
func TestGetTagWorks(t *testing.T) {
- existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(t)
+ existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(h.InitTest(t))
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
- }.TestOnStatusAndDB(t, &existingAppAssert,
- DBTesterWithStatus{
+ existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
Status: fiber.StatusOK,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory(categoryUUID))
},
},
@@ -164,6 +182,8 @@ func TestGetTagWorks(t *testing.T) {
}
func TestGetTagFailsBadRequest(t *testing.T) {
+ appAssert := h.InitTest(t)
+
badRequests := []string{
"0",
"-1",
@@ -173,94 +193,112 @@ func TestGetTagFailsBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/tags/%s", badRequest),
- }.TestOnError(t, nil, errors.FailedToValidateID).Close()
+ appAssert.TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/tags/%s", badRequest),
+ Role: &models.Super,
+ },
+ errors.FailedToValidateID,
+ )
}
+
+ appAssert.Close()
}
func TestGetTagFailsNotFound(t *testing.T) {
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/tags/%s", uuid.New()),
- }.TestOnError(t, nil, errors.TagNotFound).Close()
+ h.InitTest(t).TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/tags/%s", uuid.New()),
+ Role: &models.Super,
+ },
+ errors.TagNotFound,
+ ).Close()
}
func TestUpdateTagWorksUpdateName(t *testing.T) {
- existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(t)
+ existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(h.InitTest(t))
generateNUTag := *SampleTagFactory(categoryUUID)
generateNUTag["name"] = "GenerateNU"
- AssertUpdatedTagBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertUpdatedTagBodyRespDB := func(app h.TestApp, assert *assert.A, resp *http.Response) {
tagUUID = AssertTagWithBodyRespDB(app, assert, resp, &generateNUTag)
}
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
- Body: &generateNUTag,
- }.TestOnStatusAndDB(t, &existingAppAssert,
- DBTesterWithStatus{
- Status: fiber.StatusOK,
- DBTester: AssertUpdatedTagBodyRespDB,
+ existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
+ Body: &generateNUTag,
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusOK,
+ Tester: AssertUpdatedTagBodyRespDB,
},
).Close()
}
func TestUpdateTagWorksUpdateCategory(t *testing.T) {
- existingAppAssert, _, tagUUID := CreateSampleTag(t)
+ existingAppAssert, _, tagUUID := CreateSampleTag(h.InitTest(t))
technologyCategory := *SampleCategoryFactory()
technologyCategory["name"] = "Technology"
var technologyCategoryUUID uuid.UUID
- AssertNewCategoryBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertNewCategoryBodyRespDB := func(app h.TestApp, assert *assert.A, resp *http.Response) {
technologyCategoryUUID = AssertCategoryWithBodyRespDBMostRecent(app, assert, resp, &technologyCategory)
}
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/categories/",
- Body: &technologyCategory,
- }.TestOnStatusAndDB(t, &existingAppAssert,
- DBTesterWithStatus{
- Status: fiber.StatusCreated,
- DBTester: AssertNewCategoryBodyRespDB,
+ existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/categories/",
+ Body: &technologyCategory,
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusCreated,
+ Tester: AssertNewCategoryBodyRespDB,
},
)
technologyTag := *SampleTagFactory(technologyCategoryUUID)
- AssertUpdatedTagBodyRespDB := func(app TestApp, assert *assert.A, resp *http.Response) {
+ AssertUpdatedTagBodyRespDB := func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertTagWithBodyRespDB(app, assert, resp, &technologyTag)
}
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
- Body: &technologyTag,
- }.TestOnStatusAndDB(t, &existingAppAssert,
- DBTesterWithStatus{
- Status: fiber.StatusOK,
- DBTester: AssertUpdatedTagBodyRespDB,
+ existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
+ Body: &technologyTag,
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusOK,
+ Tester: AssertUpdatedTagBodyRespDB,
},
).Close()
}
func TestUpdateTagWorksWithSameDetails(t *testing.T) {
- existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(t)
-
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
- Body: SampleTagFactory(categoryUUID),
- }.TestOnStatusAndDB(t, &existingAppAssert,
- DBTesterWithStatus{
+ existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(h.InitTest(t))
+
+ existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
+ Body: SampleTagFactory(categoryUUID),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
Status: fiber.StatusOK,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory(categoryUUID))
},
},
@@ -268,7 +306,7 @@ func TestUpdateTagWorksWithSameDetails(t *testing.T) {
}
func TestUpdateTagFailsBadRequest(t *testing.T) {
- appAssert, uuid := CreateSampleCategory(t, nil)
+ appAssert, uuid := CreateSampleCategory(h.InitTest(t))
badRequests := []string{
"0",
@@ -279,30 +317,38 @@ func TestUpdateTagFailsBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/tags/%s", badRequest),
- Body: SampleTagFactory(uuid),
- }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close()
+ appAssert.TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: fmt.Sprintf("/api/v1/tags/%s", badRequest),
+ Body: SampleTagFactory(uuid),
+ Role: &models.Super,
+ },
+ errors.FailedToValidateID,
+ )
}
+
+ appAssert.Close()
}
func TestDeleteTagWorks(t *testing.T) {
- existingAppAssert, _, tagUUID := CreateSampleTag(t)
-
- TestRequest{
- Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
- }.TestOnStatusAndDB(t, &existingAppAssert,
- DBTesterWithStatus{
- Status: fiber.StatusNoContent,
- DBTester: AssertNoTags,
+ existingAppAssert, _, tagUUID := CreateSampleTag(h.InitTest(t))
+
+ existingAppAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodDelete,
+ Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusNoContent,
+ Tester: AssertNoTags,
},
).Close()
}
func TestDeleteTagFailsBadRequest(t *testing.T) {
- appAssert, _, _ := CreateSampleTag(t)
+ appAssert, _, _ := CreateSampleTag(h.InitTest(t))
badRequests := []string{
"0",
@@ -313,13 +359,15 @@ func TestDeleteTagFailsBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/tags/%s", badRequest),
- }.TestOnErrorAndDB(t, &appAssert,
- ErrorWithDBTester{
- Error: errors.FailedToValidateID,
- DBTester: Assert1Tag,
+ appAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodDelete,
+ Path: fmt.Sprintf("/api/v1/tags/%s", badRequest),
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.FailedToValidateID,
+ Tester: Assert1Tag,
},
)
}
@@ -328,15 +376,17 @@ func TestDeleteTagFailsBadRequest(t *testing.T) {
}
func TestDeleteTagFailsNotFound(t *testing.T) {
- appAssert, _, _ := CreateSampleTag(t)
-
- TestRequest{
- Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/tags/%s", uuid.New()),
- }.TestOnErrorAndDB(t, &appAssert,
- ErrorWithDBTester{
- Error: errors.TagNotFound,
- DBTester: Assert1Tag,
+ appAssert, _, _ := CreateSampleTag(h.InitTest(t))
+
+ appAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodDelete,
+ Path: fmt.Sprintf("/api/v1/tags/%s", uuid.New()),
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.TagNotFound,
+ Tester: Assert1Tag,
},
).Close()
}
diff --git a/backend/tests/api/user_tag_test.go b/backend/tests/api/user_tag_test.go
index a67022a56..013b5ddb9 100644
--- a/backend/tests/api/user_tag_test.go
+++ b/backend/tests/api/user_tag_test.go
@@ -7,6 +7,7 @@ import (
"github.com/GenerateNU/sac/backend/src/errors"
"github.com/GenerateNU/sac/backend/src/models"
+ h "github.com/GenerateNU/sac/backend/tests/api/helpers"
"github.com/goccy/go-json"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
@@ -55,19 +56,26 @@ func SampleTagIDsFactory(tagIDs *[]uuid.UUID) *map[string]interface{} {
}
}
-func CreateSetOfTags(t *testing.T, appAssert ExistingAppAssert) []uuid.UUID {
+func CreateSetOfTags(t *testing.T, appAssert *h.ExistingAppAssert) ([]uuid.UUID, *h.ExistingAppAssert) {
+ if appAssert == nil {
+ newAppAssert := h.InitTest(t)
+ appAssert = &newAppAssert
+ }
+
categories := SampleCategoriesFactory()
categoryIDs := []uuid.UUID{}
for _, category := range *categories {
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/categories/",
- Body: &category,
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
+ appAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/categories/",
+ Body: &category,
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
Status: fiber.StatusCreated,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var respCategory models.Category
err := json.NewDecoder(resp.Body).Decode(&respCategory)
@@ -84,14 +92,16 @@ func CreateSetOfTags(t *testing.T, appAssert ExistingAppAssert) []uuid.UUID {
tagIDs := []uuid.UUID{}
for _, tag := range *tags {
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/tags/",
- Body: &tag,
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
+ appAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/tags/",
+ Body: &tag,
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
Status: fiber.StatusCreated,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var respTag models.Tag
err := json.NewDecoder(resp.Body).Decode(&respTag)
@@ -104,10 +114,10 @@ func CreateSetOfTags(t *testing.T, appAssert ExistingAppAssert) []uuid.UUID {
)
}
- return tagIDs
+ return tagIDs, appAssert
}
-func AssertUserTagsRespDB(app TestApp, assert *assert.A, resp *http.Response, id uuid.UUID) {
+func AssertUserTagsRespDB(app h.TestApp, assert *assert.A, resp *http.Response, id uuid.UUID) {
var respTags []models.Tag
// Retrieve the tags from the response:
@@ -135,7 +145,7 @@ func AssertUserTagsRespDB(app TestApp, assert *assert.A, resp *http.Response, id
}
}
-func AssertSampleUserTagsRespDB(app TestApp, assert *assert.A, resp *http.Response, uuid uuid.UUID) {
+func AssertSampleUserTagsRespDB(app h.TestApp, assert *assert.A, resp *http.Response, uuid uuid.UUID) {
AssertUserTagsRespDB(app, assert, resp, uuid)
}
@@ -152,11 +162,16 @@ func TestCreateUserTagsFailsOnInvalidDataType(t *testing.T) {
malformedTag := *SampleTagIDsFactory(nil)
malformedTag["tags"] = tag
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/users/1/tags/",
- Body: &malformedTag,
- }.TestOnError(t, nil, errors.FailedToParseRequestBody).Close()
+ h.InitTest(t).TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/users/:userID/tags/",
+ Body: &malformedTag,
+ Role: &models.Student,
+ TestUserIDReplaces: h.StringToPointer(":userID"),
+ },
+ errors.FailedToParseRequestBody,
+ ).Close()
}
}
@@ -170,11 +185,15 @@ func TestCreateUserTagsFailsOnInvalidUserID(t *testing.T) {
}
for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/users/%s/tags", badRequest),
- Body: SampleTagIDsFactory(nil),
- }.TestOnError(t, nil, errors.FailedToValidateID).Close()
+ h.InitTest(t).TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: fmt.Sprintf("/api/v1/users/%s/tags", badRequest),
+ Body: SampleTagIDsFactory(nil),
+ Role: &models.Student,
+ },
+ errors.FailedToValidateID,
+ ).Close()
}
}
@@ -183,8 +202,6 @@ type UUIDSlice []uuid.UUID
var testUUID = uuid.New()
func TestCreateUserTagsFailsOnInvalidKey(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
-
invalidBody := []map[string]interface{}{
{
"tag": UUIDSlice{testUUID, testUUID},
@@ -195,40 +212,58 @@ func TestCreateUserTagsFailsOnInvalidKey(t *testing.T) {
}
for _, body := range invalidBody {
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
- Body: &body,
- }.TestOnError(t, &appAssert, errors.FailedToValidateUserTags)
+ h.InitTest(t).TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/users/:userID/tags/",
+ Body: &body,
+ Role: &models.Student,
+ TestUserIDReplaces: h.StringToPointer(":userID"),
+ },
+ errors.FailedToValidateUserTags,
+ ).Close()
}
-
- appAssert.Close()
}
func TestCreateUserTagsFailsOnNonExistentUser(t *testing.T) {
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/users/%s/tags", uuid.New()),
- Body: SampleTagIDsFactory(nil),
- }.TestOnError(t, nil, errors.UserNotFound).Close()
+ uuid := uuid.New()
+
+ h.InitTest(t).TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
+ Body: SampleTagIDsFactory(nil),
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.UserNotFound,
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ var dbUser models.User
+ err := app.Conn.First(&dbUser, uuid).Error
+
+ assert.Assert(err != nil)
+ },
+ },
+ ).Close()
}
func TestCreateUserTagsWorks(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
-
// Create a set of tags:
- tagUUIDs := CreateSetOfTags(t, appAssert)
+ tagUUIDs, appAssert := CreateSetOfTags(t, nil)
// Confirm adding real tags adds them to the user:
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
- Body: SampleTagIDsFactory(&tagUUIDs),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
+ appAssert.TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/users/:userID/tags/",
+ Body: SampleTagIDsFactory(&tagUUIDs),
+ Role: &models.Super,
+ TestUserIDReplaces: h.StringToPointer(":userID"),
+ },
+ h.TesterWithStatus{
Status: fiber.StatusCreated,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
- AssertSampleUserTagsRespDB(app, assert, resp, uuid)
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ AssertSampleUserTagsRespDB(app, assert, resp, app.TestUser.UUID)
},
},
)
@@ -237,16 +272,17 @@ func TestCreateUserTagsWorks(t *testing.T) {
}
func TestCreateUserTagsNoneAddedIfInvalid(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
-
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
- Body: SampleTagIDsFactory(nil),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
+ h.InitTest(t).TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/users/:userID/tags/",
+ Body: SampleTagIDsFactory(nil),
+ Role: &models.Super,
+ TestUserIDReplaces: h.StringToPointer(":userID"),
+ },
+ h.TesterWithStatus{
Status: fiber.StatusCreated,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var respTags []models.Tag
err := json.NewDecoder(resp.Body).Decode(&respTags)
@@ -256,28 +292,30 @@ func TestCreateUserTagsNoneAddedIfInvalid(t *testing.T) {
assert.Equal(len(respTags), 0)
},
},
- )
-
- appAssert.Close()
+ ).Close()
}
func TestGetUserTagsFailsOnNonExistentUser(t *testing.T) {
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid.New()),
- }.TestOnError(t, nil, errors.UserNotFound).Close()
+ h.InitTest(t).TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid.New()),
+ Role: &models.Super,
+ }, errors.UserNotFound,
+ ).Close()
}
func TestGetUserTagsReturnsEmptyListWhenNoneAdded(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
-
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
- Status: 200,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ h.InitTest(t).TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: "/api/v1/users/:userID/tags/",
+ Role: &models.Student,
+ TestUserIDReplaces: h.StringToPointer(":userID"),
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusOK,
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var respTags []models.Tag
err := json.NewDecoder(resp.Body).Decode(&respTags)
@@ -287,36 +325,35 @@ func TestGetUserTagsReturnsEmptyListWhenNoneAdded(t *testing.T) {
assert.Equal(len(respTags), 0)
},
},
- )
-
- appAssert.Close()
+ ).Close()
}
func TestGetUserTagsReturnsCorrectList(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
+ tagUUIDs, appAssert := CreateSetOfTags(t, nil)
- // Create a set of tags:
- tagUUIDs := CreateSetOfTags(t, appAssert)
-
- // Add the tags:
- TestRequest{
- Method: fiber.MethodPost,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
- Body: SampleTagIDsFactory(&tagUUIDs),
- }.TestOnStatus(t, &appAssert, fiber.StatusCreated)
-
- // Get the tags:
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
+ newAppAssert := *appAssert
+
+ newAppAssert.TestOnStatus(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/users/:userID/tags/",
+ Body: SampleTagIDsFactory(&tagUUIDs),
+ Role: &models.Student,
+ TestUserIDReplaces: h.StringToPointer(":userID"),
+ },
+ fiber.StatusCreated,
+ ).TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: "/api/v1/users/:userID/tags/",
+ Role: &models.Student,
+ TestUserIDReplaces: h.StringToPointer(":userID"),
+ },
+ h.TesterWithStatus{
Status: fiber.StatusOK,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
- AssertSampleUserTagsRespDB(app, assert, resp, uuid)
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ AssertSampleUserTagsRespDB(app, assert, resp, app.TestUser.UUID)
},
},
- )
-
- appAssert.Close()
+ ).Close()
}
diff --git a/backend/tests/api/user_test.go b/backend/tests/api/user_test.go
index a1f74a8c8..5ba723114 100644
--- a/backend/tests/api/user_test.go
+++ b/backend/tests/api/user_test.go
@@ -11,6 +11,7 @@ import (
"github.com/GenerateNU/sac/backend/src/errors"
"github.com/GenerateNU/sac/backend/src/models"
"github.com/GenerateNU/sac/backend/src/transactions"
+ h "github.com/GenerateNU/sac/backend/tests/api/helpers"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"gorm.io/gorm"
@@ -19,14 +20,16 @@ import (
"github.com/huandu/go-assert"
)
-func TestGetUsersWorks(t *testing.T) {
- TestRequest{
- Method: fiber.MethodGet,
- Path: "/api/v1/users/",
- }.TestOnStatusAndDB(t, nil,
- DBTesterWithStatus{
+func TestGetUsersWorksForSuper(t *testing.T) {
+ h.InitTest(t).TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: "/api/v1/users/",
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
Status: fiber.StatusOK,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var users []models.User
err := json.NewDecoder(resp.Body).Decode(&users)
@@ -58,23 +61,37 @@ func TestGetUsersWorks(t *testing.T) {
).Close()
}
-func TestGetUserWorks(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
+func TestGetUsersFailsForStudent(t *testing.T) {
+ h.InitTest(t).TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: "/api/v1/users/",
+ Role: &models.Student,
+ },
+ errors.Unauthorized,
+ ).Close()
+}
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/users/%s", uuid),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
+func TestGetUserWorks(t *testing.T) {
+ h.InitTest(t).TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: "/api/v1/users/:userID",
+ Role: &models.Student,
+ TestUserIDReplaces: h.StringToPointer(":userID"),
+ },
+ h.TesterWithStatus{
Status: fiber.StatusOK,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var respUser models.User
err := json.NewDecoder(resp.Body).Decode(&respUser)
assert.NilError(err)
- sampleUser := *SampleUserFactory()
+ sampleStudent, rawPassword := h.SampleStudentFactory()
+
+ sampleUser := *h.SampleStudentJSONFactory(sampleStudent, rawPassword)
assert.Equal(sampleUser["first_name"].(string), respUser.FirstName)
assert.Equal(sampleUser["last_name"].(string), respUser.LastName)
@@ -83,7 +100,7 @@ func TestGetUserWorks(t *testing.T) {
assert.Equal(models.College(sampleUser["college"].(string)), respUser.College)
assert.Equal(models.Year(sampleUser["year"].(int)), respUser.Year)
- dbUser, err := transactions.GetUser(app.Conn, uuid)
+ dbUser, err := transactions.GetUser(app.Conn, app.TestUser.UUID)
assert.NilError(&err)
@@ -94,6 +111,8 @@ func TestGetUserWorks(t *testing.T) {
}
func TestGetUserFailsBadRequest(t *testing.T) {
+ appAssert := h.InitTest(t)
+
badRequests := []string{
"0",
"-1",
@@ -103,23 +122,31 @@ func TestGetUserFailsBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/users/%s", badRequest),
- }.TestOnError(t, nil, errors.FailedToValidateID).Close()
+ appAssert.TestOnError(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/users/%s", badRequest),
+ Role: &models.Super,
+ },
+ errors.FailedToValidateID,
+ )
}
+
+ appAssert.Close()
}
func TestGetUserFailsNotExist(t *testing.T) {
uuid := uuid.New()
- TestRequest{
- Method: fiber.MethodGet,
- Path: fmt.Sprintf("/api/v1/users/%s", uuid),
- }.TestOnErrorAndDB(t, nil,
- ErrorWithDBTester{
+ h.InitTest(t).TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/users/%s", uuid),
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
Error: errors.UserNotFound,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var user models.User
err := app.Conn.Where("id = ?", uuid).First(&user).Error
@@ -131,38 +158,43 @@ func TestGetUserFailsNotExist(t *testing.T) {
}
func TestUpdateUserWorks(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
-
newFirstName := "Michael"
newLastName := "Brennan"
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/users/%s", uuid),
- Body: &map[string]interface{}{
- "first_name": newFirstName,
- "last_name": newLastName,
+ h.InitTest(t).TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: "/api/v1/users/:userID",
+ Body: &map[string]interface{}{
+ "first_name": newFirstName,
+ "last_name": newLastName,
+ },
+ Role: &models.Student,
+ TestUserIDReplaces: h.StringToPointer(":userID"),
},
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
+ h.TesterWithStatus{
Status: fiber.StatusOK,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var respUser models.User
err := json.NewDecoder(resp.Body).Decode(&respUser)
assert.NilError(err)
+ sampleStudent, rawPassword := h.SampleStudentFactory()
+
+ sampleStudentJSON := *h.SampleStudentJSONFactory(sampleStudent, rawPassword)
+
assert.Equal(newFirstName, respUser.FirstName)
assert.Equal(newLastName, respUser.LastName)
- assert.Equal((*SampleUserFactory())["email"].(string), respUser.Email)
- assert.Equal((*SampleUserFactory())["nuid"].(string), respUser.NUID)
- assert.Equal(models.College((*SampleUserFactory())["college"].(string)), respUser.College)
- assert.Equal(models.Year((*SampleUserFactory())["year"].(int)), respUser.Year)
+ assert.Equal((sampleStudentJSON)["email"].(string), respUser.Email)
+ assert.Equal((sampleStudentJSON)["nuid"].(string), respUser.NUID)
+ assert.Equal(models.College((sampleStudentJSON)["college"].(string)), respUser.College)
+ assert.Equal(models.Year((sampleStudentJSON)["year"].(int)), respUser.Year)
var dbUser models.User
- err = app.Conn.First(&dbUser, uuid).Error
+ err = app.Conn.First(&dbUser, app.TestUser.UUID).Error
assert.NilError(err)
@@ -178,27 +210,26 @@ func TestUpdateUserWorks(t *testing.T) {
}
func TestUpdateUserFailsOnInvalidBody(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
-
for _, invalidData := range []map[string]interface{}{
{"email": "not.northeastern@gmail.com"},
{"nuid": "1800-123-4567"},
- {"password": "1234"},
{"year": 1963},
{"college": "UT-Austin"},
} {
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/users/%s", uuid),
- Body: &invalidData,
- }.TestOnErrorAndDB(t, &appAssert,
- ErrorWithDBTester{
- Error: errors.FailedToValidateUser,
- DBTester: TestNumUsersRemainsAt2,
+ h.InitTest(t).TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: "/api/v1/users/:userID",
+ Body: &invalidData,
+ Role: &models.Student,
+ TestUserIDReplaces: h.StringToPointer(":userID"),
},
- )
+ h.ErrorWithTester{
+ Error: errors.FailedToValidateUser,
+ Tester: TestNumUsersRemainsAt2,
+ },
+ ).Close()
}
- appAssert.Close()
}
func TestUpdateUserFailsBadRequest(t *testing.T) {
@@ -210,26 +241,37 @@ func TestUpdateUserFailsBadRequest(t *testing.T) {
"null",
}
+ sampleStudent, rawPassword := h.SampleStudentFactory()
+ slightlyDifferentSampleStudentJSON := h.SampleStudentJSONFactory(sampleStudent, rawPassword)
+ (*slightlyDifferentSampleStudentJSON)["first_name"] = "John"
+
for _, badRequest := range badRequests {
- TestRequest{
+ h.InitTest(t).TestOnError(h.TestRequest{
Method: fiber.MethodPatch,
Path: fmt.Sprintf("/api/v1/users/%s", badRequest),
- Body: SampleUserFactory(),
- }.TestOnError(t, nil, errors.FailedToValidateID).Close()
+ Body: slightlyDifferentSampleStudentJSON,
+ Role: &models.Student,
+ },
+ errors.FailedToValidateID,
+ ).Close()
}
}
func TestUpdateUserFailsOnIdNotExist(t *testing.T) {
uuid := uuid.New()
- TestRequest{
- Method: fiber.MethodPatch,
- Path: fmt.Sprintf("/api/v1/users/%s", uuid),
- Body: SampleUserFactory(),
- }.TestOnErrorAndDB(t, nil,
- ErrorWithDBTester{
+ sampleStudent, rawPassword := h.SampleStudentFactory()
+
+ h.InitTest(t).TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPatch,
+ Path: fmt.Sprintf("/api/v1/users/%s", uuid),
+ Body: h.SampleStudentJSONFactory(sampleStudent, rawPassword),
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
Error: errors.UserNotFound,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var user models.User
err := app.Conn.Where("id = ?", uuid).First(&user).Error
@@ -241,28 +283,31 @@ func TestUpdateUserFailsOnIdNotExist(t *testing.T) {
}
func TestDeleteUserWorks(t *testing.T) {
- appAssert, uuid := CreateSampleUser(t, nil)
-
- TestRequest{
- Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/users/%s", uuid),
- }.TestOnStatusAndDB(t, &appAssert,
- DBTesterWithStatus{
- Status: fiber.StatusNoContent,
- DBTester: TestNumUsersRemainsAt1,
+ h.InitTest(t).TestOnStatusAndDB(
+ h.TestRequest{
+ Method: fiber.MethodDelete,
+ Path: "/api/v1/users/:userID",
+ Role: &models.Student,
+ TestUserIDReplaces: h.StringToPointer(":userID"),
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusNoContent,
+ Tester: TestNumUsersRemainsAt1,
},
).Close()
}
func TestDeleteUserNotExist(t *testing.T) {
uuid := uuid.New()
- TestRequest{
+
+ h.InitTest(t).TestOnErrorAndDB(h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/users/%s", uuid),
- }.TestOnErrorAndDB(t, nil,
- ErrorWithDBTester{
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
Error: errors.UserNotFound,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
var user models.User
err := app.Conn.Where("id = ?", uuid).First(&user).Error
@@ -276,6 +321,8 @@ func TestDeleteUserNotExist(t *testing.T) {
}
func TestDeleteUserBadRequest(t *testing.T) {
+ appAssert := h.InitTest(t)
+
badRequests := []string{
"0",
"-1",
@@ -285,31 +332,23 @@ func TestDeleteUserBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- TestRequest{
- Method: fiber.MethodDelete,
- Path: fmt.Sprintf("/api/v1/users/%s", badRequest),
- }.TestOnErrorAndDB(t, nil,
- ErrorWithDBTester{
- Error: errors.FailedToValidateID,
- DBTester: TestNumUsersRemainsAt1,
+ appAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodDelete,
+ Path: fmt.Sprintf("/api/v1/users/%s", badRequest),
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.FailedToValidateID,
+ Tester: TestNumUsersRemainsAt1,
},
)
}
-}
-func SampleUserFactory() *map[string]interface{} {
- return &map[string]interface{}{
- "first_name": "Jane",
- "last_name": "Doe",
- "email": "doe.jane@northeastern.edu",
- "password": "1234567890&",
- "nuid": "001234567",
- "college": "KCCS",
- "year": 3,
- }
+ appAssert.Close()
}
-func AssertUserWithIDBodyRespDB(app TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
+func AssertUserWithIDBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
var respUser models.User
err := json.NewDecoder(resp.Body).Decode(&respUser)
@@ -349,34 +388,40 @@ func AssertUserWithIDBodyRespDB(app TestApp, assert *assert.A, resp *http.Respon
return dbUser.ID
}
-func AssertSampleUserBodyRespDB(app TestApp, assert *assert.A, resp *http.Response) uuid.UUID {
- return AssertUserWithIDBodyRespDB(app, assert, resp, SampleUserFactory())
+func AssertSampleUserBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response) uuid.UUID {
+ sampleStudent, rawPassword := h.SampleStudentFactory()
+
+ return AssertUserWithIDBodyRespDB(app, assert, resp, h.SampleStudentJSONFactory(sampleStudent, rawPassword))
}
-func CreateSampleUser(t *testing.T, existingAppAssert *ExistingAppAssert) (ExistingAppAssert, uuid.UUID) {
+func CreateSampleStudent(t *testing.T, existingAppAssert *h.ExistingAppAssert) (h.ExistingAppAssert, uuid.UUID, *map[string]interface{}) {
+ if existingAppAssert == nil {
+ newAppAssert := h.InitTest(t)
+ existingAppAssert = &newAppAssert
+ }
+
var uuid uuid.UUID
- newAppAssert := TestRequest{
+ sampleStudent, rawPassword := h.SampleStudentFactory()
+
+ existingAppAssert.TestOnStatusAndDB(h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/users/",
- Body: SampleUserFactory(),
- }.TestOnStatusAndDB(t, existingAppAssert,
- DBTesterWithStatus{
+ Body: h.SampleStudentJSONFactory(sampleStudent, rawPassword),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
Status: fiber.StatusCreated,
- DBTester: func(app TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
uuid = AssertSampleUserBodyRespDB(app, assert, resp)
},
},
)
- if existingAppAssert == nil {
- return newAppAssert, uuid
- } else {
- return *existingAppAssert, uuid
- }
+ return *existingAppAssert, uuid, h.SampleStudentJSONFactory(sampleStudent, rawPassword)
}
-func AssertNumUsersRemainsAtN(app TestApp, assert *assert.A, resp *http.Response, n int) {
+func AssertNumUsersRemainsAtN(app h.TestApp, assert *assert.A, resp *http.Response, n int) {
var users []models.User
err := app.Conn.Find(&users).Error
@@ -386,79 +431,86 @@ func AssertNumUsersRemainsAtN(app TestApp, assert *assert.A, resp *http.Response
assert.Equal(n, len(users))
}
-var TestNumUsersRemainsAt1 = func(app TestApp, assert *assert.A, resp *http.Response) {
+var TestNumUsersRemainsAt1 = func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertNumUsersRemainsAtN(app, assert, resp, 1)
}
-var TestNumUsersRemainsAt2 = func(app TestApp, assert *assert.A, resp *http.Response) {
+var TestNumUsersRemainsAt2 = func(app h.TestApp, assert *assert.A, resp *http.Response) {
AssertNumUsersRemainsAtN(app, assert, resp, 2)
}
func TestCreateUserWorks(t *testing.T) {
- appAssert, _ := CreateSampleUser(t, nil)
+ appAssert, _, _ := CreateSampleStudent(t, nil)
appAssert.Close()
}
func TestCreateUserFailsIfUserWithEmailAlreadyExists(t *testing.T) {
- appAssert, _ := CreateSampleUser(t, nil)
+ appAssert, studentUUID, body := CreateSampleStudent(t, nil)
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/users/",
- Body: SampleUserFactory(),
- }.TestOnErrorAndDB(t, &appAssert,
- ErrorWithDBTester{
- Error: errors.UserAlreadyExists,
- DBTester: TestNumUsersRemainsAt2,
- },
- )
+ (*body)["id"] = studentUUID
- appAssert.Close()
+ appAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/users/",
+ Body: body,
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.UserAlreadyExists,
+ Tester: TestNumUsersRemainsAt2,
+ },
+ ).Close()
}
func TestCreateUserFailsIfUserWithNUIDAlreadyExists(t *testing.T) {
- appAssert, _ := CreateSampleUser(t, nil)
-
- slightlyDifferentSampleUser := &map[string]interface{}{
- "first_name": "John",
- "last_name": "Doe",
- "email": "doe.john@northeastern.edu",
- "password": "1234567890&",
- "nuid": "001234567",
- "college": "KCCS",
- "year": 3,
- }
+ appAssert, _, _ := CreateSampleStudent(t, nil)
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/users/",
- Body: slightlyDifferentSampleUser,
- }.TestOnErrorAndDB(t, &appAssert,
- ErrorWithDBTester{
- Error: errors.UserAlreadyExists,
- DBTester: TestNumUsersRemainsAt2,
+ sampleStudent, rawPassword := h.SampleStudentFactory()
+
+ slightlyDifferentSampleStudentJSON := h.SampleStudentJSONFactory(sampleStudent, rawPassword)
+
+ (*slightlyDifferentSampleStudentJSON)["first_name"] = "John"
+ (*slightlyDifferentSampleStudentJSON)["last_name"] = "Doe"
+ (*slightlyDifferentSampleStudentJSON)["email"] = "doe.john@northeastern.edu"
+
+ appAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/users/",
+ Body: slightlyDifferentSampleStudentJSON,
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.UserAlreadyExists,
+ Tester: TestNumUsersRemainsAt2,
},
).Close()
}
func AssertCreateBadDataFails(t *testing.T, jsonKey string, badValues []interface{}) {
- appAssert, _ := CreateSampleUser(t, nil)
+ appAssert, _, _ := CreateSampleStudent(t, nil)
+
+ sampleStudent, rawPassword := h.SampleStudentFactory()
for _, badValue := range badValues {
- sampleUserPermutation := *SampleUserFactory()
+ sampleUserPermutation := *h.SampleStudentJSONFactory(sampleStudent, rawPassword)
sampleUserPermutation[jsonKey] = badValue
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/users/",
- Body: &sampleUserPermutation,
- }.TestOnErrorAndDB(t, &appAssert,
- ErrorWithDBTester{
- Error: errors.FailedToValidateUser,
- DBTester: TestNumUsersRemainsAt2,
+ appAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/users/",
+ Body: &sampleUserPermutation,
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.FailedToValidateUser,
+ Tester: TestNumUsersRemainsAt2,
},
)
}
+
appAssert.Close()
}
@@ -509,7 +561,7 @@ func TestCreateUserFailsOnInvalidYear(t *testing.T) {
func TestCreateUserFailsOnInvalidCollege(t *testing.T) {
khouryAbbreviation := "KCCS"
- permutations := AllCasingPermutations(khouryAbbreviation)
+ permutations := h.AllCasingPermutations(khouryAbbreviation)
permutationsWithoutKhoury := make([]interface{}, len(permutations)-1)
for _, permutation := range permutations {
if permutation != khouryAbbreviation {
@@ -523,7 +575,9 @@ func TestCreateUserFailsOnInvalidCollege(t *testing.T) {
}
func TestCreateUserFailsOnMissingFields(t *testing.T) {
- appAssert, _ := CreateSampleUser(t, nil)
+ appAssert, _, _ := CreateSampleStudent(t, nil)
+
+ sampleStudent, rawPassword := h.SampleStudentFactory()
for _, missingField := range []string{
"first_name",
@@ -534,19 +588,22 @@ func TestCreateUserFailsOnMissingFields(t *testing.T) {
"college",
"year",
} {
- sampleUserPermutation := *SampleUserFactory()
+ sampleUserPermutation := *h.SampleStudentJSONFactory(sampleStudent, rawPassword)
delete(sampleUserPermutation, missingField)
- TestRequest{
- Method: fiber.MethodPost,
- Path: "/api/v1/users/",
- Body: &sampleUserPermutation,
- }.TestOnErrorAndDB(t, &appAssert,
- ErrorWithDBTester{
- Error: errors.FailedToValidateUser,
- DBTester: TestNumUsersRemainsAt2,
+ appAssert.TestOnErrorAndDB(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: "/api/v1/users/",
+ Body: &sampleUserPermutation,
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.FailedToValidateUser,
+ Tester: TestNumUsersRemainsAt2,
},
)
}
+
appAssert.Close()
}
diff --git a/frontend/node_modules/.yarn-integrity b/frontend/node_modules/.yarn-integrity
deleted file mode 100644
index 044a5ddd9..000000000
--- a/frontend/node_modules/.yarn-integrity
+++ /dev/null
@@ -1,10 +0,0 @@
-{
- "systemParams": "darwin-arm64-115",
- "modulesFolders": [],
- "flags": [],
- "linkedModules": [],
- "topLevelPatterns": [],
- "lockfileEntries": {},
- "files": [],
- "artifacts": {}
-}
\ No newline at end of file
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
deleted file mode 100644
index fb57ccd13..000000000
--- a/frontend/yarn.lock
+++ /dev/null
@@ -1,4 +0,0 @@
-# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
-# yarn lockfile v1
-
-
From de02e9b074bc60d8fbdf6f70cc4f344302e5c2ae Mon Sep 17 00:00:00 2001
From: David Oduneye <44040421+DOOduneye@users.noreply.github.com>
Date: Sun, 4 Feb 2024 23:52:08 -0500
Subject: [PATCH 11/33] Update README.md (#148)
---
README.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/README.md b/README.md
index cf5aa1666..7de95e555 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,10 @@
+## Repo Activity
+
+
+
## Contributors
From b54d0d1e81c99255fbc7d11af58efb2133a368da Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 09:35:44 -0500
Subject: [PATCH 12/33] Bump @types/node from 20.11.10 to 20.11.16 in
/frontend/sac-web (#149)
---
frontend/sac-web/package-lock.json | 6 +++---
frontend/sac-web/yarn.lock | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/frontend/sac-web/package-lock.json b/frontend/sac-web/package-lock.json
index 9d638ebed..aac743899 100644
--- a/frontend/sac-web/package-lock.json
+++ b/frontend/sac-web/package-lock.json
@@ -500,9 +500,9 @@
"dev": true
},
"node_modules/@types/node": {
- "version": "20.11.10",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.10.tgz",
- "integrity": "sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==",
+ "version": "20.11.16",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz",
+ "integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
diff --git a/frontend/sac-web/yarn.lock b/frontend/sac-web/yarn.lock
index e669abf45..9ea96ebbc 100644
--- a/frontend/sac-web/yarn.lock
+++ b/frontend/sac-web/yarn.lock
@@ -215,9 +215,9 @@
integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==
"@types/node@^20":
- version "20.11.10"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.10.tgz#6c3de8974d65c362f82ee29db6b5adf4205462f9"
- integrity sha512-rZEfe/hJSGYmdfX9tvcPMYeYPW2sNl50nsw4jZmRcaG0HIAb0WYEpsB05GOb53vjqpyE9GUhlDQ4jLSoB5q9kg==
+ version "20.11.16"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.16.tgz#4411f79411514eb8e2926f036c86c9f0e4ec6708"
+ integrity sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==
dependencies:
undici-types "~5.26.4"
From effb1ac0e158389bb6b2683cf05e812e0560ca1e Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 09:36:09 -0500
Subject: [PATCH 13/33] Bump @types/react from 18.2.48 to 18.2.53 in
/frontend/sac-web (#150)
---
frontend/sac-web/package-lock.json | 6 +++---
frontend/sac-web/yarn.lock | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/frontend/sac-web/package-lock.json b/frontend/sac-web/package-lock.json
index aac743899..528b4389c 100644
--- a/frontend/sac-web/package-lock.json
+++ b/frontend/sac-web/package-lock.json
@@ -515,9 +515,9 @@
"dev": true
},
"node_modules/@types/react": {
- "version": "18.2.48",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.48.tgz",
- "integrity": "sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==",
+ "version": "18.2.53",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.53.tgz",
+ "integrity": "sha512-52IHsMDT8qATp9B9zoOyobW8W3/0QhaJQTw1HwRj0UY2yBpCAQ7+S/CqHYQ8niAm3p4ji+rWUQ9UCib0GxQ60w==",
"dev": true,
"dependencies": {
"@types/prop-types": "*",
diff --git a/frontend/sac-web/yarn.lock b/frontend/sac-web/yarn.lock
index 9ea96ebbc..c9cebbb1a 100644
--- a/frontend/sac-web/yarn.lock
+++ b/frontend/sac-web/yarn.lock
@@ -234,9 +234,9 @@
"@types/react" "*"
"@types/react@*", "@types/react@^18":
- version "18.2.48"
- resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.48.tgz#11df5664642d0bd879c1f58bc1d37205b064e8f1"
- integrity sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==
+ version "18.2.53"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.53.tgz#09c21b4621aaad5fed6a5045b33a7430749d8dc5"
+ integrity sha512-52IHsMDT8qATp9B9zoOyobW8W3/0QhaJQTw1HwRj0UY2yBpCAQ7+S/CqHYQ8niAm3p4ji+rWUQ9UCib0GxQ60w==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
From 0d099ade1c026f47f468f910e6f9da15a2308a7e Mon Sep 17 00:00:00 2001
From: Olivier John Nzia <116421013+ojn03@users.noreply.github.com>
Date: Mon, 5 Feb 2024 11:23:24 -0500
Subject: [PATCH 14/33] edit sac-cli clean command to only drop dbs with
"sac_test_" prefix (#159)
---
cli/commands/clean_tests.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cli/commands/clean_tests.go b/cli/commands/clean_tests.go
index a984d6c77..e3d4c6973 100644
--- a/cli/commands/clean_tests.go
+++ b/cli/commands/clean_tests.go
@@ -48,7 +48,7 @@ func CleanTestDBs() error {
return fmt.Errorf("failed to get current user: %w", err)
}
- rows, err := db.Query("SELECT datname FROM pg_database WHERE datistemplate = false AND datname != 'postgres' AND datname != $1 AND datname != $2", currentUser.Username, CONFIG.Database.DatabaseName)
+ rows, err := db.Query("SELECT datname FROM pg_database WHERE datistemplate = false AND datname != 'postgres' AND datname != $1 AND datname != $2 AND datname LIKE 'sac_test_%';", currentUser.Username, CONFIG.Database.DatabaseName)
if err != nil {
return err
}
From db0731fa5626d1d152f100586d7a0845b9c89903 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 11:39:44 -0500
Subject: [PATCH 15/33] Bump expo from 49.0.22 to 50.0.4 in
/frontend/sac-mobile (#151)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
---
frontend/sac-mobile/package.json | 2 +-
frontend/sac-mobile/yarn.lock | 694 +++++++++++++++----------------
2 files changed, 347 insertions(+), 349 deletions(-)
diff --git a/frontend/sac-mobile/package.json b/frontend/sac-mobile/package.json
index deaf689b6..012aadf5c 100644
--- a/frontend/sac-mobile/package.json
+++ b/frontend/sac-mobile/package.json
@@ -21,7 +21,7 @@
"@tanstack/react-query": "^5.17.9",
"clsx": "^2.1.0",
"eslint-config-prettier": "^9.1.0",
- "expo": "~49.0.22",
+ "expo": "~50.0.4",
"expo-auth-session": "~5.4.0",
"expo-font": "~11.10.2",
"expo-linking": "~6.2.2",
diff --git a/frontend/sac-mobile/yarn.lock b/frontend/sac-mobile/yarn.lock
index da5a4231e..70591e420 100644
--- a/frontend/sac-mobile/yarn.lock
+++ b/frontend/sac-mobile/yarn.lock
@@ -84,7 +84,7 @@
jsesc "^2.5.1"
source-map "^0.5.0"
-"@babel/generator@^7.18.7", "@babel/generator@^7.20.0", "@babel/generator@^7.23.0", "@babel/generator@^7.23.6", "@babel/generator@^7.7.2":
+"@babel/generator@^7.18.7", "@babel/generator@^7.20.0", "@babel/generator@^7.20.5", "@babel/generator@^7.23.0", "@babel/generator@^7.23.6", "@babel/generator@^7.7.2":
version "7.23.6"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e"
integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==
@@ -213,7 +213,7 @@
dependencies:
"@babel/types" "^7.22.5"
-"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
+"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295"
integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==
@@ -363,14 +363,6 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-syntax-export-default-from" "^7.23.3"
-"@babel/plugin-proposal-export-namespace-from@^7.18.9":
- version "7.18.9"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203"
- integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==
- dependencies:
- "@babel/helper-plugin-utils" "^7.18.9"
- "@babel/plugin-syntax-export-namespace-from" "^7.8.3"
-
"@babel/plugin-proposal-nullish-coalescing-operator@^7.13.8", "@babel/plugin-proposal-nullish-coalescing-operator@^7.18.0":
version "7.18.6"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1"
@@ -387,7 +379,7 @@
"@babel/helper-plugin-utils" "^7.18.6"
"@babel/plugin-syntax-numeric-separator" "^7.10.4"
-"@babel/plugin-proposal-object-rest-spread@^7.0.0", "@babel/plugin-proposal-object-rest-spread@^7.12.13", "@babel/plugin-proposal-object-rest-spread@^7.20.0":
+"@babel/plugin-proposal-object-rest-spread@^7.0.0", "@babel/plugin-proposal-object-rest-spread@^7.20.0":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a"
integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==
@@ -706,7 +698,7 @@
"@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.15"
"@babel/helper-plugin-utils" "^7.22.5"
-"@babel/plugin-transform-export-namespace-from@^7.23.4":
+"@babel/plugin-transform-export-namespace-from@^7.22.11", "@babel/plugin-transform-export-namespace-from@^7.23.4":
version "7.23.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.23.4.tgz#084c7b25e9a5c8271e987a08cf85807b80283191"
integrity sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==
@@ -835,7 +827,7 @@
"@babel/helper-plugin-utils" "^7.22.5"
"@babel/plugin-syntax-numeric-separator" "^7.10.4"
-"@babel/plugin-transform-object-rest-spread@^7.23.4":
+"@babel/plugin-transform-object-rest-spread@^7.12.13", "@babel/plugin-transform-object-rest-spread@^7.23.4":
version "7.23.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.23.4.tgz#2b9c2d26bf62710460bdc0d1730d4f1048361b83"
integrity sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==
@@ -871,14 +863,14 @@
"@babel/helper-skip-transparent-expression-wrappers" "^7.22.5"
"@babel/plugin-syntax-optional-chaining" "^7.8.3"
-"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.23.3":
+"@babel/plugin-transform-parameters@^7.0.0", "@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.22.15", "@babel/plugin-transform-parameters@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.23.3.tgz#83ef5d1baf4b1072fa6e54b2b0999a7b2527e2af"
integrity sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
-"@babel/plugin-transform-private-methods@^7.23.3":
+"@babel/plugin-transform-private-methods@^7.22.5", "@babel/plugin-transform-private-methods@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.23.3.tgz#b2d7a3c97e278bfe59137a978d53b2c2e038c0e4"
integrity sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==
@@ -886,7 +878,7 @@
"@babel/helper-create-class-features-plugin" "^7.22.15"
"@babel/helper-plugin-utils" "^7.22.5"
-"@babel/plugin-transform-private-property-in-object@^7.23.4":
+"@babel/plugin-transform-private-property-in-object@^7.22.11", "@babel/plugin-transform-private-property-in-object@^7.23.4":
version "7.23.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.23.4.tgz#3ec711d05d6608fd173d9b8de39872d8dbf68bf5"
integrity sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==
@@ -903,13 +895,20 @@
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
-"@babel/plugin-transform-react-display-name@^7.0.0":
+"@babel/plugin-transform-react-display-name@^7.0.0", "@babel/plugin-transform-react-display-name@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.23.3.tgz#70529f034dd1e561045ad3c8152a267f0d7b6200"
integrity sha512-GnvhtVfA2OAtzdX58FJxU19rhoGeQzyVndw3GgtdECQvQFXPEZIOVULHVZGAYmOgmqjXpVpfocAbSjh99V/Fqw==
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
+"@babel/plugin-transform-react-jsx-development@^7.22.5":
+ version "7.22.5"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.22.5.tgz#e716b6edbef972a92165cd69d92f1255f7e73e87"
+ integrity sha512-bDhuzwWMuInwCYeDeMzyi7TaBgRQei6DqxhbyniL7/VG4RSS7HtSL2QbY4eESy1KJqlWt8g3xeEBGPuo+XqC8A==
+ dependencies:
+ "@babel/plugin-transform-react-jsx" "^7.22.5"
+
"@babel/plugin-transform-react-jsx-self@^7.0.0":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz#ed3e7dadde046cce761a8e3cf003a13d1a7972d9"
@@ -924,7 +923,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.22.5"
-"@babel/plugin-transform-react-jsx@^7.0.0", "@babel/plugin-transform-react-jsx@^7.12.17":
+"@babel/plugin-transform-react-jsx@^7.0.0", "@babel/plugin-transform-react-jsx@^7.22.15", "@babel/plugin-transform-react-jsx@^7.22.5":
version "7.23.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312"
integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA==
@@ -935,6 +934,14 @@
"@babel/plugin-syntax-jsx" "^7.23.3"
"@babel/types" "^7.23.4"
+"@babel/plugin-transform-react-pure-annotations@^7.23.3":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.23.3.tgz#fabedbdb8ee40edf5da96f3ecfc6958e3783b93c"
+ integrity sha512-qMFdSS+TUhB7Q/3HVPnEdYJDQIk57jkntAwSuz9xfSE4n+3I+vHYCli3HoHawN1Z3RfCz/y1zXA/JXjG6cVImQ==
+ dependencies:
+ "@babel/helper-annotate-as-pure" "^7.22.5"
+ "@babel/helper-plugin-utils" "^7.22.5"
+
"@babel/plugin-transform-regenerator@^7.23.3":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.23.3.tgz#141afd4a2057298602069fce7f2dc5173e6c561c"
@@ -1143,6 +1150,18 @@
"@babel/types" "^7.4.4"
esutils "^2.0.2"
+"@babel/preset-react@^7.22.15":
+ version "7.23.3"
+ resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.23.3.tgz#f73ca07e7590f977db07eb54dbe46538cc015709"
+ integrity sha512-tbkHOS9axH6Ysf2OUEqoSZ6T3Fa2SrNH6WTWSPBboxKzdxNc9qOICeLXkNG0ZEwbQ1HY8liwOce4aN/Ceyuq6w==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.22.5"
+ "@babel/helper-validator-option" "^7.22.15"
+ "@babel/plugin-transform-react-display-name" "^7.23.3"
+ "@babel/plugin-transform-react-jsx" "^7.22.15"
+ "@babel/plugin-transform-react-jsx-development" "^7.22.5"
+ "@babel/plugin-transform-react-pure-annotations" "^7.23.3"
+
"@babel/preset-typescript@^7.13.0":
version "7.23.3"
resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.23.3.tgz#14534b34ed5b6d435aa05f1ae1c5e7adcc01d913"
@@ -1440,7 +1459,7 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b"
integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==
-"@expo/bunyan@4.0.0", "@expo/bunyan@^4.0.0":
+"@expo/bunyan@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@expo/bunyan/-/bunyan-4.0.0.tgz#be0c1de943c7987a9fbd309ea0b1acd605890c7b"
integrity sha512-Ydf4LidRB/EBI+YrB+cVLqIseiRfjUI/AeHBgjGMtq3GroraDu81OV7zqophRgupngoL3iS3JUMDMnxO7g39qA==
@@ -1450,67 +1469,80 @@
mv "~2"
safe-json-stringify "~1"
-"@expo/cli@0.10.16":
- version "0.10.16"
- resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.10.16.tgz#42f9aaf08884f70f3a671b7d6b4f138ad39192d7"
- integrity sha512-EwgnRN5AMElg0JJjFLJTPk5hYkVXxnNMLIvZBiTfGoCq+rDw6u7Mg5l2Bbm/geSHOoplaHyPZ/Wr23FAuZWehA==
+"@expo/cli@0.17.3":
+ version "0.17.3"
+ resolved "https://registry.yarnpkg.com/@expo/cli/-/cli-0.17.3.tgz#0763696671522062e5592411d02952ed905d4838"
+ integrity sha512-lIK8igsEQxTh4WuDlcEhE0wAJcDrAyjWDF00phdmwuSCpE5SaEXNlddOXvGxEVKPhUxHZUFo9NbfoQC+JVmkfA==
dependencies:
"@babel/runtime" "^7.20.0"
"@expo/code-signing-certificates" "0.0.5"
- "@expo/config" "~8.1.0"
- "@expo/config-plugins" "~7.2.0"
- "@expo/dev-server" "0.5.5"
+ "@expo/config" "~8.5.0"
+ "@expo/config-plugins" "~7.8.0"
"@expo/devcert" "^1.0.0"
- "@expo/env" "0.0.5"
+ "@expo/env" "~0.2.0"
+ "@expo/image-utils" "^0.4.0"
"@expo/json-file" "^8.2.37"
- "@expo/metro-config" "~0.10.0"
+ "@expo/metro-config" "~0.17.0"
"@expo/osascript" "^2.0.31"
- "@expo/package-manager" "~1.1.0"
- "@expo/plist" "^0.0.20"
- "@expo/prebuild-config" "6.2.6"
+ "@expo/package-manager" "^1.1.1"
+ "@expo/plist" "^0.1.0"
+ "@expo/prebuild-config" "6.7.4"
"@expo/rudder-sdk-node" "1.1.1"
"@expo/spawn-async" "1.5.0"
- "@expo/xcpretty" "^4.2.1"
+ "@expo/xcpretty" "^4.3.0"
+ "@react-native/dev-middleware" "^0.73.6"
"@urql/core" "2.3.6"
"@urql/exchange-retry" "0.3.0"
accepts "^1.3.8"
- arg "4.1.0"
+ arg "5.0.2"
better-opn "~3.0.2"
bplist-parser "^0.3.1"
cacache "^15.3.0"
chalk "^4.0.0"
ci-info "^3.3.0"
+ connect "^3.7.0"
debug "^4.3.4"
env-editor "^0.4.1"
+ find-yarn-workspace-root "~2.0.0"
form-data "^3.0.1"
freeport-async "2.0.0"
fs-extra "~8.1.0"
getenv "^1.0.0"
+ glob "^7.1.7"
graphql "15.8.0"
graphql-tag "^2.10.1"
https-proxy-agent "^5.0.1"
internal-ip "4.3.0"
+ is-docker "^2.0.0"
+ is-wsl "^2.1.1"
js-yaml "^3.13.1"
json-schema-deref-sync "^0.13.0"
- md5-file "^3.2.3"
+ lodash.debounce "^4.0.8"
md5hex "^1.0.0"
- minipass "3.1.6"
+ minimatch "^3.0.4"
+ minipass "3.3.6"
node-fetch "^2.6.7"
node-forge "^1.3.1"
npm-package-arg "^7.0.0"
+ open "^8.3.0"
ora "3.4.0"
+ picomatch "^3.0.1"
pretty-bytes "5.6.0"
progress "2.0.3"
prompts "^2.3.2"
qrcode-terminal "0.11.0"
require-from-string "^2.0.2"
requireg "^0.2.2"
+ resolve "^1.22.2"
resolve-from "^5.0.0"
+ resolve.exports "^2.0.2"
semver "^7.5.3"
send "^0.18.0"
slugify "^1.3.4"
+ source-map-support "~0.5.21"
structured-headers "^0.4.1"
tar "^6.0.5"
+ temp-dir "^2.0.0"
tempy "^0.7.1"
terminal-link "^2.1.1"
text-table "^0.2.0"
@@ -1526,14 +1558,15 @@
node-forge "^1.2.1"
nullthrows "^1.1.1"
-"@expo/config-plugins@7.2.5", "@expo/config-plugins@~7.2.0":
- version "7.2.5"
- resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-7.2.5.tgz#b15f22878975fdc4ddcfa8cdc971937ddc4c0249"
- integrity sha512-w+5ccu1IxBHgyQk9CPFKLZOk8yZQEyTjbJwOzESK1eR7QwosbcsLkN1c1WWUZYiCXwORu3UTwJYll4+X2xxJhQ==
+"@expo/config-plugins@7.8.4", "@expo/config-plugins@~7.8.0", "@expo/config-plugins@~7.8.2":
+ version "7.8.4"
+ resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-7.8.4.tgz#533b5d536c1dc8b5544d64878b51bda28f2e1a1f"
+ integrity sha512-hv03HYxb/5kX8Gxv/BTI8TLc9L06WzqAfHRRXdbar4zkLcP2oTzvsLEF4/L/TIpD3rsnYa0KU42d0gWRxzPCJg==
dependencies:
- "@expo/config-types" "^49.0.0-alpha.1"
- "@expo/json-file" "~8.2.37"
- "@expo/plist" "^0.0.20"
+ "@expo/config-types" "^50.0.0-alpha.1"
+ "@expo/fingerprint" "^0.6.0"
+ "@expo/json-file" "~8.3.0"
+ "@expo/plist" "^0.1.0"
"@expo/sdk-runtime-versions" "^1.0.0"
"@react-native/normalize-color" "^2.0.0"
chalk "^4.1.2"
@@ -1544,18 +1577,18 @@
resolve-from "^5.0.0"
semver "^7.5.3"
slash "^3.0.0"
+ slugify "^1.6.6"
xcode "^3.0.1"
xml2js "0.6.0"
-"@expo/config-plugins@~7.8.0", "@expo/config-plugins@~7.8.2":
- version "7.8.4"
- resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-7.8.4.tgz#533b5d536c1dc8b5544d64878b51bda28f2e1a1f"
- integrity sha512-hv03HYxb/5kX8Gxv/BTI8TLc9L06WzqAfHRRXdbar4zkLcP2oTzvsLEF4/L/TIpD3rsnYa0KU42d0gWRxzPCJg==
+"@expo/config-plugins@~7.2.0":
+ version "7.2.5"
+ resolved "https://registry.yarnpkg.com/@expo/config-plugins/-/config-plugins-7.2.5.tgz#b15f22878975fdc4ddcfa8cdc971937ddc4c0249"
+ integrity sha512-w+5ccu1IxBHgyQk9CPFKLZOk8yZQEyTjbJwOzESK1eR7QwosbcsLkN1c1WWUZYiCXwORu3UTwJYll4+X2xxJhQ==
dependencies:
- "@expo/config-types" "^50.0.0-alpha.1"
- "@expo/fingerprint" "^0.6.0"
- "@expo/json-file" "~8.3.0"
- "@expo/plist" "^0.1.0"
+ "@expo/config-types" "^49.0.0-alpha.1"
+ "@expo/json-file" "~8.2.37"
+ "@expo/plist" "^0.0.20"
"@expo/sdk-runtime-versions" "^1.0.0"
"@react-native/normalize-color" "^2.0.0"
chalk "^4.1.2"
@@ -1566,7 +1599,6 @@
resolve-from "^5.0.0"
semver "^7.5.3"
slash "^3.0.0"
- slugify "^1.6.6"
xcode "^3.0.1"
xml2js "0.6.0"
@@ -1580,14 +1612,14 @@
resolved "https://registry.yarnpkg.com/@expo/config-types/-/config-types-50.0.0.tgz#b534d3ec997ec60f8af24f6ad56244c8afc71a0b"
integrity sha512-0kkhIwXRT6EdFDwn+zTg9R2MZIAEYGn1MVkyRohAd+C9cXOb5RA8WLQi7vuxKF9m1SMtNAUrf0pO+ENK0+/KSw==
-"@expo/config@8.1.2", "@expo/config@~8.1.0":
- version "8.1.2"
- resolved "https://registry.yarnpkg.com/@expo/config/-/config-8.1.2.tgz#7fff28b3acefe39702e9f3ce1c9fd896a52caa80"
- integrity sha512-4e7hzPj50mQIlsrzOH6XZ36O094mPfPTIDIH4yv49bWNMc7GFLTofB/lcT+QyxiLaJuC0Wlk9yOLB8DIqmtwug==
+"@expo/config@8.5.4", "@expo/config@~8.5.0":
+ version "8.5.4"
+ resolved "https://registry.yarnpkg.com/@expo/config/-/config-8.5.4.tgz#bb5eb06caa36e4e35dc8c7647fae63e147b830ca"
+ integrity sha512-ggOLJPHGzJSJHVBC1LzwXwR6qUn8Mw7hkc5zEKRIdhFRuIQ6s2FE4eOvP87LrNfDF7eZGa6tJQYsiHSmZKG+8Q==
dependencies:
"@babel/code-frame" "~7.10.4"
- "@expo/config-plugins" "~7.2.0"
- "@expo/config-types" "^49.0.0-alpha.1"
+ "@expo/config-plugins" "~7.8.2"
+ "@expo/config-types" "^50.0.0"
"@expo/json-file" "^8.2.37"
getenv "^1.0.0"
glob "7.1.6"
@@ -1595,16 +1627,16 @@
resolve-from "^5.0.0"
semver "7.5.3"
slugify "^1.3.4"
- sucrase "^3.20.0"
+ sucrase "3.34.0"
-"@expo/config@~8.5.0":
- version "8.5.4"
- resolved "https://registry.yarnpkg.com/@expo/config/-/config-8.5.4.tgz#bb5eb06caa36e4e35dc8c7647fae63e147b830ca"
- integrity sha512-ggOLJPHGzJSJHVBC1LzwXwR6qUn8Mw7hkc5zEKRIdhFRuIQ6s2FE4eOvP87LrNfDF7eZGa6tJQYsiHSmZKG+8Q==
+"@expo/config@~8.1.0":
+ version "8.1.2"
+ resolved "https://registry.yarnpkg.com/@expo/config/-/config-8.1.2.tgz#7fff28b3acefe39702e9f3ce1c9fd896a52caa80"
+ integrity sha512-4e7hzPj50mQIlsrzOH6XZ36O094mPfPTIDIH4yv49bWNMc7GFLTofB/lcT+QyxiLaJuC0Wlk9yOLB8DIqmtwug==
dependencies:
"@babel/code-frame" "~7.10.4"
- "@expo/config-plugins" "~7.8.2"
- "@expo/config-types" "^50.0.0"
+ "@expo/config-plugins" "~7.2.0"
+ "@expo/config-types" "^49.0.0-alpha.1"
"@expo/json-file" "^8.2.37"
getenv "^1.0.0"
glob "7.1.6"
@@ -1612,28 +1644,7 @@
resolve-from "^5.0.0"
semver "7.5.3"
slugify "^1.3.4"
- sucrase "3.34.0"
-
-"@expo/dev-server@0.5.5":
- version "0.5.5"
- resolved "https://registry.yarnpkg.com/@expo/dev-server/-/dev-server-0.5.5.tgz#33f9065e0cf5f36ac61944a92d11390cc71b7035"
- integrity sha512-t0fT8xH1exwYsH5hh7bAt85VF+gXxg24qrbny2rR/iKoPTWFCd2JNQV8pvfLg51hvrywQ3YCBuT3lU1w7aZxFA==
- dependencies:
- "@expo/bunyan" "4.0.0"
- "@expo/metro-config" "~0.10.0"
- "@expo/osascript" "2.0.33"
- "@expo/spawn-async" "^1.5.0"
- body-parser "^1.20.1"
- chalk "^4.0.0"
- connect "^3.7.0"
- fs-extra "9.0.0"
- is-docker "^2.0.0"
- is-wsl "^2.1.1"
- node-fetch "^2.6.0"
- open "^8.3.0"
- resolve-from "^5.0.0"
- serialize-error "6.0.0"
- temp-dir "^2.0.0"
+ sucrase "^3.20.0"
"@expo/devcert@^1.0.0":
version "1.1.0"
@@ -1654,10 +1665,10 @@
tmp "^0.0.33"
tslib "^2.4.0"
-"@expo/env@0.0.5":
- version "0.0.5"
- resolved "https://registry.yarnpkg.com/@expo/env/-/env-0.0.5.tgz#86526ed5c966fc39b2644341f7a10f4b855e59b8"
- integrity sha512-UXuKAqyXfhMQC3gP0OyjXmFX08Z1fkVWiGBN7bYzfoX8LHatjeHrDtI6w5nDvd8XPxPvmqaZoEDw1lW3+dz3oQ==
+"@expo/env@~0.2.0":
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/@expo/env/-/env-0.2.1.tgz#51b5e836ce510cbb18341ad2ab2f685354fd4a94"
+ integrity sha512-deZmRS7Dvp18VM8s559dq/ZjPlV1D9vtLoLXwHmCK/JYOvtNptdKsfxcWjI7ewmo6ln2PqgNI9HRI74q6Wk2eA==
dependencies:
chalk "^4.0.0"
debug "^4.3.4"
@@ -1729,30 +1740,38 @@
json5 "^2.2.2"
write-file-atomic "^2.3.0"
-"@expo/metro-config@~0.10.0":
- version "0.10.7"
- resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.10.7.tgz#d1b91baffcb7feb52fc7e2e122450bfc5d01e7c1"
- integrity sha512-uACymEiyX0447hI4unt+2cemLQkTZXKvTev936NhtsgVnql45EP0V0pzmo/0H0WlHaAGXgvOBZJl8wFqcJ3CbQ==
+"@expo/metro-config@0.17.3", "@expo/metro-config@~0.17.0":
+ version "0.17.3"
+ resolved "https://registry.yarnpkg.com/@expo/metro-config/-/metro-config-0.17.3.tgz#f06f0929e4ac907517d24794d35021901651da49"
+ integrity sha512-YW8ixbaz6yL7/Mg1rJJejiAAVQQKjGY1wXvT2Dh487r/r9/j1yE1YRS/oRY1yItYzbnHvO0p0jMnEGfiFYL3Tg==
dependencies:
- "@expo/config" "~8.1.0"
- "@expo/env" "0.0.5"
- "@expo/json-file" "~8.2.37"
+ "@babel/core" "^7.20.0"
+ "@babel/generator" "^7.20.5"
+ "@babel/parser" "^7.20.0"
+ "@babel/types" "^7.20.0"
+ "@expo/config" "~8.5.0"
+ "@expo/env" "~0.2.0"
+ "@expo/json-file" "~8.3.0"
+ "@expo/spawn-async" "^1.7.2"
+ babel-preset-fbjs "^3.4.0"
chalk "^4.1.0"
debug "^4.3.2"
find-yarn-workspace-root "~2.0.0"
+ fs-extra "^9.1.0"
getenv "^1.0.0"
+ glob "^7.2.3"
jsc-safe-url "^0.2.4"
lightningcss "~1.19.0"
- postcss "~8.4.21"
+ postcss "~8.4.32"
resolve-from "^5.0.0"
- sucrase "^3.20.0"
+ sucrase "3.34.0"
"@expo/metro-runtime@3.1.2":
version "3.1.2"
resolved "https://registry.yarnpkg.com/@expo/metro-runtime/-/metro-runtime-3.1.2.tgz#4cc4fe53aa8f716307f584c67db76268d38c2ed7"
integrity sha512-Wekv2AZ3HY0NG9Im8AMB1KHGyHjmALg6xekVK34724I/DLtcocLKtQEP04oI9GcAZBotAhROHH5E4ADhJAEgYg==
-"@expo/osascript@2.0.33", "@expo/osascript@^2.0.31":
+"@expo/osascript@^2.0.31":
version "2.0.33"
resolved "https://registry.yarnpkg.com/@expo/osascript/-/osascript-2.0.33.tgz#e9dcc8da54466c11939074aa71a006024ea884b1"
integrity sha512-FQinlwHrTlJbntp8a7NAlCKedVXe06Va/0DSLXRO8lZVtgbEMrYYSUZWQNcOlNtc58c2elNph6z9dMOYwSo3JQ==
@@ -1760,10 +1779,10 @@
"@expo/spawn-async" "^1.5.0"
exec-async "^2.2.0"
-"@expo/package-manager@~1.1.0":
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-1.1.2.tgz#e58c9bed4cbb829ebf2cbb80b8542600a6609bd1"
- integrity sha512-JI9XzrxB0QVXysyuJ996FPCJGDCYRkbUvgG4QmMTTMFA1T+mv8YzazC3T9C1pHQUAAveVCre1+Pqv0nZXN24Xg==
+"@expo/package-manager@^1.1.1":
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/@expo/package-manager/-/package-manager-1.4.2.tgz#8c12a9163c5ff7c7cc89806c4b75cff4974c57fc"
+ integrity sha512-LKdo/6y4W7llZ6ghsg1kdx2CeH/qR/c6QI/JI8oPUvppsZoeIYjSkdflce978fAMfR8IXoi0wt0jA2w0kWpwbg==
dependencies:
"@expo/json-file" "^8.2.37"
"@expo/spawn-async" "^1.5.0"
@@ -1774,6 +1793,7 @@
js-yaml "^3.13.1"
micromatch "^4.0.2"
npm-package-arg "^7.0.0"
+ ora "^3.4.0"
split "^1.0.1"
sudo-prompt "9.1.1"
@@ -1862,7 +1882,7 @@
dependencies:
cross-spawn "^6.0.5"
-"@expo/spawn-async@^1.5.0":
+"@expo/spawn-async@^1.5.0", "@expo/spawn-async@^1.7.2":
version "1.7.2"
resolved "https://registry.yarnpkg.com/@expo/spawn-async/-/spawn-async-1.7.2.tgz#fcfe66c3e387245e72154b1a7eae8cada6a47f58"
integrity sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==
@@ -1874,10 +1894,15 @@
resolved "https://registry.yarnpkg.com/@expo/vector-icons/-/vector-icons-13.0.0.tgz#e2989b85e95a82bce216f88cf8fb583ab050ec95"
integrity sha512-TI+l71+5aSKnShYclFa14Kum+hQMZ86b95SH6tQUG3qZEmLTarvWpKwqtTwQKqvlJSJrpFiSFu3eCuZokY6zWA==
-"@expo/xcpretty@^4.2.1":
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/@expo/xcpretty/-/xcpretty-4.3.0.tgz#d745c2c5ec38fc6acd451112bb05c6ae952a2c3a"
- integrity sha512-whBbvHZ2Q10T5TNmN0z5NbO6C9ZDw+XUTu8h6vVMnMzQrbGexc9oaCCZfz+L3Q7TEL5vfr+9L86nY62c3Bsm+g==
+"@expo/vector-icons@^14.0.0":
+ version "14.0.0"
+ resolved "https://registry.yarnpkg.com/@expo/vector-icons/-/vector-icons-14.0.0.tgz#48ce0aa5c05873b07c0c78bfe16c870388f4de9a"
+ integrity sha512-5orm59pdnBQlovhU9k4DbjMUZBHNlku7IRgFY56f7pcaaCnXq9yaLJoOQl9sMwNdFzf4gnkTyHmR5uN10mI9rA==
+
+"@expo/xcpretty@^4.3.0":
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/@expo/xcpretty/-/xcpretty-4.3.1.tgz#e0a6a92d1e46ab5ac5e90d9a8e66ac1a2a2f5920"
+ integrity sha512-sqXgo1SCv+j4VtYEwl/bukuOIBrVgx6euIoCat3Iyx5oeoXwEA2USCoeL0IPubflMxncA2INkqJ/Wr3NGrSgzw==
dependencies:
"@babel/code-frame" "7.10.4"
chalk "^4.1.0"
@@ -1978,6 +2003,11 @@
wrap-ansi "^8.1.0"
wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
+"@isaacs/ttlcache@^1.4.1":
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz#21fb23db34e9b6220c6ba023a0118a2dd3461ea2"
+ integrity sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==
+
"@istanbuljs/load-nyc-config@^1.0.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
@@ -2517,6 +2547,79 @@
resolved "https://registry.yarnpkg.com/@react-native/assets-registry/-/assets-registry-0.72.0.tgz#c82a76a1d86ec0c3907be76f7faf97a32bbed05d"
integrity sha512-Im93xRJuHHxb1wniGhBMsxLwcfzdYreSZVQGDoMJgkd6+Iky61LInGEHnQCTN0fKNYF1Dvcofb4uMmE1RQHXHQ==
+"@react-native/assets-registry@~0.73.1":
+ version "0.73.1"
+ resolved "https://registry.yarnpkg.com/@react-native/assets-registry/-/assets-registry-0.73.1.tgz#e2a6b73b16c183a270f338dc69c36039b3946e85"
+ integrity sha512-2FgAbU7uKM5SbbW9QptPPZx8N9Ke2L7bsHb+EhAanZjFZunA9PaYtyjUQ1s7HD+zDVqOQIvjkpXSv7Kejd2tqg==
+
+"@react-native/babel-plugin-codegen@0.73.3":
+ version "0.73.3"
+ resolved "https://registry.yarnpkg.com/@react-native/babel-plugin-codegen/-/babel-plugin-codegen-0.73.3.tgz#6bf135322b89264342c80778ee6bb697f968f773"
+ integrity sha512-+zQrDDbz6lB48LyzFHxNCgXDCBHH+oTRdXAjikRcBUdeG9St9ABbYFLtb799zSxLOrCqFVyXqhJR2vlgLLEbcg==
+ dependencies:
+ "@react-native/codegen" "0.73.2"
+
+"@react-native/babel-preset@^0.73.18":
+ version "0.73.20"
+ resolved "https://registry.yarnpkg.com/@react-native/babel-preset/-/babel-preset-0.73.20.tgz#65ab68cce16bb222bb1faece498abb6f7b1d5db0"
+ integrity sha512-fU9NqkusbfFq71l4BWQfqqD/lLcLC0MZ++UYgieA3j8lIEppJTLVauv2RwtD2yltBkjebgYEC5Rwvt1l0MUBXw==
+ dependencies:
+ "@babel/core" "^7.20.0"
+ "@babel/plugin-proposal-async-generator-functions" "^7.0.0"
+ "@babel/plugin-proposal-class-properties" "^7.18.0"
+ "@babel/plugin-proposal-export-default-from" "^7.0.0"
+ "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.0"
+ "@babel/plugin-proposal-numeric-separator" "^7.0.0"
+ "@babel/plugin-proposal-object-rest-spread" "^7.20.0"
+ "@babel/plugin-proposal-optional-catch-binding" "^7.0.0"
+ "@babel/plugin-proposal-optional-chaining" "^7.20.0"
+ "@babel/plugin-syntax-dynamic-import" "^7.8.0"
+ "@babel/plugin-syntax-export-default-from" "^7.0.0"
+ "@babel/plugin-syntax-flow" "^7.18.0"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0"
+ "@babel/plugin-syntax-optional-chaining" "^7.0.0"
+ "@babel/plugin-transform-arrow-functions" "^7.0.0"
+ "@babel/plugin-transform-async-to-generator" "^7.20.0"
+ "@babel/plugin-transform-block-scoping" "^7.0.0"
+ "@babel/plugin-transform-classes" "^7.0.0"
+ "@babel/plugin-transform-computed-properties" "^7.0.0"
+ "@babel/plugin-transform-destructuring" "^7.20.0"
+ "@babel/plugin-transform-flow-strip-types" "^7.20.0"
+ "@babel/plugin-transform-function-name" "^7.0.0"
+ "@babel/plugin-transform-literals" "^7.0.0"
+ "@babel/plugin-transform-modules-commonjs" "^7.0.0"
+ "@babel/plugin-transform-named-capturing-groups-regex" "^7.0.0"
+ "@babel/plugin-transform-parameters" "^7.0.0"
+ "@babel/plugin-transform-private-methods" "^7.22.5"
+ "@babel/plugin-transform-private-property-in-object" "^7.22.11"
+ "@babel/plugin-transform-react-display-name" "^7.0.0"
+ "@babel/plugin-transform-react-jsx" "^7.0.0"
+ "@babel/plugin-transform-react-jsx-self" "^7.0.0"
+ "@babel/plugin-transform-react-jsx-source" "^7.0.0"
+ "@babel/plugin-transform-runtime" "^7.0.0"
+ "@babel/plugin-transform-shorthand-properties" "^7.0.0"
+ "@babel/plugin-transform-spread" "^7.0.0"
+ "@babel/plugin-transform-sticky-regex" "^7.0.0"
+ "@babel/plugin-transform-typescript" "^7.5.0"
+ "@babel/plugin-transform-unicode-regex" "^7.0.0"
+ "@babel/template" "^7.0.0"
+ "@react-native/babel-plugin-codegen" "0.73.3"
+ babel-plugin-transform-flow-enums "^0.0.2"
+ react-refresh "^0.14.0"
+
+"@react-native/codegen@0.73.2":
+ version "0.73.2"
+ resolved "https://registry.yarnpkg.com/@react-native/codegen/-/codegen-0.73.2.tgz#58af4e4c3098f0e6338e88ec64412c014dd51519"
+ integrity sha512-lfy8S7umhE3QLQG5ViC4wg5N1Z+E6RnaeIw8w1voroQsXXGPB72IBozh8dAHR3+ceTxIU0KX3A8OpJI8e1+HpQ==
+ dependencies:
+ "@babel/parser" "^7.20.0"
+ flow-parser "^0.206.0"
+ glob "^7.1.1"
+ invariant "^2.2.4"
+ jscodeshift "^0.14.0"
+ mkdirp "^0.5.1"
+ nullthrows "^1.1.1"
+
"@react-native/codegen@^0.72.7":
version "0.72.8"
resolved "https://registry.yarnpkg.com/@react-native/codegen/-/codegen-0.72.8.tgz#0593f628e1310f430450a9479fbb4be35e7b63d6"
@@ -2530,6 +2633,27 @@
mkdirp "^0.5.1"
nullthrows "^1.1.1"
+"@react-native/debugger-frontend@0.73.3":
+ version "0.73.3"
+ resolved "https://registry.yarnpkg.com/@react-native/debugger-frontend/-/debugger-frontend-0.73.3.tgz#033757614d2ada994c68a1deae78c1dd2ad33c2b"
+ integrity sha512-RgEKnWuoo54dh7gQhV7kvzKhXZEhpF9LlMdZolyhGxHsBqZ2gXdibfDlfcARFFifPIiaZ3lXuOVVa4ei+uPgTw==
+
+"@react-native/dev-middleware@^0.73.6":
+ version "0.73.7"
+ resolved "https://registry.yarnpkg.com/@react-native/dev-middleware/-/dev-middleware-0.73.7.tgz#61d2bf08973d9a537fa3f2a42deeb13530d721ae"
+ integrity sha512-BZXpn+qKp/dNdr4+TkZxXDttfx8YobDh8MFHsMk9usouLm22pKgFIPkGBV0X8Do4LBkFNPGtrnsKkWk/yuUXKg==
+ dependencies:
+ "@isaacs/ttlcache" "^1.4.1"
+ "@react-native/debugger-frontend" "0.73.3"
+ chrome-launcher "^0.15.2"
+ chromium-edge-launcher "^1.0.0"
+ connect "^3.6.5"
+ debug "^2.2.0"
+ node-fetch "^2.2.0"
+ open "^7.0.3"
+ serve-static "^1.13.1"
+ temp-dir "^2.0.0"
+
"@react-native/gradle-plugin@^0.72.11":
version "0.72.11"
resolved "https://registry.yarnpkg.com/@react-native/gradle-plugin/-/gradle-plugin-0.72.11.tgz#c063ef12778706611de7a1e42b74b14d9405fb9f"
@@ -3244,12 +3368,7 @@ application-config-path@^0.1.0:
resolved "https://registry.yarnpkg.com/application-config-path/-/application-config-path-0.1.1.tgz#8b5ac64ff6afdd9bd70ce69f6f64b6998f5f756e"
integrity sha512-zy9cHePtMP0YhwG+CfHm0bgwdnga2X3gZexpdCwEj//dpb+TKajtiC8REEUJUSq6Ab4f9cgNy2l8ObXzCXFkEw==
-arg@4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.0.tgz#583c518199419e0037abb74062c37f8519e575f0"
- integrity sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==
-
-arg@^5.0.2:
+arg@5.0.2, arg@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
@@ -3443,17 +3562,6 @@ babel-plugin-macros@^3.1.0:
cosmiconfig "^7.0.0"
resolve "^1.19.0"
-babel-plugin-module-resolver@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-module-resolver/-/babel-plugin-module-resolver-5.0.0.tgz#2b7fc176bd55da25f516abf96015617b4f70fc73"
- integrity sha512-g0u+/ChLSJ5+PzYwLwP8Rp8Rcfowz58TJNCe+L/ui4rpzE/mg//JVX0EWBUYoxaextqnwuGHzfGp2hh0PPV25Q==
- dependencies:
- find-babel-config "^2.0.0"
- glob "^8.0.3"
- pkg-up "^3.1.0"
- reselect "^4.1.7"
- resolve "^1.22.1"
-
babel-plugin-polyfill-corejs2@^0.4.7:
version "0.4.7"
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.7.tgz#679d1b94bf3360f7682e11f2cb2708828a24fe8c"
@@ -3513,19 +3621,20 @@ babel-preset-current-node-syntax@^1.0.0:
"@babel/plugin-syntax-optional-chaining" "^7.8.3"
"@babel/plugin-syntax-top-level-await" "^7.8.3"
-babel-preset-expo@~9.5.2:
- version "9.5.2"
- resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-9.5.2.tgz#5ed1756c8434ca972d7a940e4f13570a283641df"
- integrity sha512-hU1G1TDiikuXV6UDZjPnX+WdbjbtidDiYhftMEVrZQSst45pDPVBWbM41TUKrpJMwv4FypsLzK+378gnMPRVWQ==
+babel-preset-expo@~10.0.1:
+ version "10.0.1"
+ resolved "https://registry.yarnpkg.com/babel-preset-expo/-/babel-preset-expo-10.0.1.tgz#a0e7ad0119f46e58cb3f0738c3ca0c6e97b69c11"
+ integrity sha512-uWIGmLfbP3dS5+8nesxaW6mQs41d4iP7X82ZwRdisB/wAhKQmuJM9Y1jQe4006uNYkw6Phf2TT03ykLVro7KuQ==
dependencies:
"@babel/plugin-proposal-decorators" "^7.12.9"
- "@babel/plugin-proposal-export-namespace-from" "^7.18.9"
- "@babel/plugin-proposal-object-rest-spread" "^7.12.13"
- "@babel/plugin-transform-react-jsx" "^7.12.17"
+ "@babel/plugin-transform-export-namespace-from" "^7.22.11"
+ "@babel/plugin-transform-object-rest-spread" "^7.12.13"
+ "@babel/plugin-transform-parameters" "^7.22.15"
"@babel/preset-env" "^7.20.0"
- babel-plugin-module-resolver "^5.0.0"
+ "@babel/preset-react" "^7.22.15"
+ "@react-native/babel-preset" "^0.73.18"
babel-plugin-react-native-web "~0.18.10"
- metro-react-native-babel-preset "0.76.8"
+ react-refresh "0.14.0"
babel-preset-fbjs@^3.4.0:
version "3.4.0"
@@ -3614,24 +3723,6 @@ blueimp-md5@^2.10.0:
resolved "https://registry.yarnpkg.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz#b53feea5498dcb53dc6ec4b823adb84b729c4af0"
integrity sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==
-body-parser@^1.20.1:
- version "1.20.2"
- resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd"
- integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==
- dependencies:
- bytes "3.1.2"
- content-type "~1.0.5"
- debug "2.6.9"
- depd "2.0.0"
- destroy "1.2.0"
- http-errors "2.0.0"
- iconv-lite "0.4.24"
- on-finished "2.4.1"
- qs "6.11.0"
- raw-body "2.5.2"
- type-is "~1.6.18"
- unpipe "1.0.0"
-
bplist-creator@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/bplist-creator/-/bplist-creator-0.1.0.tgz#018a2d1b587f769e379ef5519103730f8963ba1e"
@@ -3740,11 +3831,6 @@ bytes@3.0.0:
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==
-bytes@3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
- integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
-
cacache@^15.3.0:
version "15.3.0"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb"
@@ -3887,6 +3973,28 @@ chownr@^2.0.0:
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
+chrome-launcher@^0.15.2:
+ version "0.15.2"
+ resolved "https://registry.yarnpkg.com/chrome-launcher/-/chrome-launcher-0.15.2.tgz#4e6404e32200095fdce7f6a1e1004f9bd36fa5da"
+ integrity sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==
+ dependencies:
+ "@types/node" "*"
+ escape-string-regexp "^4.0.0"
+ is-wsl "^2.2.0"
+ lighthouse-logger "^1.0.0"
+
+chromium-edge-launcher@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/chromium-edge-launcher/-/chromium-edge-launcher-1.0.0.tgz#0443083074715a13c669530b35df7bfea33b1509"
+ integrity sha512-pgtgjNKZ7i5U++1g1PWv75umkHvhVTDOQIZ+sjeUX9483S7Y6MUvO0lrd7ShGlQlFHMN4SwKTCq/X8hWrbv2KA==
+ dependencies:
+ "@types/node" "*"
+ escape-string-regexp "^4.0.0"
+ is-wsl "^2.2.0"
+ lighthouse-logger "^1.0.0"
+ mkdirp "^1.0.4"
+ rimraf "^3.0.2"
+
ci-info@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
@@ -4072,11 +4180,6 @@ compare-urls@^2.0.0:
dependencies:
normalize-url "^2.0.1"
-compare-versions@^3.4.0:
- version "3.6.0"
- resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.6.0.tgz#1a5689913685e5a87637b8d3ffca75514ec41d62"
- integrity sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==
-
component-type@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.2.tgz#4458ecc0c1871efc6288bfaff0cbdab08141d079"
@@ -4117,11 +4220,6 @@ connect@^3.6.5, connect@^3.7.0:
parseurl "~1.3.3"
utils-merge "1.0.1"
-content-type@~1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
- integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
-
convert-source-map@^1.5.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f"
@@ -4324,7 +4422,7 @@ dayjs@^1.8.15:
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.10.tgz#68acea85317a6e164457d6d6947564029a6a16a0"
integrity sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==
-debug@2.6.9, debug@^2.2.0:
+debug@2.6.9, debug@^2.2.0, debug@^2.6.9:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
@@ -4998,28 +5096,22 @@ expect@^29.7.0:
jest-message-util "^29.7.0"
jest-util "^29.7.0"
-expo-application@~5.3.0:
- version "5.3.1"
- resolved "https://registry.yarnpkg.com/expo-application/-/expo-application-5.3.1.tgz#074bbfc6bb5d65ae74a67f5288fa3eb582237e53"
- integrity sha512-HR2+K+Hm33vLw/TfbFaHrvUbRRNRco8R+3QaCKy7eJC2LFfT05kZ15ynGaKfB5DJ/oqPV3mxXVR/EfwmE++hoA==
-
expo-application@~5.8.0:
version "5.8.3"
resolved "https://registry.yarnpkg.com/expo-application/-/expo-application-5.8.3.tgz#43991bd81d05c987b07b2f430c036cda1572bc62"
integrity sha512-IISxzpPX+Xe4ynnwX8yY52T6dm1g9sME1GCj4lvUlrdc5xeTPM6U35x7Wj82V7lLWBaVGe+/Tg9EeKqfylCEwA==
-expo-asset@~8.10.1:
- version "8.10.1"
- resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-8.10.1.tgz#a7e8cf1c555ab8f844599822cb084fee95a93644"
- integrity sha512-5VMTESxgY9GBsspO/esY25SKEa7RyascVkLe/OcL1WgblNFm7xCCEEUIW8VWS1nHJQGYxpMZPr3bEfjMpdWdyA==
+expo-asset@~9.0.2:
+ version "9.0.2"
+ resolved "https://registry.yarnpkg.com/expo-asset/-/expo-asset-9.0.2.tgz#e8a6b6da356d5fc97955599d2fa49af78c7f0bfd"
+ integrity sha512-PzYKME1MgUOoUvwtdzhAyXkjXOXGiSYqGKG/MsXwWr0Ef5wlBaBm2DCO9V6KYbng5tBPFu6hTjoRNil1tBOSow==
dependencies:
+ "@react-native/assets-registry" "~0.73.1"
blueimp-md5 "^2.10.0"
- expo-constants "~14.4.2"
- expo-file-system "~15.4.0"
+ expo-constants "~15.4.0"
+ expo-file-system "~16.0.0"
invariant "^2.2.4"
md5-file "^3.2.3"
- path-browserify "^1.0.0"
- url-parse "^1.5.9"
expo-auth-session@~5.4.0:
version "5.4.0"
@@ -5033,14 +5125,6 @@ expo-auth-session@~5.4.0:
expo-web-browser "~12.8.0"
invariant "^2.2.4"
-expo-constants@~14.4.2:
- version "14.4.2"
- resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-14.4.2.tgz#cac5e8b524069545739b8d8595ce96cc5be6578c"
- integrity sha512-nOB122DOAjk+KrJT69lFQAoYVQGQjFHSigCPVBzVdko9S1xGsfiOH9+X5dygTsZTIlVLpQJDdmZ7ONiv3i+26w==
- dependencies:
- "@expo/config" "~8.1.0"
- uuid "^3.3.2"
-
expo-constants@~15.4.0, expo-constants@~15.4.3:
version "15.4.5"
resolved "https://registry.yarnpkg.com/expo-constants/-/expo-constants-15.4.5.tgz#81756a4c4e1c020f840a419cd86a124a6d1fb35b"
@@ -5055,12 +5139,10 @@ expo-crypto@~12.8.0:
dependencies:
base64-js "^1.3.0"
-expo-file-system@~15.4.0, expo-file-system@~15.4.5:
- version "15.4.5"
- resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-15.4.5.tgz#3ef68583027ff0e2fb9eca7a22b3caff6cfc550d"
- integrity sha512-xy61KaTaDgXhT/dllwYDHm3ch026EyO8j4eC6wSVr/yE12MMMxAC09yGwy4f7kkOs6ztGVQF5j7ldRzNLN4l0Q==
- dependencies:
- uuid "^3.4.0"
+expo-file-system@~16.0.0, expo-file-system@~16.0.5:
+ version "16.0.5"
+ resolved "https://registry.yarnpkg.com/expo-file-system/-/expo-file-system-16.0.5.tgz#7ade3b1295d47a38a2236c5b121069d1bf27a526"
+ integrity sha512-JpKMbKfwTaMCbwUwq7MwcSbPR7r+IqZEL3RFam3ClPHDtKLnlEoywREeaDsWjSZb7dS25hG3WqXspfTuugCDvg==
expo-font@~11.10.2:
version "11.10.2"
@@ -5069,17 +5151,10 @@ expo-font@~11.10.2:
dependencies:
fontfaceobserver "^2.1.0"
-expo-font@~11.4.0:
- version "11.4.0"
- resolved "https://registry.yarnpkg.com/expo-font/-/expo-font-11.4.0.tgz#e2d31c0bb76ba3c37c2d84703a49aeafc3afef28"
- integrity sha512-nkmezCFD7gR/I6R+e3/ry18uEfF8uYrr6h+PdBJu+3dawoLOpo+wFb/RG9bHUekU1/cPanR58LR7G5MEMKHR2w==
- dependencies:
- fontfaceobserver "^2.1.0"
-
-expo-keep-awake@~12.3.0:
- version "12.3.0"
- resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-12.3.0.tgz#c42449ae19c993274ddc43aafa618792b6aec408"
- integrity sha512-ujiJg1p9EdCOYS05jh5PtUrfiZnK0yyLy+UewzqrjUqIT8eAGMQbkfOn3C3fHE7AKd5AefSMzJnS3lYZcZYHDw==
+expo-keep-awake@~12.8.2:
+ version "12.8.2"
+ resolved "https://registry.yarnpkg.com/expo-keep-awake/-/expo-keep-awake-12.8.2.tgz#6cfdf8ad02b5fa130f99d4a1eb98e459d5b4332e"
+ integrity sha512-uiQdGbSX24Pt8nGbnmBtrKq6xL/Tm3+DuDRGBk/3ZE/HlizzNosGRIufIMJ/4B4FRw4dw8KU81h2RLuTjbay6g==
expo-linking@~6.2.0, expo-linking@~6.2.2:
version "6.2.2"
@@ -5089,24 +5164,23 @@ expo-linking@~6.2.0, expo-linking@~6.2.2:
expo-constants "~15.4.3"
invariant "^2.2.4"
-expo-modules-autolinking@1.5.1:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-1.5.1.tgz#363f90c172769ce12bf56c7be9ca0897adfc7a81"
- integrity sha512-yt5a1VCp2BF9CrsO689PCD5oXKP14MMhnOanQMvDn4BDpURYfzAlDVGC5fZrNQKtwn/eq3bcrxIwZ7D9QjVVRg==
+expo-modules-autolinking@1.10.2:
+ version "1.10.2"
+ resolved "https://registry.yarnpkg.com/expo-modules-autolinking/-/expo-modules-autolinking-1.10.2.tgz#a413235941c2f7167f5e5d5b66807f7417a5a8ea"
+ integrity sha512-OEeoz0+zGx5EJwGtDm9pSywCr+gUCaisZV0mNkK7V3fuRl+EVPBSsI+957JwAc4ZxVps95jy28eLcRRtQ33yVg==
dependencies:
- "@expo/config" "~8.1.0"
+ "@expo/config" "~8.5.0"
chalk "^4.1.0"
commander "^7.2.0"
fast-glob "^3.2.5"
find-up "^5.0.0"
fs-extra "^9.1.0"
-expo-modules-core@1.5.13:
- version "1.5.13"
- resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-1.5.13.tgz#abe00502b1b622ff8ef37bc7516180595a0fc4e3"
- integrity sha512-cKRsiHKwpDPRkBgMW3XdUWmEUDzihEPWXAyeo629BXpJ6uX6a66Zbz63SEXhlgsbLq8FB77gvYku3ceBqb+hHg==
+expo-modules-core@1.11.8:
+ version "1.11.8"
+ resolved "https://registry.yarnpkg.com/expo-modules-core/-/expo-modules-core-1.11.8.tgz#b0bdb31e70e1e570b70a9613f4b6392306c99a80"
+ integrity sha512-rlctE3nCNLCGv3LosGQNaTuwGrr2SyQA+hOgci/0l+VRc0gFNtvl0gskph9C0tnN1jzBeb8rRZQYVj5ih1yxcA==
dependencies:
- compare-versions "^3.4.0"
invariant "^2.2.4"
expo-router@^3.4.6:
@@ -5167,31 +5241,26 @@ expo-web-browser@~12.8.0:
compare-urls "^2.0.0"
url "^0.11.0"
-expo@~49.0.22:
- version "49.0.22"
- resolved "https://registry.yarnpkg.com/expo/-/expo-49.0.22.tgz#e5f1a37d10c2196790971e9e9cfdfdd14de01635"
- integrity sha512-1hhcphaKN74gDqEmGzU4sqxnusLi/i8SsWZ04rRn7b6zdyEchyudVLN3SOzeIUgfGmn7AcXm78JAQ7+e0WqSyw==
+expo@~50.0.4:
+ version "50.0.4"
+ resolved "https://registry.yarnpkg.com/expo/-/expo-50.0.4.tgz#3ec5349a58b8b3264bc2f218d85b673beb352a99"
+ integrity sha512-8QWBvYZyKFd7pHxbtri8/ZITBR19QbrW2IkezAhs3ZOHR2kluSgNfyo9ojAe7GnOnE8hCB6Xe83Dbm0R3Ealhw==
dependencies:
"@babel/runtime" "^7.20.0"
- "@expo/cli" "0.10.16"
- "@expo/config" "8.1.2"
- "@expo/config-plugins" "7.2.5"
- "@expo/vector-icons" "^13.0.0"
- babel-preset-expo "~9.5.2"
- expo-application "~5.3.0"
- expo-asset "~8.10.1"
- expo-constants "~14.4.2"
- expo-file-system "~15.4.5"
- expo-font "~11.4.0"
- expo-keep-awake "~12.3.0"
- expo-modules-autolinking "1.5.1"
- expo-modules-core "1.5.13"
+ "@expo/cli" "0.17.3"
+ "@expo/config" "8.5.4"
+ "@expo/config-plugins" "7.8.4"
+ "@expo/metro-config" "0.17.3"
+ "@expo/vector-icons" "^14.0.0"
+ babel-preset-expo "~10.0.1"
+ expo-asset "~9.0.2"
+ expo-file-system "~16.0.5"
+ expo-font "~11.10.2"
+ expo-keep-awake "~12.8.2"
+ expo-modules-autolinking "1.10.2"
+ expo-modules-core "1.11.8"
fbemitter "^3.0.0"
- invariant "^2.2.4"
- md5-file "^3.2.3"
- node-fetch "^2.6.7"
- pretty-format "^26.5.2"
- uuid "^3.4.0"
+ whatwg-url-without-unicode "8.0.0-3"
extend@^3.0.1:
version "3.0.2"
@@ -5322,14 +5391,6 @@ finalhandler@1.1.2:
statuses "~1.5.0"
unpipe "~1.0.0"
-find-babel-config@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-2.0.0.tgz#a8216f825415a839d0f23f4d18338a1cc966f701"
- integrity sha512-dOKT7jvF3hGzlW60Gc3ONox/0rRZ/tz7WCil0bqA1In/3I8f1BctpXahRnEKDySZqci7u+dqq93sZST9fOJpFw==
- dependencies:
- json5 "^2.1.1"
- path-exists "^4.0.0"
-
find-cache-dir@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7"
@@ -5630,7 +5691,7 @@ glob@^6.0.1:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
+glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.7, glob@^7.2.3:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
@@ -5642,17 +5703,6 @@ glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@^8.0.3:
- version "8.1.0"
- resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
- integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^5.0.1"
- once "^1.3.0"
-
globals@^11.1.0:
version "11.12.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
@@ -5842,13 +5892,6 @@ hyphenate-style-name@^1.0.3:
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d"
integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==
-iconv-lite@0.4.24:
- version "0.4.24"
- resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
- integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
- dependencies:
- safer-buffer ">= 2.1.2 < 3"
-
iconv-lite@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
@@ -6984,7 +7027,7 @@ json-stable-stringify-without-jsonify@^1.0.1:
resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
-json5@^2.1.1, json5@^2.2.2, json5@^2.2.3:
+json5@^2.2.2, json5@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
@@ -7045,6 +7088,14 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"
+lighthouse-logger@^1.0.0:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz#aef90f9e97cd81db367c7634292ee22079280aaa"
+ integrity sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==
+ dependencies:
+ debug "^2.6.9"
+ marky "^1.2.2"
+
lightningcss-darwin-arm64@1.19.0:
version "1.19.0"
resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.19.0.tgz#56ab071e932f845dbb7667f44f5b78441175a343"
@@ -7230,6 +7281,11 @@ makeerror@1.0.12:
dependencies:
tmpl "1.0.5"
+marky@^1.2.2:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/marky/-/marky-1.2.5.tgz#55796b688cbd72390d2d399eaaf1832c9413e3c0"
+ integrity sha512-q9JtQJKjpsVxCRVgQ+WapguSbKC3SQ5HEzFGPAJMStgh3QjCawp00UKv3MTTAArTmGmmPUvllHZoNbZ3gs0I+Q==
+
md5-file@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/md5-file/-/md5-file-3.2.3.tgz#f9bceb941eca2214a4c0727f5e700314e770f06f"
@@ -7260,11 +7316,6 @@ md5hex@^1.0.0:
resolved "https://registry.yarnpkg.com/md5hex/-/md5hex-1.0.0.tgz#ed74b477a2ee9369f75efee2f08d5915e52a42e8"
integrity sha512-c2YOUbp33+6thdCUi34xIyOU/a7bvGKj/3DB1iaPMTuPHf/Q2d5s4sn1FaCOO43XkXggnb08y5W2PU8UNYNLKQ==
-media-typer@0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
- integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
-
memoize-one@^5.0.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
@@ -7569,7 +7620,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
-mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34:
+mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.34:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
@@ -7603,13 +7654,6 @@ mimic-fn@^2.1.0:
dependencies:
brace-expansion "^1.1.7"
-minimatch@^5.0.1:
- version "5.1.6"
- resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
- integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
- dependencies:
- brace-expansion "^2.0.1"
-
minimatch@^9.0.1:
version "9.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825"
@@ -7643,14 +7687,7 @@ minipass-pipeline@^1.2.2:
dependencies:
minipass "^3.0.0"
-minipass@3.1.6:
- version "3.1.6"
- resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee"
- integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==
- dependencies:
- yallist "^4.0.0"
-
-minipass@^3.0.0, minipass@^3.1.1:
+minipass@3.3.6, minipass@^3.0.0, minipass@^3.1.1:
version "3.3.6"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a"
integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
@@ -7998,6 +8035,14 @@ open@^6.2.0:
dependencies:
is-wsl "^1.1.0"
+open@^7.0.3:
+ version "7.4.2"
+ resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321"
+ integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==
+ dependencies:
+ is-docker "^2.0.0"
+ is-wsl "^2.1.1"
+
open@^8.0.4, open@^8.3.0:
version "8.4.2"
resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9"
@@ -8019,7 +8064,7 @@ optionator@^0.9.3:
prelude-ls "^1.2.1"
type-check "^0.4.0"
-ora@3.4.0:
+ora@3.4.0, ora@^3.4.0:
version "3.4.0"
resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318"
integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg==
@@ -8168,11 +8213,6 @@ password-prompt@^1.0.4:
ansi-escapes "^4.3.2"
cross-spawn "^7.0.3"
-path-browserify@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
- integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
-
path-exists@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
@@ -8226,6 +8266,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1:
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+picomatch@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-3.0.1.tgz#817033161def55ec9638567a2f3bbc876b3e7516"
+ integrity sha512-I3EurrIQMlRc9IaAZnqRR044Phh2DXY+55o7uJ0V+hYZAcQYSuFWsc9q5PvyDHUSCe1Qxn/iBz+78s86zWnGag==
+
pify@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -8255,13 +8300,6 @@ pkg-dir@^4.2.0:
dependencies:
find-up "^4.0.0"
-pkg-up@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5"
- integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==
- dependencies:
- find-up "^3.0.0"
-
plist@^3.0.5:
version "3.1.0"
resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9"
@@ -8351,7 +8389,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
-postcss@^8.4.12, postcss@^8.4.23, postcss@^8.4.33, postcss@~8.4.21:
+postcss@^8.4.12, postcss@^8.4.23, postcss@^8.4.33, postcss@~8.4.32:
version "8.4.33"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742"
integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==
@@ -8545,16 +8583,6 @@ range-parser@~1.2.1:
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
-raw-body@2.5.2:
- version "2.5.2"
- resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
- integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
- dependencies:
- bytes "3.1.2"
- http-errors "2.0.0"
- iconv-lite "0.4.24"
- unpipe "1.0.0"
-
rc@~1.2.7:
version "1.2.8"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
@@ -8704,7 +8732,7 @@ react-native@0.72.6:
ws "^6.2.2"
yargs "^17.6.2"
-react-refresh@^0.4.0, react-refresh@~0.14.0:
+react-refresh@0.14.0, react-refresh@^0.14.0, react-refresh@^0.4.0, react-refresh@~0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
@@ -8887,11 +8915,6 @@ requires-port@^1.0.0:
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==
-reselect@^4.1.7:
- version "4.1.8"
- resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524"
- integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==
-
resolve-cwd@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
@@ -8914,12 +8937,12 @@ resolve-from@^5.0.0:
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
-resolve.exports@^2.0.0:
+resolve.exports@^2.0.0, resolve.exports@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800"
integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==
-resolve@^1.1.7, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.2:
+resolve@^1.1.7, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.2:
version "1.22.8"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d"
integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==
@@ -9034,7 +9057,7 @@ safe-regex-test@^1.0.0:
get-intrinsic "^1.2.2"
is-regex "^1.1.4"
-"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
+"safer-buffer@>= 2.1.2 < 3.0.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -9123,13 +9146,6 @@ send@0.18.0, send@^0.18.0:
range-parser "~1.2.1"
statuses "2.0.1"
-serialize-error@6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-6.0.0.tgz#ccfb887a1dd1c48d6d52d7863b92544331fd752b"
- integrity sha512-3vmBkMZLQO+BR4RPHcyRGdE09XCF6cvxzk2N2qn8Er3F91cy8Qt7VvEbZBOpaL53qsBbe2cFOefU6tRY6WDelA==
- dependencies:
- type-fest "^0.12.0"
-
serialize-error@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a"
@@ -9938,11 +9954,6 @@ type-detect@4.0.8:
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
-type-fest@^0.12.0:
- version "0.12.0"
- resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.12.0.tgz#f57a27ab81c68d136a51fd71467eff94157fa1ee"
- integrity sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg==
-
type-fest@^0.16.0:
version "0.16.0"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.16.0.tgz#3240b891a78b0deae910dbeb86553e552a148860"
@@ -9973,14 +9984,6 @@ type-fest@^3.0.0:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-3.13.1.tgz#bb744c1f0678bea7543a2d1ec24e83e68e8c8706"
integrity sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==
-type-is@~1.6.18:
- version "1.6.18"
- resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
- integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
- dependencies:
- media-typer "0.3.0"
- mime-types "~2.1.24"
-
typed-array-buffer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60"
@@ -10124,7 +10127,7 @@ universalify@^2.0.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.1.tgz#168efc2180964e6386d061e094df61afe239b18d"
integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==
-unpipe@1.0.0, unpipe@~1.0.0:
+unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
@@ -10149,7 +10152,7 @@ url-join@4.0.0:
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.0.tgz#4d3340e807d3773bda9991f8305acdcc2a665d2a"
integrity sha512-EGXjXJZhIHiQMK2pQukuFcL303nskqIRzWvPvV5O8miOfwoUb9G+a/Cld60kUyeaybEI94wvVClT10DtfeAExA==
-url-parse@^1.5.3, url-parse@^1.5.9:
+url-parse@^1.5.3:
version "1.5.10"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1"
integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==
@@ -10196,11 +10199,6 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
-uuid@^3.3.2, uuid@^3.4.0:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
- integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
-
uuid@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
From 503f4707171155d08563e75e4683c545fcebe5d6 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 11:41:07 -0500
Subject: [PATCH 16/33] Bump github.com/swaggo/swag from 1.16.2 to 1.16.3 in
/backend (#158)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
---
backend/go.mod | 2 +-
backend/go.sum | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/backend/go.mod b/backend/go.mod
index 7ea6f10af..655144427 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -8,7 +8,7 @@ require (
github.com/gofiber/swagger v0.1.14
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/spf13/viper v1.18.2
- github.com/swaggo/swag v1.16.2
+ github.com/swaggo/swag v1.16.3
gorm.io/driver/postgres v1.5.4
gorm.io/gorm v1.25.6
)
diff --git a/backend/go.sum b/backend/go.sum
index 3e4d881f4..ebd382283 100644
--- a/backend/go.sum
+++ b/backend/go.sum
@@ -141,8 +141,9 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
-github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04=
github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E=
+github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
+github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
From 68647af20554919238bb5bc02c8e620224981855 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 12:00:35 -0500
Subject: [PATCH 17/33] Bump github.com/gofiber/swagger from 0.1.14 to 1.0.0 in
/backend (#156)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
backend/go.mod | 2 +-
backend/go.sum | 60 ++------------------------------------------------
2 files changed, 3 insertions(+), 59 deletions(-)
diff --git a/backend/go.mod b/backend/go.mod
index 655144427..b1fb7d436 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -5,7 +5,7 @@ go 1.21.1
require (
github.com/go-playground/validator/v10 v10.17.0
github.com/gofiber/fiber/v2 v2.52.0
- github.com/gofiber/swagger v0.1.14
+ github.com/gofiber/swagger v1.0.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/spf13/viper v1.18.2
github.com/swaggo/swag v1.16.3
diff --git a/backend/go.sum b/backend/go.sum
index ebd382283..6441c0fc3 100644
--- a/backend/go.sum
+++ b/backend/go.sum
@@ -1,4 +1,3 @@
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
@@ -7,7 +6,6 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -39,16 +37,14 @@ github.com/go-playground/validator/v10 v10.17.0 h1:SmVVlfAOtlZncTxRuinDPomC2DkXJ
github.com/go-playground/validator/v10 v10.17.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
-github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw=
github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE=
github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
-github.com/gofiber/swagger v0.1.14 h1:o524wh4QaS4eKhUCpj7M0Qhn8hvtzcyxDsfZLXuQcRI=
-github.com/gofiber/swagger v0.1.14/go.mod h1:DCk1fUPsj+P07CKaZttBbV1WzTZSQcSxfub8y9/BFr8=
+github.com/gofiber/swagger v1.0.0 h1:BzUzDS9ZT6fDUa692kxmfOjc1DZiloLiPK/W5z1H1tc=
+github.com/gofiber/swagger v1.0.0/go.mod h1:QrYNF1Yrc7ggGK6ATsJ6yfH/8Zi5bu9lA7wB8TmCecg=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@@ -67,8 +63,6 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
-github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -90,7 +84,6 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
@@ -102,7 +95,6 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
-github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -110,12 +102,10 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
@@ -141,85 +131,42 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw=
github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM=
-github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E=
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
-github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
-github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
-golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
-golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
-golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -228,16 +175,13 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo=
gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0=
gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A=
gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
-sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
From 7fefe2456e946990e164017eb046f12931dfecf8 Mon Sep 17 00:00:00 2001
From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
Date: Mon, 5 Feb 2024 12:06:37 -0500
Subject: [PATCH 18/33] Update Repobeats Alt Text (#160)
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 7de95e555..af4932d48 100644
--- a/README.md
+++ b/README.md
@@ -43,7 +43,7 @@
## Repo Activity
-
+
## Contributors
From bd5eff9effad1eec571e38fceb5f78cae8ce61bc Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 12:07:30 -0500
Subject: [PATCH 19/33] Bump react-native from 0.72.6 to 0.73.3 in
/frontend/sac-mobile (#152)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
---
frontend/sac-mobile/package.json | 2 +-
frontend/sac-mobile/yarn.lock | 718 +++++++++++++------------------
2 files changed, 295 insertions(+), 425 deletions(-)
diff --git a/frontend/sac-mobile/package.json b/frontend/sac-mobile/package.json
index 012aadf5c..759d7d9e7 100644
--- a/frontend/sac-mobile/package.json
+++ b/frontend/sac-mobile/package.json
@@ -33,7 +33,7 @@
"nativewind": "^2.0.11",
"react": "18.2.0",
"react-dom": "18.2.0",
- "react-native": "0.72.6",
+ "react-native": "0.73.3",
"react-native-gesture-handler": "~2.14.1",
"react-native-safe-area-context": "4.8.2",
"react-native-screens": "~3.29.0",
diff --git a/frontend/sac-mobile/yarn.lock b/frontend/sac-mobile/yarn.lock
index 70591e420..ced9f0687 100644
--- a/frontend/sac-mobile/yarn.lock
+++ b/frontend/sac-mobile/yarn.lock
@@ -2070,7 +2070,7 @@
slash "^3.0.0"
strip-ansi "^6.0.0"
-"@jest/create-cache-key-function@^29.2.1":
+"@jest/create-cache-key-function@^29.2.1", "@jest/create-cache-key-function@^29.6.3":
version "29.7.0"
resolved "https://registry.yarnpkg.com/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz#793be38148fab78e65f40ae30c36785f4ad859f0"
integrity sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==
@@ -2222,17 +2222,6 @@
"@types/yargs" "^15.0.0"
chalk "^4.0.0"
-"@jest/types@^27.5.1":
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/@jest/types/-/types-27.5.1.tgz#3c79ec4a8ba61c170bf937bcf9e98a9df175ec80"
- integrity sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==
- dependencies:
- "@types/istanbul-lib-coverage" "^2.0.0"
- "@types/istanbul-reports" "^3.0.0"
- "@types/node" "*"
- "@types/yargs" "^16.0.0"
- chalk "^4.0.0"
-
"@jest/types@^29.6.3":
version "29.6.3"
resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.6.3.tgz#1131f8cf634e7e84c5e77bab12f052af585fba59"
@@ -2354,117 +2343,104 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-compose-refs" "1.0.0"
-"@react-native-community/cli-clean@11.3.7":
- version "11.3.7"
- resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-11.3.7.tgz#cb4c2f225f78593412c2d191b55b8570f409a48f"
- integrity sha512-twtsv54ohcRyWVzPXL3F9VHGb4Qhn3slqqRs3wEuRzjR7cTmV2TIO2b1VhaqF4HlCgNd+cGuirvLtK2JJyaxMg==
+"@react-native-community/cli-clean@12.3.2":
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-clean/-/cli-clean-12.3.2.tgz#d4f1730c3d22d816b4d513d330d5f3896a3f5921"
+ integrity sha512-90k2hCX0ddSFPT7EN7h5SZj0XZPXP0+y/++v262hssoey3nhurwF57NGWN0XAR0o9BSW7+mBfeInfabzDraO6A==
dependencies:
- "@react-native-community/cli-tools" "11.3.7"
+ "@react-native-community/cli-tools" "12.3.2"
chalk "^4.1.2"
execa "^5.0.0"
- prompts "^2.4.0"
-"@react-native-community/cli-config@11.3.7":
- version "11.3.7"
- resolved "https://registry.yarnpkg.com/@react-native-community/cli-config/-/cli-config-11.3.7.tgz#4ce95548252ecb094b576369abebf9867c95d277"
- integrity sha512-FDBLku9xskS+bx0YFJFLCmUJhEZ4/MMSC9qPYOGBollWYdgE7k/TWI0IeYFmMALAnbCdKQAYP5N29N55Tad8lg==
+"@react-native-community/cli-config@12.3.2":
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-config/-/cli-config-12.3.2.tgz#1a5de302de4d597ff2fc9932a032134b6ec4325f"
+ integrity sha512-UUCzDjQgvAVL/57rL7eOuFUhd+d+6qfM7V8uOegQFeFEmSmvUUDLYoXpBa5vAK9JgQtSqMBJ1Shmwao+/oElxQ==
dependencies:
- "@react-native-community/cli-tools" "11.3.7"
+ "@react-native-community/cli-tools" "12.3.2"
chalk "^4.1.2"
cosmiconfig "^5.1.0"
deepmerge "^4.3.0"
glob "^7.1.3"
joi "^17.2.1"
-"@react-native-community/cli-debugger-ui@11.3.7":
- version "11.3.7"
- resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-11.3.7.tgz#2147b73313af8de3c9b396406d5d344b904cf2bb"
- integrity sha512-aVmKuPKHZENR8SrflkMurZqeyLwbKieHdOvaZCh1Nn/0UC5CxWcyST2DB2XQboZwsvr3/WXKJkSUO+SZ1J9qTQ==
+"@react-native-community/cli-debugger-ui@12.3.2":
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-debugger-ui/-/cli-debugger-ui-12.3.2.tgz#b2743876b03e560fbf5ef516e95387fcb6d91630"
+ integrity sha512-nSWQUL+51J682DlfcC1bjkUbQbGvHCC25jpqTwHIjmmVjYCX1uHuhPSqQKgPNdvtfOkrkACxczd7kVMmetxY2Q==
dependencies:
serve-static "^1.13.1"
-"@react-native-community/cli-doctor@11.3.7":
- version "11.3.7"
- resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-11.3.7.tgz#7d5f5b1aea78134bba713fa97795986345ff1344"
- integrity sha512-YEHUqWISOHnsl5+NM14KHelKh68Sr5/HeEZvvNdIcvcKtZic3FU7Xd1WcbNdo3gCq5JvzGFfufx02Tabh5zmrg==
+"@react-native-community/cli-doctor@12.3.2":
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-doctor/-/cli-doctor-12.3.2.tgz#9e82b49f04ee03872b2975f26c8799cecac021ce"
+ integrity sha512-GrAabdY4qtBX49knHFvEAdLtCjkmndjTeqhYO6BhsbAeKOtspcLT/0WRgdLIaKODRa61ADNB3K5Zm4dU0QrZOg==
dependencies:
- "@react-native-community/cli-config" "11.3.7"
- "@react-native-community/cli-platform-android" "11.3.7"
- "@react-native-community/cli-platform-ios" "11.3.7"
- "@react-native-community/cli-tools" "11.3.7"
+ "@react-native-community/cli-config" "12.3.2"
+ "@react-native-community/cli-platform-android" "12.3.2"
+ "@react-native-community/cli-platform-ios" "12.3.2"
+ "@react-native-community/cli-tools" "12.3.2"
chalk "^4.1.2"
command-exists "^1.2.8"
- envinfo "^7.7.2"
+ deepmerge "^4.3.0"
+ envinfo "^7.10.0"
execa "^5.0.0"
hermes-profile-transformer "^0.0.6"
ip "^1.1.5"
node-stream-zip "^1.9.1"
ora "^5.4.1"
- prompts "^2.4.0"
semver "^7.5.2"
strip-ansi "^5.2.0"
- sudo-prompt "^9.0.0"
wcwidth "^1.0.1"
yaml "^2.2.1"
-"@react-native-community/cli-hermes@11.3.7":
- version "11.3.7"
- resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-11.3.7.tgz#091e730a1f8bace6c3729e8744bad6141002e0e8"
- integrity sha512-chkKd8n/xeZkinRvtH6QcYA8rjNOKU3S3Lw/3Psxgx+hAYV0Gyk95qJHTalx7iu+PwjOOqqvCkJo5jCkYLkoqw==
+"@react-native-community/cli-hermes@12.3.2":
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-hermes/-/cli-hermes-12.3.2.tgz#5f266985fe32a37e9020e881460e9017870be2e5"
+ integrity sha512-SL6F9O8ghp4ESBFH2YAPLtIN39jdnvGBKnK4FGKpDCjtB3DnUmDsGFlH46S+GGt5M6VzfG2eeKEOKf3pZ6jUzA==
dependencies:
- "@react-native-community/cli-platform-android" "11.3.7"
- "@react-native-community/cli-tools" "11.3.7"
+ "@react-native-community/cli-platform-android" "12.3.2"
+ "@react-native-community/cli-tools" "12.3.2"
chalk "^4.1.2"
hermes-profile-transformer "^0.0.6"
ip "^1.1.5"
-"@react-native-community/cli-platform-android@11.3.7":
- version "11.3.7"
- resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-11.3.7.tgz#7845bc48258b6bb55df208a23b3690647f113995"
- integrity sha512-WGtXI/Rm178UQb8bu1TAeFC/RJvYGnbHpULXvE20GkmeJ1HIrMjkagyk6kkY3Ej25JAP2R878gv+TJ/XiRhaEg==
+"@react-native-community/cli-platform-android@12.3.2":
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-android/-/cli-platform-android-12.3.2.tgz#de54d89712f8ea95046d798ec274fd6caea70c34"
+ integrity sha512-MZ5nO8yi/N+Fj2i9BJcJ9C/ez+9/Ir7lQt49DWRo9YDmzye66mYLr/P2l/qxsixllbbDi7BXrlLpxaEhMrDopg==
dependencies:
- "@react-native-community/cli-tools" "11.3.7"
+ "@react-native-community/cli-tools" "12.3.2"
chalk "^4.1.2"
execa "^5.0.0"
+ fast-xml-parser "^4.2.4"
glob "^7.1.3"
logkitty "^0.7.1"
-"@react-native-community/cli-platform-ios@11.3.7":
- version "11.3.7"
- resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-11.3.7.tgz#87478f907634713b7236c77870446a5ca1f35ff1"
- integrity sha512-Z/8rseBput49EldX7MogvN6zJlWzZ/4M97s2P+zjS09ZoBU7I0eOKLi0N9wx+95FNBvGQQ/0P62bB9UaFQH2jw==
+"@react-native-community/cli-platform-ios@12.3.2":
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-platform-ios/-/cli-platform-ios-12.3.2.tgz#07e298f69761424da85909790a43ec60ebfe6097"
+ integrity sha512-OcWEAbkev1IL6SUiQnM6DQdsvfsKZhRZtoBNSj9MfdmwotVZSOEZJ+IjZ1FR9ChvMWayO9ns/o8LgoQxr1ZXeg==
dependencies:
- "@react-native-community/cli-tools" "11.3.7"
+ "@react-native-community/cli-tools" "12.3.2"
chalk "^4.1.2"
execa "^5.0.0"
fast-xml-parser "^4.0.12"
glob "^7.1.3"
ora "^5.4.1"
-"@react-native-community/cli-plugin-metro@11.3.7":
- version "11.3.7"
- resolved "https://registry.yarnpkg.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-11.3.7.tgz#2e8a9deb30b40495c5c1347a1837a824400fa00f"
- integrity sha512-0WhgoBVGF1f9jXcuagQmtxpwpfP+2LbLZH4qMyo6OtYLWLG13n2uRep+8tdGzfNzl1bIuUTeE9yZSAdnf9LfYQ==
- dependencies:
- "@react-native-community/cli-server-api" "11.3.7"
- "@react-native-community/cli-tools" "11.3.7"
- chalk "^4.1.2"
- execa "^5.0.0"
- metro "0.76.8"
- metro-config "0.76.8"
- metro-core "0.76.8"
- metro-react-native-babel-transformer "0.76.8"
- metro-resolver "0.76.8"
- metro-runtime "0.76.8"
- readline "^1.3.0"
+"@react-native-community/cli-plugin-metro@12.3.2":
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-plugin-metro/-/cli-plugin-metro-12.3.2.tgz#7db7dc8939b821b9aeebdd5ee3293f3a0201a2ea"
+ integrity sha512-FpFBwu+d2E7KRhYPTkKvQsWb2/JKsJv+t1tcqgQkn+oByhp+qGyXBobFB8/R3yYvRRDCSDhS+atWTJzk9TjM8g==
-"@react-native-community/cli-server-api@11.3.7":
- version "11.3.7"
- resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-11.3.7.tgz#2cce54b3331c9c51b9067129c297ab2e9a142216"
- integrity sha512-yoFyGdvR3HxCnU6i9vFqKmmSqFzCbnFSnJ29a+5dppgPRetN+d//O8ard/YHqHzToFnXutAFf2neONn23qcJAg==
+"@react-native-community/cli-server-api@12.3.2":
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-server-api/-/cli-server-api-12.3.2.tgz#11df4e20ed72d59cf22adf77bd30aff3d6e70dc9"
+ integrity sha512-iwa7EO9XFA/OjI5pPLLpI/6mFVqv8L73kNck3CNOJIUCCveGXBKK0VMyOkXaf/BYnihgQrXh+x5cxbDbggr7+Q==
dependencies:
- "@react-native-community/cli-debugger-ui" "11.3.7"
- "@react-native-community/cli-tools" "11.3.7"
+ "@react-native-community/cli-debugger-ui" "12.3.2"
+ "@react-native-community/cli-tools" "12.3.2"
compression "^1.7.1"
connect "^3.6.5"
errorhandler "^1.5.1"
@@ -2473,10 +2449,10 @@
serve-static "^1.13.1"
ws "^7.5.1"
-"@react-native-community/cli-tools@11.3.7":
- version "11.3.7"
- resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-11.3.7.tgz#37aa7efc7b4a1b7077d541f1d7bb11a2ab7b6ff2"
- integrity sha512-peyhP4TV6Ps1hk+MBHTFaIR1eI3u+OfGBvr5r0wPwo3FAJvldRinMgcB/TcCcOBXVORu7ba1XYjkubPeYcqAyA==
+"@react-native-community/cli-tools@12.3.2":
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-tools/-/cli-tools-12.3.2.tgz#d3362b04fba3f73ec82c5a493696b575acfb420c"
+ integrity sha512-nDH7vuEicHI2TI0jac/DjT3fr977iWXRdgVAqPZFFczlbs7A8GQvEdGnZ1G8dqRUmg+kptw0e4hwczAOG89JzQ==
dependencies:
appdirsjs "^1.2.4"
chalk "^4.1.2"
@@ -2487,35 +2463,37 @@
ora "^5.4.1"
semver "^7.5.2"
shell-quote "^1.7.3"
+ sudo-prompt "^9.0.0"
-"@react-native-community/cli-types@11.3.7":
- version "11.3.7"
- resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-11.3.7.tgz#12fe7cff3da08bd27e11116531b2e001939854b9"
- integrity sha512-OhSr/TiDQkXjL5YOs8+hvGSB+HltLn5ZI0+A3DCiMsjUgTTsYh+Z63OtyMpNjrdCEFcg0MpfdU2uxstCS6Dc5g==
+"@react-native-community/cli-types@12.3.2":
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli-types/-/cli-types-12.3.2.tgz#0551c553c87701faae580097d7786dfff8ec2ef4"
+ integrity sha512-9D0UEFqLW8JmS16mjHJxUJWX8E+zJddrHILSH8AJHZ0NNHv4u2DXKdb0wFLMobFxGNxPT+VSOjc60fGvXzWHog==
dependencies:
joi "^17.2.1"
-"@react-native-community/cli@11.3.7":
- version "11.3.7"
- resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-11.3.7.tgz#564c0054269d8385fa9d301750b2e56dbb5c0cc9"
- integrity sha512-Ou8eDlF+yh2rzXeCTpMPYJ2fuqsusNOhmpYPYNQJQ2h6PvaF30kPomflgRILems+EBBuggRtcT+I+1YH4o/q6w==
- dependencies:
- "@react-native-community/cli-clean" "11.3.7"
- "@react-native-community/cli-config" "11.3.7"
- "@react-native-community/cli-debugger-ui" "11.3.7"
- "@react-native-community/cli-doctor" "11.3.7"
- "@react-native-community/cli-hermes" "11.3.7"
- "@react-native-community/cli-plugin-metro" "11.3.7"
- "@react-native-community/cli-server-api" "11.3.7"
- "@react-native-community/cli-tools" "11.3.7"
- "@react-native-community/cli-types" "11.3.7"
+"@react-native-community/cli@12.3.2":
+ version "12.3.2"
+ resolved "https://registry.yarnpkg.com/@react-native-community/cli/-/cli-12.3.2.tgz#002ae3683b9fe6b0a83a837f41d9db541ea7667f"
+ integrity sha512-WgoUWwLDcf/G1Su2COUUVs3RzAwnV/vUTdISSpAUGgSc57mPabaAoUctKTnfYEhCnE3j02k3VtaVPwCAFRO3TQ==
+ dependencies:
+ "@react-native-community/cli-clean" "12.3.2"
+ "@react-native-community/cli-config" "12.3.2"
+ "@react-native-community/cli-debugger-ui" "12.3.2"
+ "@react-native-community/cli-doctor" "12.3.2"
+ "@react-native-community/cli-hermes" "12.3.2"
+ "@react-native-community/cli-plugin-metro" "12.3.2"
+ "@react-native-community/cli-server-api" "12.3.2"
+ "@react-native-community/cli-tools" "12.3.2"
+ "@react-native-community/cli-types" "12.3.2"
chalk "^4.1.2"
commander "^9.4.1"
+ deepmerge "^4.3.0"
execa "^5.0.0"
find-up "^4.1.0"
fs-extra "^8.1.0"
graceful-fs "^4.1.3"
- prompts "^2.4.0"
+ prompts "^2.4.2"
semver "^7.5.2"
"@react-native-community/eslint-config@^3.2.0":
@@ -2542,12 +2520,7 @@
resolved "https://registry.yarnpkg.com/@react-native-community/eslint-plugin/-/eslint-plugin-1.3.0.tgz#9e558170c106bbafaa1ef502bd8e6d4651012bf9"
integrity sha512-+zDZ20NUnSWghj7Ku5aFphMzuM9JulqCW+aPXT6IfIXFbb8tzYTTOSeRFOtuekJ99ibW2fUCSsjuKNlwDIbHFg==
-"@react-native/assets-registry@^0.72.0":
- version "0.72.0"
- resolved "https://registry.yarnpkg.com/@react-native/assets-registry/-/assets-registry-0.72.0.tgz#c82a76a1d86ec0c3907be76f7faf97a32bbed05d"
- integrity sha512-Im93xRJuHHxb1wniGhBMsxLwcfzdYreSZVQGDoMJgkd6+Iky61LInGEHnQCTN0fKNYF1Dvcofb4uMmE1RQHXHQ==
-
-"@react-native/assets-registry@~0.73.1":
+"@react-native/assets-registry@0.73.1", "@react-native/assets-registry@~0.73.1":
version "0.73.1"
resolved "https://registry.yarnpkg.com/@react-native/assets-registry/-/assets-registry-0.73.1.tgz#e2a6b73b16c183a270f338dc69c36039b3946e85"
integrity sha512-2FgAbU7uKM5SbbW9QptPPZx8N9Ke2L7bsHb+EhAanZjFZunA9PaYtyjUQ1s7HD+zDVqOQIvjkpXSv7Kejd2tqg==
@@ -2559,7 +2532,7 @@
dependencies:
"@react-native/codegen" "0.73.2"
-"@react-native/babel-preset@^0.73.18":
+"@react-native/babel-preset@0.73.20", "@react-native/babel-preset@^0.73.18":
version "0.73.20"
resolved "https://registry.yarnpkg.com/@react-native/babel-preset/-/babel-preset-0.73.20.tgz#65ab68cce16bb222bb1faece498abb6f7b1d5db0"
integrity sha512-fU9NqkusbfFq71l4BWQfqqD/lLcLC0MZ++UYgieA3j8lIEppJTLVauv2RwtD2yltBkjebgYEC5Rwvt1l0MUBXw==
@@ -2620,25 +2593,29 @@
mkdirp "^0.5.1"
nullthrows "^1.1.1"
-"@react-native/codegen@^0.72.7":
- version "0.72.8"
- resolved "https://registry.yarnpkg.com/@react-native/codegen/-/codegen-0.72.8.tgz#0593f628e1310f430450a9479fbb4be35e7b63d6"
- integrity sha512-jQCcBlXV7B7ap5VlHhwIPieYz89yiRgwd2FPUBu+unz+kcJ6pAiB2U8RdLDmyIs8fiWd+Vq1xxaWs4TR329/ng==
+"@react-native/community-cli-plugin@0.73.14":
+ version "0.73.14"
+ resolved "https://registry.yarnpkg.com/@react-native/community-cli-plugin/-/community-cli-plugin-0.73.14.tgz#e7767df11a8f54fd84ebff36d8962ef733c8143d"
+ integrity sha512-KzIwsTvAJrXPtwhGOSm+OcJH1B8TpY8cS4xxzu/e2qv3a2n4VLePHTPAfco1tmvekV8OHWvvD9JSIX7i2fB1gg==
dependencies:
- "@babel/parser" "^7.20.0"
- flow-parser "^0.206.0"
- glob "^7.1.1"
- invariant "^2.2.4"
- jscodeshift "^0.14.0"
- mkdirp "^0.5.1"
- nullthrows "^1.1.1"
+ "@react-native-community/cli-server-api" "12.3.2"
+ "@react-native-community/cli-tools" "12.3.2"
+ "@react-native/dev-middleware" "0.73.7"
+ "@react-native/metro-babel-transformer" "0.73.14"
+ chalk "^4.0.0"
+ execa "^5.1.1"
+ metro "^0.80.3"
+ metro-config "^0.80.3"
+ metro-core "^0.80.3"
+ node-fetch "^2.2.0"
+ readline "^1.3.0"
"@react-native/debugger-frontend@0.73.3":
version "0.73.3"
resolved "https://registry.yarnpkg.com/@react-native/debugger-frontend/-/debugger-frontend-0.73.3.tgz#033757614d2ada994c68a1deae78c1dd2ad33c2b"
integrity sha512-RgEKnWuoo54dh7gQhV7kvzKhXZEhpF9LlMdZolyhGxHsBqZ2gXdibfDlfcARFFifPIiaZ3lXuOVVa4ei+uPgTw==
-"@react-native/dev-middleware@^0.73.6":
+"@react-native/dev-middleware@0.73.7", "@react-native/dev-middleware@^0.73.6":
version "0.73.7"
resolved "https://registry.yarnpkg.com/@react-native/dev-middleware/-/dev-middleware-0.73.7.tgz#61d2bf08973d9a537fa3f2a42deeb13530d721ae"
integrity sha512-BZXpn+qKp/dNdr4+TkZxXDttfx8YobDh8MFHsMk9usouLm22pKgFIPkGBV0X8Do4LBkFNPGtrnsKkWk/yuUXKg==
@@ -2654,35 +2631,40 @@
serve-static "^1.13.1"
temp-dir "^2.0.0"
-"@react-native/gradle-plugin@^0.72.11":
- version "0.72.11"
- resolved "https://registry.yarnpkg.com/@react-native/gradle-plugin/-/gradle-plugin-0.72.11.tgz#c063ef12778706611de7a1e42b74b14d9405fb9f"
- integrity sha512-P9iRnxiR2w7EHcZ0mJ+fmbPzMby77ZzV6y9sJI3lVLJzF7TLSdbwcQyD3lwMsiL+q5lKUHoZJS4sYmih+P2HXw==
+"@react-native/gradle-plugin@0.73.4":
+ version "0.73.4"
+ resolved "https://registry.yarnpkg.com/@react-native/gradle-plugin/-/gradle-plugin-0.73.4.tgz#aa55784a8c2b471aa89934db38c090d331baf23b"
+ integrity sha512-PMDnbsZa+tD55Ug+W8CfqXiGoGneSSyrBZCMb5JfiB3AFST3Uj5e6lw8SgI/B6SKZF7lG0BhZ6YHZsRZ5MlXmg==
-"@react-native/js-polyfills@^0.72.1":
- version "0.72.1"
- resolved "https://registry.yarnpkg.com/@react-native/js-polyfills/-/js-polyfills-0.72.1.tgz#905343ef0c51256f128256330fccbdb35b922291"
- integrity sha512-cRPZh2rBswFnGt5X5EUEPs0r+pAsXxYsifv/fgy9ZLQokuT52bPH+9xjDR+7TafRua5CttGW83wP4TntRcWNDA==
+"@react-native/js-polyfills@0.73.1":
+ version "0.73.1"
+ resolved "https://registry.yarnpkg.com/@react-native/js-polyfills/-/js-polyfills-0.73.1.tgz#730b0a7aaab947ae6f8e5aa9d995e788977191ed"
+ integrity sha512-ewMwGcumrilnF87H4jjrnvGZEaPFCAC4ebraEK+CurDDmwST/bIicI4hrOAv+0Z0F7DEK4O4H7r8q9vH7IbN4g==
+
+"@react-native/metro-babel-transformer@0.73.14":
+ version "0.73.14"
+ resolved "https://registry.yarnpkg.com/@react-native/metro-babel-transformer/-/metro-babel-transformer-0.73.14.tgz#a4ee02c729216e4ab5b7c7aa28abbfe8e0a943a8"
+ integrity sha512-5wLeYw/lormpSqYfI9H/geZ/EtPmi+x5qLkEit15Q/70hkzYo/M+aWztUtbOITfgTEOP8d6ybROzoGsqgyZLcw==
+ dependencies:
+ "@babel/core" "^7.20.0"
+ "@react-native/babel-preset" "0.73.20"
+ hermes-parser "0.15.0"
+ nullthrows "^1.1.1"
"@react-native/normalize-color@^2.0.0", "@react-native/normalize-color@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@react-native/normalize-color/-/normalize-color-2.1.0.tgz#939b87a9849e81687d3640c5efa2a486ac266f91"
integrity sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA==
-"@react-native/normalize-colors@*":
- version "0.74.1"
- resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.74.1.tgz#6e8ccf99954728dcd3cfe0d56e758ee5050a7bea"
- integrity sha512-r+bTRs6pImqE3fx4h7bPzH2sOWSrnSHF/RJ7d00pNUj2P6ws3DdhS7WV+/7YosZkloYQfkiIkK3pIHvcYn665w==
-
-"@react-native/normalize-colors@^0.72.0":
- version "0.72.0"
- resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.72.0.tgz#14294b7ed3c1d92176d2a00df48456e8d7d62212"
- integrity sha512-285lfdqSXaqKuBbbtP9qL2tDrfxdOFtIMvkKadtleRQkdOxx+uzGvFr82KHmc/sSiMtfXGp7JnFYWVh4sFl7Yw==
+"@react-native/normalize-colors@0.73.2", "@react-native/normalize-colors@^0.73.0":
+ version "0.73.2"
+ resolved "https://registry.yarnpkg.com/@react-native/normalize-colors/-/normalize-colors-0.73.2.tgz#cc8e48fbae2bbfff53e12f209369e8d2e4cf34ec"
+ integrity sha512-bRBcb2T+I88aG74LMVHaKms2p/T8aQd8+BZ7LuuzXlRfog1bMWWn/C5i0HVuvW4RPtXQYgIlGiXVDy9Ir1So/w==
-"@react-native/virtualized-lists@^0.72.8":
- version "0.72.8"
- resolved "https://registry.yarnpkg.com/@react-native/virtualized-lists/-/virtualized-lists-0.72.8.tgz#a2c6a91ea0f1d40eb5a122fb063daedb92ed1dc3"
- integrity sha512-J3Q4Bkuo99k7mu+jPS9gSUSgq+lLRSI/+ahXNwV92XgJ/8UgOTxu2LPwhJnBk/sQKxq7E8WkZBnBiozukQMqrw==
+"@react-native/virtualized-lists@0.73.4":
+ version "0.73.4"
+ resolved "https://registry.yarnpkg.com/@react-native/virtualized-lists/-/virtualized-lists-0.73.4.tgz#640e594775806f63685435b5d9c3d05c378ccd8c"
+ integrity sha512-HpmLg1FrEiDtrtAbXiwCgXFYyloK/dOIPIuWW3fsqukwJEWAiTzm1nXGJ7xPU5XTHiWZ4sKup5Ebaj8z7iyWog==
dependencies:
invariant "^2.2.4"
nullthrows "^1.1.1"
@@ -3027,13 +3009,6 @@
dependencies:
"@types/yargs-parser" "*"
-"@types/yargs@^16.0.0":
- version "16.0.9"
- resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-16.0.9.tgz#ba506215e45f7707e6cbcaf386981155b7ab956e"
- integrity sha512-tHhzvkFXZQeTECenFoRljLBYPZJ7jAVxqqtEI0qTLOmuultnFp4I9yKE17vTuhf7BkhCu7I4XuemPgikDVuYqA==
- dependencies:
- "@types/yargs-parser" "*"
-
"@types/yargs@^17.0.8":
version "17.0.32"
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.32.tgz#030774723a2f7faafebf645f4e5a48371dca6229"
@@ -3475,11 +3450,6 @@ async-limiter@~1.0.0:
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
-async@^3.2.2:
- version "3.2.5"
- resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66"
- integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==
-
asynciterator.prototype@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz#8c5df0514936cdd133604dfcc9d3fb93f09b2b62"
@@ -3687,7 +3657,7 @@ base-64@1.0.0:
resolved "https://registry.yarnpkg.com/base-64/-/base-64-1.0.0.tgz#09d0f2084e32a3fd08c2475b973788eee6ae8f4a"
integrity sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==
-base64-js@^1.1.2, base64-js@^1.2.3, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1:
+base64-js@^1.2.3, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
@@ -4163,11 +4133,6 @@ commander@^9.4.1:
resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30"
integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==
-commander@~2.13.0:
- version "2.13.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c"
- integrity sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==
-
commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
@@ -4545,14 +4510,14 @@ depd@2.0.0:
resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
-deprecated-react-native-prop-types@4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-4.1.0.tgz#8ed03a64c21b7fbdd2d000957b6838d4f38d2c66"
- integrity sha512-WfepZHmRbbdTvhcolb8aOKEvQdcmTMn5tKLbqbXmkBvjFjRVWAYqsXk/DBsV8TZxws8SdGHLuHaJrHSQUPRdfw==
+deprecated-react-native-prop-types@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-5.0.0.tgz#02a12f090da7bd9e8c3ac53c31cf786a1315d302"
+ integrity sha512-cIK8KYiiGVOFsKdPMmm1L3tA/Gl+JopXL6F5+C7x39MyPsQYnP57Im/D6bNUzcborD7fcMwiwZqcBdBXXZucYQ==
dependencies:
- "@react-native/normalize-colors" "*"
- invariant "*"
- prop-types "*"
+ "@react-native/normalize-colors" "^0.73.0"
+ invariant "^2.2.4"
+ prop-types "^15.8.1"
dequal@2.0.3:
version "2.0.3"
@@ -4679,10 +4644,10 @@ env-editor@^0.4.1:
resolved "https://registry.yarnpkg.com/env-editor/-/env-editor-0.4.2.tgz#4e76568d0bd8f5c2b6d314a9412c8fe9aa3ae861"
integrity sha512-ObFo8v4rQJAE59M69QzwloxPZtd33TpYEIjtKD1rrFDcM1Gd7IkDxEBU+HriziN6HSHQnBJi8Dmy+JWkav5HKA==
-envinfo@^7.7.2:
- version "7.11.0"
- resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f"
- integrity sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg==
+envinfo@^7.10.0:
+ version "7.11.1"
+ resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.1.tgz#2ffef77591057081b0129a8fd8cf6118da1b94e1"
+ integrity sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg==
eol@^0.9.1:
version "0.9.1"
@@ -5065,7 +5030,7 @@ execa@^1.0.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"
-execa@^5.0.0:
+execa@^5.0.0, execa@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
@@ -5310,6 +5275,13 @@ fast-xml-parser@^4.0.12:
dependencies:
strnum "^1.0.5"
+fast-xml-parser@^4.2.4:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.3.4.tgz#385cc256ad7bbc57b91515a38a22502a9e1fca0d"
+ integrity sha512-utnwm92SyozgA3hhH2I8qldf2lBqm6qHOICawRNRFu1qMe3+oqr+GcXjGqTmXTMGE5T4eC03kr/rlh5C1IRdZA==
+ dependencies:
+ strnum "^1.0.5"
+
fastest-levenshtein@1.0.16:
version "1.0.16"
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
@@ -5449,10 +5421,10 @@ flatted@^3.2.9:
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf"
integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==
-flow-enums-runtime@^0.0.5:
- version "0.0.5"
- resolved "https://registry.yarnpkg.com/flow-enums-runtime/-/flow-enums-runtime-0.0.5.tgz#95884bfcc82edaf27eef7e1dd09732331cfbafbc"
- integrity sha512-PSZF9ZuaZD03sT9YaIs0FrGJ7lSUw7rHZIex+73UYVXg46eL/wxN5PaVcPJFudE2cJu5f0fezitV5aBkLHPUOQ==
+flow-enums-runtime@^0.0.6:
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz#5bb0cd1b0a3e471330f4d109039b7eba5cb3e787"
+ integrity sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==
flow-parser@0.*:
version "0.226.0"
@@ -5809,17 +5781,29 @@ hasown@^2.0.0:
dependencies:
function-bind "^1.1.2"
-hermes-estree@0.12.0:
- version "0.12.0"
- resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.12.0.tgz#8a289f9aee854854422345e6995a48613bac2ca8"
- integrity sha512-+e8xR6SCen0wyAKrMT3UD0ZCCLymKhRgjEB5sS28rKiFir/fXgLoeRilRUssFCILmGHb+OvHDUlhxs0+IEyvQw==
+hermes-estree@0.15.0:
+ version "0.15.0"
+ resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.15.0.tgz#e32f6210ab18c7b705bdcb375f7700f2db15d6ba"
+ integrity sha512-lLYvAd+6BnOqWdnNbP/Q8xfl8LOGw4wVjfrNd9Gt8eoFzhNBRVD95n4l2ksfMVOoxuVyegs85g83KS9QOsxbVQ==
-hermes-parser@0.12.0:
- version "0.12.0"
- resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.12.0.tgz#114dc26697cfb41a6302c215b859b74224383773"
- integrity sha512-d4PHnwq6SnDLhYl3LHNHvOg7nQ6rcI7QVil418REYksv0Mh3cEkHDcuhGxNQ3vgnLSLl4QSvDrFCwQNYdpWlzw==
+hermes-estree@0.18.2:
+ version "0.18.2"
+ resolved "https://registry.yarnpkg.com/hermes-estree/-/hermes-estree-0.18.2.tgz#fd450fa1659cf074ceaa2ddeeb21674f3b2342f3"
+ integrity sha512-KoLsoWXJ5o81nit1wSyEZnWUGy9cBna9iYMZBR7skKh7okYAYKqQ9/OczwpMHn/cH0hKDyblulGsJ7FknlfVxQ==
+
+hermes-parser@0.15.0:
+ version "0.15.0"
+ resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.15.0.tgz#f611a297c2a2dbbfbce8af8543242254f604c382"
+ integrity sha512-Q1uks5rjZlE9RjMMjSUCkGrEIPI5pKJILeCtK1VmTj7U4pf3wVPoo+cxfu+s4cBAPy2JzikIIdCZgBoR6x7U1Q==
+ dependencies:
+ hermes-estree "0.15.0"
+
+hermes-parser@0.18.2:
+ version "0.18.2"
+ resolved "https://registry.yarnpkg.com/hermes-parser/-/hermes-parser-0.18.2.tgz#50f15e2fcd559a48c68cd7af259d4292298bd14d"
+ integrity sha512-1eQfvib+VPpgBZ2zYKQhpuOjw1tH+Emuib6QmjkJWJMhyjM8xnXMvA+76o9LhF0zOAJDZgPfQhg43cyXEyl5Ew==
dependencies:
- hermes-estree "0.12.0"
+ hermes-estree "0.18.2"
hermes-profile-transformer@^0.0.6:
version "0.0.6"
@@ -5998,7 +5982,7 @@ internal-slot@^1.0.5:
hasown "^2.0.0"
side-channel "^1.0.4"
-invariant@*, invariant@^2.2.4:
+invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
@@ -6539,7 +6523,7 @@ jest-environment-jsdom@^29.2.1:
jest-util "^29.7.0"
jsdom "^20.0.0"
-jest-environment-node@^29.2.1, jest-environment-node@^29.7.0:
+jest-environment-node@^29.6.3, jest-environment-node@^29.7.0:
version "29.7.0"
resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.7.0.tgz#0b93e111dda8ec120bc8300e6d1fb9576e164376"
integrity sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==
@@ -6640,11 +6624,6 @@ jest-pnp-resolver@^1.2.2:
resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz#930b1546164d4ad5937d5540e711d4d38d4cad2e"
integrity sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==
-jest-regex-util@^27.0.6:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-27.5.1.tgz#4da143f7e9fd1e542d4aa69617b38e4a78365b95"
- integrity sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==
-
jest-regex-util@^29.0.0, jest-regex-util@^29.6.3:
version "29.6.3"
resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.6.3.tgz#4a556d9c776af68e1c5f48194f4d0327d24e8a52"
@@ -6754,18 +6733,6 @@ jest-snapshot@^29.7.0:
pretty-format "^29.7.0"
semver "^7.5.3"
-jest-util@^27.2.0:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-27.5.1.tgz#3ba9771e8e31a0b85da48fe0b0891fb86c01c2f9"
- integrity sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==
- dependencies:
- "@jest/types" "^27.5.1"
- "@types/node" "*"
- chalk "^4.0.0"
- ci-info "^3.2.0"
- graceful-fs "^4.2.9"
- picomatch "^2.2.3"
-
jest-util@^29.7.0:
version "29.7.0"
resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.7.0.tgz#23c2b62bfb22be82b44de98055802ff3710fc0bc"
@@ -6778,7 +6745,7 @@ jest-util@^29.7.0:
graceful-fs "^4.2.9"
picomatch "^2.2.3"
-jest-validate@^29.2.1, jest-validate@^29.7.0:
+jest-validate@^29.6.3, jest-validate@^29.7.0:
version "29.7.0"
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.7.0.tgz#7bf705511c64da591d46b15fce41400d52147d9c"
integrity sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==
@@ -6826,16 +6793,7 @@ jest-watcher@^29.0.0, jest-watcher@^29.7.0:
jest-util "^29.7.0"
string-length "^4.0.1"
-jest-worker@^27.2.0:
- version "27.5.1"
- resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0"
- integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==
- dependencies:
- "@types/node" "*"
- merge-stream "^2.0.0"
- supports-color "^8.0.0"
-
-jest-worker@^29.7.0:
+jest-worker@^29.6.3, jest-worker@^29.7.0:
version "29.7.0"
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.7.0.tgz#acad073acbbaeb7262bd5389e1bcf43e10058d4a"
integrity sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==
@@ -7341,62 +7299,60 @@ merge2@^1.3.0, merge2@^1.4.1:
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
-metro-babel-transformer@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.76.8.tgz#5efd1027353b36b73706164ef09c290dceac096a"
- integrity sha512-Hh6PW34Ug/nShlBGxkwQJSgPGAzSJ9FwQXhUImkzdsDgVu6zj5bx258J8cJVSandjNoQ8nbaHK6CaHlnbZKbyA==
+metro-babel-transformer@0.80.5:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro-babel-transformer/-/metro-babel-transformer-0.80.5.tgz#a31bdafe22c63d2548c78a41fd902bf2a98af681"
+ integrity sha512-sxH6hcWCorhTbk4kaShCWsadzu99WBL4Nvq4m/sDTbp32//iGuxtAnUK+ZV+6IEygr2u9Z0/4XoZ8Sbcl71MpA==
dependencies:
"@babel/core" "^7.20.0"
- hermes-parser "0.12.0"
+ hermes-parser "0.18.2"
nullthrows "^1.1.1"
-metro-cache-key@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.76.8.tgz#8a0a5e991c06f56fcc584acadacb313c312bdc16"
- integrity sha512-buKQ5xentPig9G6T37Ww/R/bC+/V1MA5xU/D8zjnhlelsrPG6w6LtHUS61ID3zZcMZqYaELWk5UIadIdDsaaLw==
+metro-cache-key@0.80.5:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro-cache-key/-/metro-cache-key-0.80.5.tgz#3fd0ce5a360e0455dc8b68a659c60abde3edac1d"
+ integrity sha512-fr3QLZUarsB3tRbVcmr34kCBsTHk0Sh9JXGvBY/w3b2lbre+Lq5gtgLyFElHPecGF7o4z1eK9r3ubxtScHWcbA==
-metro-cache@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.76.8.tgz#296c1c189db2053b89735a8f33dbe82575f53661"
- integrity sha512-QBJSJIVNH7Hc/Yo6br/U/qQDUpiUdRgZ2ZBJmvAbmAKp2XDzsapnMwK/3BGj8JNWJF7OLrqrYHsRsukSbUBpvQ==
+metro-cache@0.80.5:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro-cache/-/metro-cache-0.80.5.tgz#adc2e48d87312d68f305f830b22359cb4362ce4b"
+ integrity sha512-2u+dQ4PZwmC7eZo9uMBNhQQMig9f+w4QWBZwXCdVy/RYOHM0eObgGdMEOwODo73uxie82T9lWzxr3aZOZ+Nqtw==
dependencies:
- metro-core "0.76.8"
+ metro-core "0.80.5"
rimraf "^3.0.2"
-metro-config@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.76.8.tgz#20bd5397fcc6096f98d2a813a7cecb38b8af062d"
- integrity sha512-SL1lfKB0qGHALcAk2zBqVgQZpazDYvYFGwCK1ikz0S6Y/CM2i2/HwuZN31kpX6z3mqjv/6KvlzaKoTb1otuSAA==
+metro-config@0.80.5, metro-config@^0.80.3:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro-config/-/metro-config-0.80.5.tgz#859e2ce307372f583bc8ebfe69bd233dd9328a8b"
+ integrity sha512-elqo/lwvF+VjZ1OPyvmW/9hSiGlmcqu+rQvDKw5F5WMX48ZC+ySTD1WcaD7e97pkgAlJHVYqZ98FCjRAYOAFRQ==
dependencies:
connect "^3.6.5"
cosmiconfig "^5.0.5"
- jest-validate "^29.2.1"
- metro "0.76.8"
- metro-cache "0.76.8"
- metro-core "0.76.8"
- metro-runtime "0.76.8"
+ jest-validate "^29.6.3"
+ metro "0.80.5"
+ metro-cache "0.80.5"
+ metro-core "0.80.5"
+ metro-runtime "0.80.5"
-metro-core@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.76.8.tgz#917c8157c63406cb223522835abb8e7c6291dcad"
- integrity sha512-sl2QLFI3d1b1XUUGxwzw/KbaXXU/bvFYrSKz6Sg19AdYGWFyzsgZ1VISRIDf+HWm4R/TJXluhWMEkEtZuqi3qA==
+metro-core@0.80.5, metro-core@^0.80.3:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro-core/-/metro-core-0.80.5.tgz#3ead635dcecfa6d0b380f8a80d3e5406f70846a9"
+ integrity sha512-vkLuaBhnZxTVpaZO8ZJVEHzjaqSXpOdpAiztSZ+NDaYM6jEFgle3/XIbLW91jTSf2+T8Pj5yB1G7KuOX+BcVwg==
dependencies:
lodash.throttle "^4.1.1"
- metro-resolver "0.76.8"
+ metro-resolver "0.80.5"
-metro-file-map@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.76.8.tgz#a1db1185b6c316904ba6b53d628e5d1323991d79"
- integrity sha512-A/xP1YNEVwO1SUV9/YYo6/Y1MmzhL4ZnVgcJC3VmHp/BYVOXVStzgVbWv2wILe56IIMkfXU+jpXrGKKYhFyHVw==
+metro-file-map@0.80.5:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro-file-map/-/metro-file-map-0.80.5.tgz#1e2f0026c1a380a8802c977279018093c21b191e"
+ integrity sha512-bKCvJ05drjq6QhQxnDUt3I8x7bTcHo3IIKVobEr14BK++nmxFGn/BmFLRzVBlghM6an3gqwpNEYxS5qNc+VKcg==
dependencies:
anymatch "^3.0.3"
debug "^2.2.0"
fb-watchman "^2.0.0"
graceful-fs "^4.2.4"
invariant "^2.2.4"
- jest-regex-util "^27.0.6"
- jest-util "^27.2.0"
- jest-worker "^27.2.0"
+ jest-worker "^29.6.3"
micromatch "^4.0.4"
node-abort-controller "^3.1.1"
nullthrows "^1.1.1"
@@ -7404,130 +7360,55 @@ metro-file-map@0.76.8:
optionalDependencies:
fsevents "^2.3.2"
-metro-inspector-proxy@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-inspector-proxy/-/metro-inspector-proxy-0.76.8.tgz#6b8678a7461b0b42f913a7881cc9319b4d3cddff"
- integrity sha512-Us5o5UEd4Smgn1+TfHX4LvVPoWVo9VsVMn4Ldbk0g5CQx3Gu0ygc/ei2AKPGTwsOZmKxJeACj7yMH2kgxQP/iw==
- dependencies:
- connect "^3.6.5"
- debug "^2.2.0"
- node-fetch "^2.2.0"
- ws "^7.5.1"
- yargs "^17.6.2"
-
-metro-minify-terser@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.76.8.tgz#915ab4d1419257fc6a0b9fa15827b83fe69814bf"
- integrity sha512-Orbvg18qXHCrSj1KbaeSDVYRy/gkro2PC7Fy2tDSH1c9RB4aH8tuMOIXnKJE+1SXxBtjWmQ5Yirwkth2DyyEZA==
+metro-minify-terser@0.80.5:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro-minify-terser/-/metro-minify-terser-0.80.5.tgz#6163fc920faad46153456fcd191ccc1ce0450946"
+ integrity sha512-S7oZLLcab6YXUT6jYFX/ZDMN7Fq6xBGGAG8liMFU1UljX6cTcEC2u+UIafYgCLrdVexp/+ClxrIetVPZ5LtL/g==
dependencies:
terser "^5.15.0"
-metro-minify-uglify@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-minify-uglify/-/metro-minify-uglify-0.76.8.tgz#74745045ea2dd29f8783db483b2fce58385ba695"
- integrity sha512-6l8/bEvtVaTSuhG1FqS0+Mc8lZ3Bl4RI8SeRIifVLC21eeSDp4CEBUWSGjpFyUDfi6R5dXzYaFnSgMNyfxADiQ==
- dependencies:
- uglify-es "^3.1.9"
-
-metro-react-native-babel-preset@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-react-native-babel-preset/-/metro-react-native-babel-preset-0.76.8.tgz#7476efae14363cbdfeeec403b4f01d7348e6c048"
- integrity sha512-Ptza08GgqzxEdK8apYsjTx2S8WDUlS2ilBlu9DR1CUcHmg4g3kOkFylZroogVAUKtpYQNYwAvdsjmrSdDNtiAg==
- dependencies:
- "@babel/core" "^7.20.0"
- "@babel/plugin-proposal-async-generator-functions" "^7.0.0"
- "@babel/plugin-proposal-class-properties" "^7.18.0"
- "@babel/plugin-proposal-export-default-from" "^7.0.0"
- "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.0"
- "@babel/plugin-proposal-numeric-separator" "^7.0.0"
- "@babel/plugin-proposal-object-rest-spread" "^7.20.0"
- "@babel/plugin-proposal-optional-catch-binding" "^7.0.0"
- "@babel/plugin-proposal-optional-chaining" "^7.20.0"
- "@babel/plugin-syntax-dynamic-import" "^7.8.0"
- "@babel/plugin-syntax-export-default-from" "^7.0.0"
- "@babel/plugin-syntax-flow" "^7.18.0"
- "@babel/plugin-syntax-nullish-coalescing-operator" "^7.0.0"
- "@babel/plugin-syntax-optional-chaining" "^7.0.0"
- "@babel/plugin-transform-arrow-functions" "^7.0.0"
- "@babel/plugin-transform-async-to-generator" "^7.20.0"
- "@babel/plugin-transform-block-scoping" "^7.0.0"
- "@babel/plugin-transform-classes" "^7.0.0"
- "@babel/plugin-transform-computed-properties" "^7.0.0"
- "@babel/plugin-transform-destructuring" "^7.20.0"
- "@babel/plugin-transform-flow-strip-types" "^7.20.0"
- "@babel/plugin-transform-function-name" "^7.0.0"
- "@babel/plugin-transform-literals" "^7.0.0"
- "@babel/plugin-transform-modules-commonjs" "^7.0.0"
- "@babel/plugin-transform-named-capturing-groups-regex" "^7.0.0"
- "@babel/plugin-transform-parameters" "^7.0.0"
- "@babel/plugin-transform-react-display-name" "^7.0.0"
- "@babel/plugin-transform-react-jsx" "^7.0.0"
- "@babel/plugin-transform-react-jsx-self" "^7.0.0"
- "@babel/plugin-transform-react-jsx-source" "^7.0.0"
- "@babel/plugin-transform-runtime" "^7.0.0"
- "@babel/plugin-transform-shorthand-properties" "^7.0.0"
- "@babel/plugin-transform-spread" "^7.0.0"
- "@babel/plugin-transform-sticky-regex" "^7.0.0"
- "@babel/plugin-transform-typescript" "^7.5.0"
- "@babel/plugin-transform-unicode-regex" "^7.0.0"
- "@babel/template" "^7.0.0"
- babel-plugin-transform-flow-enums "^0.0.2"
- react-refresh "^0.4.0"
-
-metro-react-native-babel-transformer@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-react-native-babel-transformer/-/metro-react-native-babel-transformer-0.76.8.tgz#c3a98e1f4cd5faf1e21eba8e004b94a90c4db69b"
- integrity sha512-3h+LfS1WG1PAzhq8QF0kfXjxuXetbY/lgz8vYMQhgrMMp17WM1DNJD0gjx8tOGYbpbBC1qesJ45KMS4o5TA73A==
- dependencies:
- "@babel/core" "^7.20.0"
- babel-preset-fbjs "^3.4.0"
- hermes-parser "0.12.0"
- metro-react-native-babel-preset "0.76.8"
- nullthrows "^1.1.1"
-
-metro-resolver@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.76.8.tgz#0862755b9b84e26853978322464fb37c6fdad76d"
- integrity sha512-KccOqc10vrzS7ZhG2NSnL2dh3uVydarB7nOhjreQ7C4zyWuiW9XpLC4h47KtGQv3Rnv/NDLJYeDqaJ4/+140HQ==
+metro-resolver@0.80.5:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro-resolver/-/metro-resolver-0.80.5.tgz#3915be3b2bcf4f3e9e2f24bdde8d8c9ac26bb134"
+ integrity sha512-haJ/Hveio3zv/Fr4eXVdKzjUeHHDogYok7OpRqPSXGhTXisNXB+sLN7CpcUrCddFRUDLnVaqQOYwhYsFndgUwA==
-metro-runtime@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.76.8.tgz#74b2d301a2be5f3bbde91b8f1312106f8ffe50c3"
- integrity sha512-XKahvB+iuYJSCr3QqCpROli4B4zASAYpkK+j3a0CJmokxCDNbgyI4Fp88uIL6rNaZfN0Mv35S0b99SdFXIfHjg==
+metro-runtime@0.80.5, metro-runtime@^0.80.3:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro-runtime/-/metro-runtime-0.80.5.tgz#48ac4c732be195e0ebdefb5ac5d32c7da76305ad"
+ integrity sha512-L0syTWJUdWzfUmKgkScr6fSBVTh6QDr8eKEkRtn40OBd8LPagrJGySBboWSgbyn9eIb4ayW3Y347HxgXBSAjmg==
dependencies:
"@babel/runtime" "^7.0.0"
- react-refresh "^0.4.0"
-metro-source-map@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.76.8.tgz#f085800152a6ba0b41ca26833874d31ec36c5a53"
- integrity sha512-Hh0ncPsHPVf6wXQSqJqB3K9Zbudht4aUtNpNXYXSxH+pteWqGAXnjtPsRAnCsCWl38wL0jYF0rJDdMajUI3BDw==
+metro-source-map@0.80.5, metro-source-map@^0.80.3:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro-source-map/-/metro-source-map-0.80.5.tgz#21acdc1d5417cf3009209555d84cbeb61de5d6d5"
+ integrity sha512-DwSF4l03mKPNqCtyQ6K23I43qzU1BViAXnuH81eYWdHglP+sDlPpY+/7rUahXEo6qXEHXfAJgVoo1sirbXbmsQ==
dependencies:
"@babel/traverse" "^7.20.0"
"@babel/types" "^7.20.0"
invariant "^2.2.4"
- metro-symbolicate "0.76.8"
+ metro-symbolicate "0.80.5"
nullthrows "^1.1.1"
- ob1 "0.76.8"
+ ob1 "0.80.5"
source-map "^0.5.6"
vlq "^1.0.0"
-metro-symbolicate@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.76.8.tgz#f102ac1a306d51597ecc8fdf961c0a88bddbca03"
- integrity sha512-LrRL3uy2VkzrIXVlxoPtqb40J6Bf1mlPNmUQewipc3qfKKFgtPHBackqDy1YL0njDsWopCKcfGtFYLn0PTUn3w==
+metro-symbolicate@0.80.5:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro-symbolicate/-/metro-symbolicate-0.80.5.tgz#7c253a8e05e2a8380753373ab30705d6e81094a5"
+ integrity sha512-IsM4mTYvmo9JvIqwEkCZ5+YeDVPST78Q17ZgljfLdHLSpIivOHp9oVoiwQ/YGbLx0xRHRIS/tKiXueWBnj3UWA==
dependencies:
invariant "^2.2.4"
- metro-source-map "0.76.8"
+ metro-source-map "0.80.5"
nullthrows "^1.1.1"
source-map "^0.5.6"
through2 "^2.0.1"
vlq "^1.0.0"
-metro-transform-plugins@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.76.8.tgz#d77c28a6547a8e3b72250f740fcfbd7f5408f8ba"
- integrity sha512-PlkGTQNqS51Bx4vuufSQCdSn2R2rt7korzngo+b5GCkeX5pjinPjnO2kNhQ8l+5bO0iUD/WZ9nsM2PGGKIkWFA==
+metro-transform-plugins@0.80.5:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro-transform-plugins/-/metro-transform-plugins-0.80.5.tgz#ef90d8d6522b042b832a571381f1a5a9c2c72adf"
+ integrity sha512-7IdlTqK/k5+qE3RvIU5QdCJUPk4tHWEqgVuYZu8exeW+s6qOJ66hGIJjXY/P7ccucqF+D4nsbAAW5unkoUdS6g==
dependencies:
"@babel/core" "^7.20.0"
"@babel/generator" "^7.20.0"
@@ -7535,28 +7416,28 @@ metro-transform-plugins@0.76.8:
"@babel/traverse" "^7.20.0"
nullthrows "^1.1.1"
-metro-transform-worker@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.76.8.tgz#b9012a196cee205170d0c899b8b175b9305acdea"
- integrity sha512-mE1fxVAnJKmwwJyDtThildxxos9+DGs9+vTrx2ktSFMEVTtXS/bIv2W6hux1pqivqAfyJpTeACXHk5u2DgGvIQ==
+metro-transform-worker@0.80.5:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro-transform-worker/-/metro-transform-worker-0.80.5.tgz#45b78093f5925cbbf1ecf2695114469dd49f5169"
+ integrity sha512-Q1oM7hfP+RBgAtzRFBDjPhArELUJF8iRCZ8OidqCpYzQJVGuJZ7InSnIf3hn1JyqiUQwv2f1LXBO78i2rAjzyA==
dependencies:
"@babel/core" "^7.20.0"
"@babel/generator" "^7.20.0"
"@babel/parser" "^7.20.0"
"@babel/types" "^7.20.0"
- babel-preset-fbjs "^3.4.0"
- metro "0.76.8"
- metro-babel-transformer "0.76.8"
- metro-cache "0.76.8"
- metro-cache-key "0.76.8"
- metro-source-map "0.76.8"
- metro-transform-plugins "0.76.8"
+ metro "0.80.5"
+ metro-babel-transformer "0.80.5"
+ metro-cache "0.80.5"
+ metro-cache-key "0.80.5"
+ metro-minify-terser "0.80.5"
+ metro-source-map "0.80.5"
+ metro-transform-plugins "0.80.5"
nullthrows "^1.1.1"
-metro@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/metro/-/metro-0.76.8.tgz#ba526808b99977ca3f9ac5a7432fd02a340d13a6"
- integrity sha512-oQA3gLzrrYv3qKtuWArMgHPbHu8odZOD9AoavrqSFllkPgOtmkBvNNDLCELqv5SjBfqjISNffypg+5UGG3y0pg==
+metro@0.80.5, metro@^0.80.3:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/metro/-/metro-0.80.5.tgz#94cd36df2eab434f050314a6d31529e8dc1b26df"
+ integrity sha512-OE/CGbOgbi8BlTN1QqJgKOBaC27dS0JBQw473JcivrpgVnqIsluROA7AavEaTVUrB9wPUZvoNVDROn5uiM2jfw==
dependencies:
"@babel/code-frame" "^7.0.0"
"@babel/core" "^7.20.0"
@@ -7566,7 +7447,6 @@ metro@0.76.8:
"@babel/traverse" "^7.20.0"
"@babel/types" "^7.20.0"
accepts "^1.3.7"
- async "^3.2.2"
chalk "^4.0.0"
ci-info "^2.0.0"
connect "^3.6.5"
@@ -7574,28 +7454,24 @@ metro@0.76.8:
denodeify "^1.2.1"
error-stack-parser "^2.0.6"
graceful-fs "^4.2.4"
- hermes-parser "0.12.0"
+ hermes-parser "0.18.2"
image-size "^1.0.2"
invariant "^2.2.4"
- jest-worker "^27.2.0"
+ jest-worker "^29.6.3"
jsc-safe-url "^0.2.2"
lodash.throttle "^4.1.1"
- metro-babel-transformer "0.76.8"
- metro-cache "0.76.8"
- metro-cache-key "0.76.8"
- metro-config "0.76.8"
- metro-core "0.76.8"
- metro-file-map "0.76.8"
- metro-inspector-proxy "0.76.8"
- metro-minify-terser "0.76.8"
- metro-minify-uglify "0.76.8"
- metro-react-native-babel-preset "0.76.8"
- metro-resolver "0.76.8"
- metro-runtime "0.76.8"
- metro-source-map "0.76.8"
- metro-symbolicate "0.76.8"
- metro-transform-plugins "0.76.8"
- metro-transform-worker "0.76.8"
+ metro-babel-transformer "0.80.5"
+ metro-cache "0.80.5"
+ metro-cache-key "0.80.5"
+ metro-config "0.80.5"
+ metro-core "0.80.5"
+ metro-file-map "0.80.5"
+ metro-resolver "0.80.5"
+ metro-runtime "0.80.5"
+ metro-source-map "0.80.5"
+ metro-symbolicate "0.80.5"
+ metro-transform-plugins "0.80.5"
+ metro-transform-worker "0.80.5"
mime-types "^2.1.27"
node-fetch "^2.2.0"
nullthrows "^1.1.1"
@@ -7918,10 +7794,10 @@ nwsapi@^2.2.2:
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.7.tgz#738e0707d3128cb750dddcfe90e4610482df0f30"
integrity sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==
-ob1@0.76.8:
- version "0.76.8"
- resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.76.8.tgz#ac4c459465b1c0e2c29aaa527e09fc463d3ffec8"
- integrity sha512-dlBkJJV5M/msj9KYA9upc+nUWVwuOFFTbu28X6kZeGwcuW+JxaHSBZ70SYQnk5M+j5JbNLR6yKHmgW4M5E7X5g==
+ob1@0.80.5:
+ version "0.80.5"
+ resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.80.5.tgz#101f5257f7e6b75599dcd55c20bfcf2a4016c37c"
+ integrity sha512-zYDMnnNrFi/1Tqh0vo3PE4p97Tpl9/4MP2k2ECvkbLOZzQuAYZJLTUYVLZb7hJhbhjT+JJxAwBGS8iu5hCSd1w==
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
@@ -8473,7 +8349,7 @@ promise@^8.3.0:
dependencies:
asap "~2.0.6"
-prompts@^2.0.1, prompts@^2.2.1, prompts@^2.3.2, prompts@^2.4.0:
+prompts@^2.0.1, prompts@^2.2.1, prompts@^2.3.2, prompts@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==
@@ -8481,7 +8357,7 @@ prompts@^2.0.1, prompts@^2.2.1, prompts@^2.3.2, prompts@^2.4.0:
kleur "^3.0.3"
sisteransi "^1.0.5"
-prop-types@*, prop-types@^15.7.2, prop-types@^15.8.1:
+prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -8593,7 +8469,7 @@ rc@~1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
-react-devtools-core@^4.27.2:
+react-devtools-core@^4.27.7:
version "4.28.5"
resolved "https://registry.yarnpkg.com/react-devtools-core/-/react-devtools-core-4.28.5.tgz#c8442b91f068cdf0c899c543907f7f27d79c2508"
integrity sha512-cq/o30z9W2Wb4rzBefjv5fBalHU0rJGZCHAkf/RHSBWSSYwh8PlQTqqOJmgIIbBtpj27T6FIPXeomIjZtCNVqA==
@@ -8690,49 +8566,51 @@ react-native-web@~0.19.6:
postcss-value-parser "^4.2.0"
styleq "^0.1.3"
-react-native@0.72.6:
- version "0.72.6"
- resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.72.6.tgz#9f8d090694907e2f83af22e115cc0e4a3d5fa626"
- integrity sha512-RafPY2gM7mcrFySS8TL8x+TIO3q7oAlHpzEmC7Im6pmXni6n1AuufGaVh0Narbr1daxstw7yW7T9BKW5dpVc2A==
- dependencies:
- "@jest/create-cache-key-function" "^29.2.1"
- "@react-native-community/cli" "11.3.7"
- "@react-native-community/cli-platform-android" "11.3.7"
- "@react-native-community/cli-platform-ios" "11.3.7"
- "@react-native/assets-registry" "^0.72.0"
- "@react-native/codegen" "^0.72.7"
- "@react-native/gradle-plugin" "^0.72.11"
- "@react-native/js-polyfills" "^0.72.1"
- "@react-native/normalize-colors" "^0.72.0"
- "@react-native/virtualized-lists" "^0.72.8"
+react-native@0.73.3:
+ version "0.73.3"
+ resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.73.3.tgz#aae18b4c6da84294c1f8e1d6446b46c887bf087c"
+ integrity sha512-RSQDtT2DNUcmB4IgmW9NhRb5wqvXFl6DI2NEJmt0ps2OrVHpoA8Tkq+lkFOA/fvPscJKtFKEHFBDSR5UHR3PUw==
+ dependencies:
+ "@jest/create-cache-key-function" "^29.6.3"
+ "@react-native-community/cli" "12.3.2"
+ "@react-native-community/cli-platform-android" "12.3.2"
+ "@react-native-community/cli-platform-ios" "12.3.2"
+ "@react-native/assets-registry" "0.73.1"
+ "@react-native/codegen" "0.73.2"
+ "@react-native/community-cli-plugin" "0.73.14"
+ "@react-native/gradle-plugin" "0.73.4"
+ "@react-native/js-polyfills" "0.73.1"
+ "@react-native/normalize-colors" "0.73.2"
+ "@react-native/virtualized-lists" "0.73.4"
abort-controller "^3.0.0"
anser "^1.4.9"
- base64-js "^1.1.2"
- deprecated-react-native-prop-types "4.1.0"
+ ansi-regex "^5.0.0"
+ base64-js "^1.5.1"
+ chalk "^4.0.0"
+ deprecated-react-native-prop-types "^5.0.0"
event-target-shim "^5.0.1"
- flow-enums-runtime "^0.0.5"
+ flow-enums-runtime "^0.0.6"
invariant "^2.2.4"
- jest-environment-node "^29.2.1"
+ jest-environment-node "^29.6.3"
jsc-android "^250231.0.0"
memoize-one "^5.0.0"
- metro-runtime "0.76.8"
- metro-source-map "0.76.8"
+ metro-runtime "^0.80.3"
+ metro-source-map "^0.80.3"
mkdirp "^0.5.1"
nullthrows "^1.1.1"
pretty-format "^26.5.2"
promise "^8.3.0"
- react-devtools-core "^4.27.2"
- react-refresh "^0.4.0"
+ react-devtools-core "^4.27.7"
+ react-refresh "^0.14.0"
react-shallow-renderer "^16.15.0"
regenerator-runtime "^0.13.2"
scheduler "0.24.0-canary-efb381bbf-20230505"
stacktrace-parser "^0.1.10"
- use-sync-external-store "^1.0.0"
whatwg-fetch "^3.0.0"
ws "^6.2.2"
yargs "^17.6.2"
-react-refresh@0.14.0, react-refresh@^0.14.0, react-refresh@^0.4.0, react-refresh@~0.14.0:
+react-refresh@0.14.0, react-refresh@^0.14.0, react-refresh@~0.14.0:
version "0.14.0"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.0.tgz#4e02825378a5f227079554d4284889354e5f553e"
integrity sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==
@@ -10033,14 +9911,6 @@ ua-parser-js@^1.0.35:
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-1.0.37.tgz#b5dc7b163a5c1f0c510b08446aed4da92c46373f"
integrity sha512-bhTyI94tZofjo+Dn8SN6Zv8nBDvyXTymAdM3LDI/0IboIUwTu1rEhW7v2TfiVsoYWgkQ4kOVqnI8APUFbIQIFQ==
-uglify-es@^3.1.9:
- version "3.3.9"
- resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
- integrity sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==
- dependencies:
- commander "~2.13.0"
- source-map "~0.6.1"
-
unbox-primitive@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
@@ -10173,7 +10043,7 @@ use-latest-callback@^0.1.7:
resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.1.9.tgz#10191dc54257e65a8e52322127643a8940271e2a"
integrity sha512-CL/29uS74AwreI/f2oz2hLTW7ZqVeV5+gxFeGudzQrgkCytrHw33G4KbnQOrRlAEzzAFXi7dDLMC9zhWcVpzmw==
-use-sync-external-store@1.2.0, use-sync-external-store@^1.0.0, use-sync-external-store@^1.1.0, use-sync-external-store@^1.2.0:
+use-sync-external-store@1.2.0, use-sync-external-store@^1.1.0, use-sync-external-store@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
From 4cd61782519485794d36673e406707d9c4997ef7 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 23:56:37 -0500
Subject: [PATCH 20/33] Bump the npm_and_yarn group across 1 directories with 1
update (#166)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
frontend/sac-mobile/package.json | 2 +-
frontend/sac-mobile/yarn.lock | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/frontend/sac-mobile/package.json b/frontend/sac-mobile/package.json
index 759d7d9e7..195e27b29 100644
--- a/frontend/sac-mobile/package.json
+++ b/frontend/sac-mobile/package.json
@@ -38,7 +38,7 @@
"react-native-safe-area-context": "4.8.2",
"react-native-screens": "~3.29.0",
"react-native-web": "~0.19.6",
- "semver": "7.5.4",
+ "semver": "7.6.0",
"zod": "^3.22.4",
"zustand": "^4.4.7"
},
diff --git a/frontend/sac-mobile/yarn.lock b/frontend/sac-mobile/yarn.lock
index ced9f0687..e02ab6263 100644
--- a/frontend/sac-mobile/yarn.lock
+++ b/frontend/sac-mobile/yarn.lock
@@ -8988,10 +8988,10 @@ semver@7.5.3:
dependencies:
lru-cache "^6.0.0"
-semver@7.5.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4:
- version "7.5.4"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
- integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
+semver@7.6.0, semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4:
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.0.tgz#1a46a4db4bffcccd97b743b5005c8325f23d4e2d"
+ integrity sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==
dependencies:
lru-cache "^6.0.0"
From b9bab07a2600838c5ce0bc95308c6ec638e721c0 Mon Sep 17 00:00:00 2001
From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
Date: Mon, 5 Feb 2024 23:57:11 -0500
Subject: [PATCH 21/33] CI Updates (#162)
---
.github/workflows/backend.yml | 24 +++++++--------
.github/workflows/cli.yml | 24 +++++++--------
README.md | 9 +++++-
backend/.golangci.yml | 23 ++++++++++++++
backend/src/database/db.go | 1 -
backend/src/database/super.go | 2 +-
backend/src/main.go | 5 +++-
backend/src/middleware/auth.go | 2 +-
backend/src/server/server.go | 6 +++-
backend/src/services/auth.go | 2 +-
backend/src/transactions/club.go | 2 +-
backend/src/utilities/validator.go | 30 ++++++++++++++-----
backend/tests/api/club_test.go | 7 +++--
backend/tests/api/helpers/requests.go | 1 +
backend/tests/api/tag_test.go | 2 ++
backend/tests/api/user_tag_test.go | 3 ++
backend/tests/api/user_test.go | 1 +
cli/.golangci.yml | 17 +++++++++++
cli/commands/drop.go | 7 +++--
cli/commands/format.go | 15 ++++++++--
cli/commands/insert.go | 29 +++++++++---------
cli/commands/lint.go | 36 +++++++++++++++++-----
cli/commands/migrate.go | 5 +++-
cli/commands/reset.go | 29 +++++++++++++-----
cli/commands/swagger.go | 5 +++-
cli/commands/test.go | 43 ++++++++++++++++++++++-----
go.work.sum | 5 ++++
27 files changed, 247 insertions(+), 88 deletions(-)
create mode 100644 backend/.golangci.yml
create mode 100644 cli/.golangci.yml
diff --git a/.github/workflows/backend.yml b/.github/workflows/backend.yml
index 26801fd58..4310d47fe 100644
--- a/.github/workflows/backend.yml
+++ b/.github/workflows/backend.yml
@@ -46,21 +46,21 @@ jobs:
lint:
name: Lint
runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: read
+ checks: write
steps:
- - name: Checkout Repository
- uses: actions/checkout@v3
- - name: Set up Go
- uses: actions/setup-go@v3
+ - uses: actions/checkout@v3
+ - uses: actions/setup-go@v4
with:
go-version: "1.21"
- - name: Enforce linting
- run: |
- cd ./backend/ && lint_output=$(go vet ./...)
- if [[ -n "$lint_output" ]]; then
- echo "$lint_output"
- echo "::error::Linting issues found"
- exit 1
- fi
+ cache: false
+ - name: golangci-lint
+ uses: golangci/golangci-lint-action@v3
+ with:
+ version: latest
+ working-directory: ./backend/
test:
name: Test
runs-on: ubuntu-latest
diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml
index 8abe3bc6d..6d7da6357 100644
--- a/.github/workflows/cli.yml
+++ b/.github/workflows/cli.yml
@@ -46,18 +46,18 @@ jobs:
lint:
name: Lint
runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ pull-requests: read
+ checks: write
steps:
- - name: Checkout Repository
- uses: actions/checkout@v3
- - name: Set up Go
- uses: actions/setup-go@v3
+ - uses: actions/checkout@v3
+ - uses: actions/setup-go@v4
with:
go-version: "1.21"
- - name: Enforce linting
- run: |
- cd ./cli/ && lint_output=$(go vet ./...)
- if [[ -n "$lint_output" ]]; then
- echo "$lint_output"
- echo "::error::Linting issues found"
- exit 1
- fi
+ cache: false
+ - name: golangci-lint
+ uses: golangci/golangci-lint-action@v3
+ with:
+ version: latest
+ working-directory: ./cli/
diff --git a/README.md b/README.md
index af4932d48..fb5635004 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,10 @@
-
+
+
+
@@ -16,6 +19,10 @@
+
+
+
diff --git a/backend/.golangci.yml b/backend/.golangci.yml
new file mode 100644
index 000000000..0eab32c33
--- /dev/null
+++ b/backend/.golangci.yml
@@ -0,0 +1,23 @@
+linters:
+ enable:
+ - cyclop
+ - exportloopref
+ - gocritic
+ - gosec
+ - ineffassign
+ - misspell
+ - prealloc
+ - unconvert
+ - unparam
+ - goimports
+ - whitespace
+
+linters-settings:
+ whitespace:
+ multi-func: true
+
+issues:
+ exclude-rules:
+ - path: tests/api/helpers/
+ linters:
+ - cyclop
diff --git a/backend/src/database/db.go b/backend/src/database/db.go
index 35ad1d868..b45406eff 100644
--- a/backend/src/database/db.go
+++ b/backend/src/database/db.go
@@ -141,7 +141,6 @@ func createSuperUser(settings config.Settings, db *gorm.DB) error {
}
return tx.Commit().Error
-
}
return nil
}
diff --git a/backend/src/database/super.go b/backend/src/database/super.go
index 6ad3f930f..22700693e 100644
--- a/backend/src/database/super.go
+++ b/backend/src/database/super.go
@@ -35,7 +35,7 @@ func SuperClub() models.Club {
Description: "SAC",
NumMembers: 0,
IsRecruiting: true,
- RecruitmentCycle: models.RecruitmentCycle(models.Always),
+ RecruitmentCycle: models.Always,
RecruitmentType: models.Application,
ApplicationLink: "https://generatenu.com/apply",
Logo: "https://aws.amazon.com/s3",
diff --git a/backend/src/main.go b/backend/src/main.go
index 9ace36db9..2c2f73672 100644
--- a/backend/src/main.go
+++ b/backend/src/main.go
@@ -35,5 +35,8 @@ func main() {
app := server.Init(db, config)
- app.Listen(fmt.Sprintf("%s:%d", config.Application.Host, config.Application.Port))
+ err = app.Listen(fmt.Sprintf("%s:%d", config.Application.Host, config.Application.Port))
+ if err != nil {
+ panic(err)
+ }
}
diff --git a/backend/src/middleware/auth.go b/backend/src/middleware/auth.go
index 07fd76a61..3bba7c497 100644
--- a/backend/src/middleware/auth.go
+++ b/backend/src/middleware/auth.go
@@ -23,7 +23,7 @@ func SuperSkipper(h fiber.Handler) fiber.Handler {
return skip.New(h, func(c *fiber.Ctx) bool {
claims, err := types.From(c)
if err != nil {
- err.FiberError(c)
+ _ = err.FiberError(c)
return false
}
if claims == nil {
diff --git a/backend/src/server/server.go b/backend/src/server/server.go
index f65b70c23..5d249a0a3 100644
--- a/backend/src/server/server.go
+++ b/backend/src/server/server.go
@@ -26,7 +26,11 @@ import (
func Init(db *gorm.DB, settings config.Settings) *fiber.App {
app := newFiberApp()
- validate := utilities.RegisterCustomValidators()
+ validate, err := utilities.RegisterCustomValidators()
+ if err != nil {
+ panic(err)
+ }
+
middlewareService := middleware.NewMiddlewareService(db, validate, settings.Auth)
apiv1 := app.Group("/api/v1")
diff --git a/backend/src/services/auth.go b/backend/src/services/auth.go
index 4c87dd7e4..4e0d4de72 100644
--- a/backend/src/services/auth.go
+++ b/backend/src/services/auth.go
@@ -75,7 +75,7 @@ func (a *AuthService) GetRole(id string) (*models.UserRole, *errors.Error) {
return nil, &errors.UserNotFound
}
- role := models.UserRole(user.Role)
+ role := user.Role
return &role, nil
}
diff --git a/backend/src/transactions/club.go b/backend/src/transactions/club.go
index a7fe4495c..016e2d0ef 100644
--- a/backend/src/transactions/club.go
+++ b/backend/src/transactions/club.go
@@ -17,7 +17,7 @@ func GetAdminIDs(db *gorm.DB, clubID uuid.UUID) ([]uuid.UUID, *errors.Error) {
return nil, &errors.FailedtoGetAdminIDs
}
- var adminUUIDs []uuid.UUID
+ adminUUIDs := make([]uuid.UUID, 0)
for _, adminID := range adminIDs {
adminUUIDs = append(adminUUIDs, adminID.ClubID)
}
diff --git a/backend/src/utilities/validator.go b/backend/src/utilities/validator.go
index 78c2e53ef..701f3c595 100644
--- a/backend/src/utilities/validator.go
+++ b/backend/src/utilities/validator.go
@@ -13,18 +13,32 @@ import (
"github.com/go-playground/validator/v10"
)
-func RegisterCustomValidators() *validator.Validate {
+func RegisterCustomValidators() (*validator.Validate, error) {
validate := validator.New(validator.WithRequiredStructEnabled())
- validate.RegisterValidation("neu_email", validateEmail)
- validate.RegisterValidation("password", validatePassword)
- validate.RegisterValidation("mongo_url", validateMongoURL)
- validate.RegisterValidation("s3_url", validateS3URL)
- validate.RegisterValidation("contact_pointer", func(fl validator.FieldLevel) bool {
+ if err := validate.RegisterValidation("neu_email", validateEmail); err != nil {
+ return nil, err
+ }
+
+ if err := validate.RegisterValidation("password", validatePassword); err != nil {
+ return nil, err
+ }
+
+ if err := validate.RegisterValidation("mongo_url", validateMongoURL); err != nil {
+ return nil, err
+ }
+
+ if err := validate.RegisterValidation("s3_url", validateS3URL); err != nil {
+ return nil, err
+ }
+
+ if err := validate.RegisterValidation("contact_pointer", func(fl validator.FieldLevel) bool {
return validateContactPointer(validate, fl)
- })
+ }); err != nil {
+ return nil, err
+ }
- return validate
+ return validate, nil
}
func validateEmail(fl validator.FieldLevel) bool {
diff --git a/backend/tests/api/club_test.go b/backend/tests/api/club_test.go
index bdce0892e..4a632dd42 100644
--- a/backend/tests/api/club_test.go
+++ b/backend/tests/api/club_test.go
@@ -206,8 +206,8 @@ func TestGetClubsWorks(t *testing.T) {
assert.Equal("SAC", dbClub.Description)
assert.Equal(1, dbClub.NumMembers)
assert.Equal(true, dbClub.IsRecruiting)
- assert.Equal(models.RecruitmentCycle(models.Always), dbClub.RecruitmentCycle)
- assert.Equal(models.RecruitmentType(models.Application), dbClub.RecruitmentType)
+ assert.Equal(models.Always, dbClub.RecruitmentCycle)
+ assert.Equal(models.Application, dbClub.RecruitmentType)
assert.Equal("https://generatenu.com/apply", dbClub.ApplicationLink)
assert.Equal("https://aws.amazon.com/s3", dbClub.Logo)
},
@@ -303,7 +303,7 @@ func TestCreateClubFailsOnInvalidLogo(t *testing.T) {
[]interface{}{
"Not an URL",
"@#139081#$Ad_Axf",
- //"https://google.com", <-- TODO uncomment once we figure out s3 url validation
+ // "https://google.com", <-- TODO uncomment once we figure out s3 url validation
},
)
}
@@ -343,6 +343,7 @@ func TestUpdateClubFailsOnInvalidBody(t *testing.T) {
{"application_link": "Not an URL"},
{"logo": "@12394X_2"},
} {
+ invalidData := invalidData
appAssert.TestOnErrorAndDB(
h.TestRequest{
Method: fiber.MethodPatch,
diff --git a/backend/tests/api/helpers/requests.go b/backend/tests/api/helpers/requests.go
index 85bf4fd44..3e76dc5bb 100644
--- a/backend/tests/api/helpers/requests.go
+++ b/backend/tests/api/helpers/requests.go
@@ -24,6 +24,7 @@ type TestRequest struct {
TestUserIDReplaces *string
}
+//gocyclo:ignore
func (app TestApp) Send(request TestRequest) (*http.Response, error) {
address := fmt.Sprintf("%s%s", app.Address, request.Path)
diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go
index 531993fcd..f61cbe7d0 100644
--- a/backend/tests/api/tag_test.go
+++ b/backend/tests/api/tag_test.go
@@ -115,6 +115,7 @@ func TestCreateTagFailsBadRequest(t *testing.T) {
}
for _, badBody := range badBodys {
+ badBody := badBody
appAssert.TestOnErrorAndDB(
h.TestRequest{
Method: fiber.MethodPost,
@@ -146,6 +147,7 @@ func TestCreateTagFailsValidation(t *testing.T) {
}
for _, badBody := range badBodys {
+ badBody := badBody
appAssert.TestOnErrorAndDB(
h.TestRequest{
Method: fiber.MethodPost,
diff --git a/backend/tests/api/user_tag_test.go b/backend/tests/api/user_tag_test.go
index 013b5ddb9..9d8a2ecc4 100644
--- a/backend/tests/api/user_tag_test.go
+++ b/backend/tests/api/user_tag_test.go
@@ -66,6 +66,7 @@ func CreateSetOfTags(t *testing.T, appAssert *h.ExistingAppAssert) ([]uuid.UUID,
categoryIDs := []uuid.UUID{}
for _, category := range *categories {
+ category := category
appAssert.TestOnStatusAndDB(
h.TestRequest{
Method: fiber.MethodPost,
@@ -92,6 +93,7 @@ func CreateSetOfTags(t *testing.T, appAssert *h.ExistingAppAssert) ([]uuid.UUID,
tagIDs := []uuid.UUID{}
for _, tag := range *tags {
+ tag := tag
appAssert.TestOnStatusAndDB(
h.TestRequest{
Method: fiber.MethodPost,
@@ -212,6 +214,7 @@ func TestCreateUserTagsFailsOnInvalidKey(t *testing.T) {
}
for _, body := range invalidBody {
+ body := body
h.InitTest(t).TestOnError(
h.TestRequest{
Method: fiber.MethodPost,
diff --git a/backend/tests/api/user_test.go b/backend/tests/api/user_test.go
index 5ba723114..66bd629c4 100644
--- a/backend/tests/api/user_test.go
+++ b/backend/tests/api/user_test.go
@@ -216,6 +216,7 @@ func TestUpdateUserFailsOnInvalidBody(t *testing.T) {
{"year": 1963},
{"college": "UT-Austin"},
} {
+ invalidData := invalidData
h.InitTest(t).TestOnErrorAndDB(
h.TestRequest{
Method: fiber.MethodPatch,
diff --git a/cli/.golangci.yml b/cli/.golangci.yml
new file mode 100644
index 000000000..31fd57a91
--- /dev/null
+++ b/cli/.golangci.yml
@@ -0,0 +1,17 @@
+linters:
+ enable:
+ - cyclop
+ - exportloopref
+ - gocritic
+ - gosec
+ - ineffassign
+ - misspell
+ - prealloc
+ - unconvert
+ - unparam
+ - goimports
+ - whitespace
+
+linters-settings:
+ whitespace:
+ multi-func: true
diff --git a/cli/commands/drop.go b/cli/commands/drop.go
index 5f75e1726..3a34f1ae1 100644
--- a/cli/commands/drop.go
+++ b/cli/commands/drop.go
@@ -70,8 +70,7 @@ func DropData() error {
return fmt.Errorf("error scanning table name: %w", err)
}
- deleteStmt := fmt.Sprintf("DELETE FROM \"%s\"", tablename)
- _, err := db.Exec(deleteStmt)
+ _, err := db.Exec("DELETE FROM $1", tablename)
if err != nil {
return fmt.Errorf("error deleting rows from table %s: %w", tablename, err)
}
@@ -82,7 +81,9 @@ func DropData() error {
return fmt.Errorf("error in rows handling: %w", err)
}
- Migrate()
+ if Migrate() != nil {
+ return fmt.Errorf("error migrating database: %w", err)
+ }
fmt.Println("All rows removed successfully.")
return nil
diff --git a/cli/commands/format.go b/cli/commands/format.go
index 9b60c9fc2..cfaa9ddea 100644
--- a/cli/commands/format.go
+++ b/cli/commands/format.go
@@ -39,7 +39,10 @@ func FormatCommand() *cli.Command {
runFrontend := folder != ""
runBackend := c.Bool("backend")
- Format(folder, runFrontend, runBackend)
+ err := Format(folder, runFrontend, runBackend)
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
return nil
},
@@ -56,7 +59,10 @@ func Format(folder string, runFrontend bool, runBackend bool) error {
wg.Add(1)
go func() {
defer wg.Done()
- BackendFormat()
+ err := BackendFormat()
+ if err != nil {
+ fmt.Println(err)
+ }
}()
}
@@ -65,7 +71,10 @@ func Format(folder string, runFrontend bool, runBackend bool) error {
wg.Add(1)
go func() {
defer wg.Done()
- FrontendFormat(folder)
+ err := FrontendFormat(folder)
+ if err != nil {
+ fmt.Println(err)
+ }
}()
}
diff --git a/cli/commands/insert.go b/cli/commands/insert.go
index 376468ad3..9b8a55754 100644
--- a/cli/commands/insert.go
+++ b/cli/commands/insert.go
@@ -1,15 +1,12 @@
package commands
import (
- "bytes"
"database/sql"
- "errors"
"fmt"
+ "os"
"os/exec"
- "strconv"
- "strings"
- _ "github.com/lib/pq"
+ "github.com/lib/pq"
"github.com/urfave/cli/v2"
)
@@ -65,18 +62,20 @@ func InsertDB() error {
fmt.Println("Database exists with tables.")
}
- insertCmd := exec.Command("psql", "-h", CONFIG.Database.Host, "-p", strconv.Itoa(int(CONFIG.Database.Port)), "-U", CONFIG.Database.Username, "-d", CONFIG.Database.DatabaseName, "-a", "-f", MIGRATION_FILE)
-
- var output bytes.Buffer
- insertCmd.Stdout = &output
- insertCmd.Stderr = &output
-
- if err := insertCmd.Run(); err != nil {
- return fmt.Errorf("error inserting data: %w", err)
+ migrationSQL, err := os.ReadFile(MIGRATION_FILE)
+ if err != nil {
+ return fmt.Errorf("error reading migration file: %w", err)
}
- if strings.Contains(output.String(), "ROLLBACK") {
- return errors.New("insertion failed, rolling back")
+ _, err = db.Exec(string(migrationSQL))
+ if err != nil {
+ if pqErr, ok := err.(*pq.Error); ok {
+ fmt.Println("PostgreSQL Error:")
+ fmt.Println("Code:", pqErr.Code)
+ fmt.Println("Message:", pqErr.Message)
+ } else {
+ return fmt.Errorf("error executing migration: %w", err)
+ }
}
fmt.Println("Data inserted successfully.")
diff --git a/cli/commands/lint.go b/cli/commands/lint.go
index 3351ab554..e6836ce55 100644
--- a/cli/commands/lint.go
+++ b/cli/commands/lint.go
@@ -39,7 +39,10 @@ func LintCommand() *cli.Command {
runFrontend := folder != ""
runBackend := c.Bool("backend")
- Lint(folder, runFrontend, runBackend)
+ err := Lint(folder, runFrontend, runBackend)
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
return nil
},
@@ -50,26 +53,45 @@ func LintCommand() *cli.Command {
func Lint(folder string, runFrontend bool, runBackend bool) error {
var wg sync.WaitGroup
+ errChan := make(chan error, 1)
+ var errOccurred bool // Flag to indicate whether an error has occurred
// Start the backend if specified
- if runBackend {
+ if runBackend && !errOccurred {
wg.Add(1)
go func() {
defer wg.Done()
- BackendLint()
+ err := BackendLint()
+ if err != nil {
+ errChan <- err
+ errOccurred = true
+ }
}()
}
// Start the frontend if specified
- if runFrontend {
+ if runFrontend && !errOccurred {
wg.Add(1)
go func() {
defer wg.Done()
- FrontendLint(folder)
+ err := FrontendLint(folder)
+ if err != nil {
+ errChan <- err
+ errOccurred = true
+ }
}()
}
- wg.Wait()
+ go func() {
+ wg.Wait()
+ close(errChan)
+ }()
+
+ for err := range errChan {
+ if err != nil {
+ return err
+ }
+ }
return nil
}
@@ -77,7 +99,7 @@ func Lint(folder string, runFrontend bool, runBackend bool) error {
func BackendLint() error {
fmt.Println("Linting backend")
- cmd := exec.Command("go", "vet", "./...")
+ cmd := exec.Command("golangci-lint", "run")
cmd.Dir = BACKEND_DIR
err := cmd.Run()
diff --git a/cli/commands/migrate.go b/cli/commands/migrate.go
index a2189e805..7aa7d8aa4 100644
--- a/cli/commands/migrate.go
+++ b/cli/commands/migrate.go
@@ -17,7 +17,10 @@ func MigrateCommand() *cli.Command {
return cli.Exit("Invalid arguments", 1)
}
- Migrate()
+ err := Migrate()
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
return nil
},
}
diff --git a/cli/commands/reset.go b/cli/commands/reset.go
index 07929c1fb..4e9cf4324 100644
--- a/cli/commands/reset.go
+++ b/cli/commands/reset.go
@@ -46,17 +46,23 @@ func ResetCommand() *cli.Command {
func ResetData() error {
fmt.Println("Clearing database")
- DropData()
+ err := DropData()
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
cmd := exec.Command("sleep", "1")
cmd.Dir = BACKEND_DIR
- err := cmd.Run()
+ err = cmd.Run()
if err != nil {
return cli.Exit("Error running sleep", 1)
}
- Migrate()
+ err = Migrate()
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
cmd = exec.Command("sleep", "1")
cmd.Dir = BACKEND_DIR
@@ -66,7 +72,10 @@ func ResetData() error {
return cli.Exit("Error running sleep", 1)
}
- InsertDB()
+ err = InsertDB()
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
fmt.Println("Data reset successfully")
@@ -76,17 +85,23 @@ func ResetData() error {
func ResetMigration() error {
fmt.Println("Resetting migration")
- DropDB()
+ err := DropDB()
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
cmd := exec.Command("sleep", "1")
cmd.Dir = BACKEND_DIR
- err := cmd.Run()
+ err = cmd.Run()
if err != nil {
return cli.Exit("Error running sleep", 1)
}
- Migrate()
+ err = Migrate()
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
fmt.Println("Migration reset successfully")
diff --git a/cli/commands/swagger.go b/cli/commands/swagger.go
index bac4191d3..3881d93cf 100644
--- a/cli/commands/swagger.go
+++ b/cli/commands/swagger.go
@@ -17,7 +17,10 @@ func SwaggerCommand() *cli.Command {
return cli.Exit("Invalid arguments", 1)
}
- Swagger()
+ err := Swagger()
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
return nil
},
}
diff --git a/cli/commands/test.go b/cli/commands/test.go
index 0fa9d6a27..be12543c4 100644
--- a/cli/commands/test.go
+++ b/cli/commands/test.go
@@ -38,7 +38,10 @@ func TestCommand() *cli.Command {
folder := c.String("frontend")
runFrontend := folder != ""
runBackend := c.Bool("backend")
- Test(folder, runFrontend, runBackend)
+ err := Test(folder, runFrontend, runBackend)
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
return nil
},
}
@@ -47,26 +50,46 @@ func TestCommand() *cli.Command {
func Test(folder string, runFrontend bool, runBackend bool) error {
var wg sync.WaitGroup
+ errChan := make(chan error, 1)
+ var errOccurred bool // Flag to indicate whether an error has occurred
// Start the backend if specified
- if runBackend {
+ if runBackend && !errOccurred {
wg.Add(1)
go func() {
defer wg.Done()
- BackendTest()
+ err := BackendTest()
+ if err != nil {
+ errChan <- err
+ errOccurred = true
+ }
}()
}
// Start the frontend if specified
- if runFrontend {
+ if runFrontend && !errOccurred {
wg.Add(1)
go func() {
defer wg.Done()
- FrontendTest(folder)
+ err := FrontendTest(folder)
+ if err != nil {
+ errChan <- err
+ errOccurred = true
+ }
}()
}
- wg.Wait()
+ go func() {
+ wg.Wait()
+ close(errChan)
+ }()
+
+ for err := range errChan {
+ if err != nil {
+ return err
+ }
+ }
+
return nil
}
@@ -74,8 +97,6 @@ func BackendTest() error {
cmd := exec.Command("go", "test", "./...")
cmd.Dir = fmt.Sprintf("%s/..", BACKEND_DIR)
- defer CleanTestDBs()
-
out, err := cmd.CombinedOutput()
if err != nil {
fmt.Println(string(out))
@@ -83,6 +104,12 @@ func BackendTest() error {
}
fmt.Println(string(out))
+
+ err = CleanTestDBs()
+ if err != nil {
+ return cli.Exit(err.Error(), 1)
+ }
+
return nil
}
diff --git a/go.work.sum b/go.work.sum
index 9dfa3c6d2..a9dfabbc1 100644
--- a/go.work.sum
+++ b/go.work.sum
@@ -10,7 +10,12 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
From f7d6d0821c4eface683c3ac69428e544ef35e6b3 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 23:57:32 -0500
Subject: [PATCH 22/33] Bump expo-web-browser from 12.3.2 to 12.8.2 in
/frontend/sac-mobile (#153)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
---
frontend/sac-mobile/package.json | 2 +-
frontend/sac-mobile/yarn.lock | 10 +---------
2 files changed, 2 insertions(+), 10 deletions(-)
diff --git a/frontend/sac-mobile/package.json b/frontend/sac-mobile/package.json
index 195e27b29..6e1ec9cc3 100644
--- a/frontend/sac-mobile/package.json
+++ b/frontend/sac-mobile/package.json
@@ -29,7 +29,7 @@
"expo-splash-screen": "~0.20.5",
"expo-status-bar": "~1.11.1",
"expo-system-ui": "~2.9.3",
- "expo-web-browser": "~12.3.2",
+ "expo-web-browser": "~12.8.2",
"nativewind": "^2.0.11",
"react": "18.2.0",
"react-dom": "18.2.0",
diff --git a/frontend/sac-mobile/yarn.lock b/frontend/sac-mobile/yarn.lock
index e02ab6263..4819c421c 100644
--- a/frontend/sac-mobile/yarn.lock
+++ b/frontend/sac-mobile/yarn.lock
@@ -5190,15 +5190,7 @@ expo-system-ui@~2.9.3:
"@react-native/normalize-color" "^2.0.0"
debug "^4.3.2"
-expo-web-browser@~12.3.2:
- version "12.3.2"
- resolved "https://registry.yarnpkg.com/expo-web-browser/-/expo-web-browser-12.3.2.tgz#45ac727a5d8462d7faa403ea2fa1db160ed8e4b5"
- integrity sha512-ohBf+vnRnGzlTleY8EQ2XQU0vRdRwqMJtKkzM9MZRPDOK5QyJYPJjpk6ixGhxdeoUG2Ogj0InvhhgX9NUn4jkg==
- dependencies:
- compare-urls "^2.0.0"
- url "^0.11.0"
-
-expo-web-browser@~12.8.0:
+expo-web-browser@~12.8.0, expo-web-browser@~12.8.2:
version "12.8.2"
resolved "https://registry.yarnpkg.com/expo-web-browser/-/expo-web-browser-12.8.2.tgz#f34fb85c80031e0dddd4f9b9efd03cb60333b089"
integrity sha512-Mw8WoFMSADecNjtC4PZVsVj1/lYdxIAH1jOVV+F8v8SEWYxORWofoShfXg7oUxRLu0iUG8JETfO5y4m8+fOgdg==
From c27bbb39aaa959afcbb5af401529dada3215c1cb Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 23:57:53 -0500
Subject: [PATCH 23/33] Bump expo-splash-screen from 0.20.5 to 0.26.4 in
/frontend/sac-mobile (#154)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
frontend/sac-mobile/package.json | 2 +-
frontend/sac-mobile/yarn.lock | 9 +--------
2 files changed, 2 insertions(+), 9 deletions(-)
diff --git a/frontend/sac-mobile/package.json b/frontend/sac-mobile/package.json
index 6e1ec9cc3..77cfcecc7 100644
--- a/frontend/sac-mobile/package.json
+++ b/frontend/sac-mobile/package.json
@@ -26,7 +26,7 @@
"expo-font": "~11.10.2",
"expo-linking": "~6.2.2",
"expo-router": "^3.4.6",
- "expo-splash-screen": "~0.20.5",
+ "expo-splash-screen": "~0.26.4",
"expo-status-bar": "~1.11.1",
"expo-system-ui": "~2.9.3",
"expo-web-browser": "~12.8.2",
diff --git a/frontend/sac-mobile/yarn.lock b/frontend/sac-mobile/yarn.lock
index 4819c421c..63eead6a2 100644
--- a/frontend/sac-mobile/yarn.lock
+++ b/frontend/sac-mobile/yarn.lock
@@ -5163,20 +5163,13 @@ expo-router@^3.4.6:
react-helmet-async "^1.3.0"
schema-utils "^4.0.1"
-expo-splash-screen@0.26.4:
+expo-splash-screen@0.26.4, expo-splash-screen@~0.26.4:
version "0.26.4"
resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.26.4.tgz#bc1fb226c6eae03ee351a3ebe5521a37f868cbc7"
integrity sha512-2DwofTQ0FFQCsvDysm/msENsbyNsJiAJwK3qK/oXeizECAPqD7bK19J4z9kuEbr7ORPX9MLnTQYKl6kmX3keUg==
dependencies:
"@expo/prebuild-config" "6.7.4"
-expo-splash-screen@~0.20.5:
- version "0.20.5"
- resolved "https://registry.yarnpkg.com/expo-splash-screen/-/expo-splash-screen-0.20.5.tgz#ebeba3e3977606830f74f506ab2cc25042bb7efd"
- integrity sha512-nTALYdjHpeEA30rdOWSguxn72ctv8WM8ptuUgpfRgsWyn4i6rwYds/rBXisX69XO5fg+XjHAQqijGx/b28+3tg==
- dependencies:
- "@expo/prebuild-config" "6.2.6"
-
expo-status-bar@~1.11.1:
version "1.11.1"
resolved "https://registry.yarnpkg.com/expo-status-bar/-/expo-status-bar-1.11.1.tgz#a11318741d361048c11db2b16c4364a79a74af30"
From ddb0145c464baf949b7d449660ef4a79113ea424 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 5 Feb 2024 23:58:27 -0500
Subject: [PATCH 24/33] Bump zustand from 4.4.7 to 4.5.0 in
/frontend/sac-mobile (#155)
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
---
frontend/sac-mobile/package.json | 2 +-
frontend/sac-mobile/yarn.lock | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/frontend/sac-mobile/package.json b/frontend/sac-mobile/package.json
index 77cfcecc7..4ec914d5b 100644
--- a/frontend/sac-mobile/package.json
+++ b/frontend/sac-mobile/package.json
@@ -40,7 +40,7 @@
"react-native-web": "~0.19.6",
"semver": "7.6.0",
"zod": "^3.22.4",
- "zustand": "^4.4.7"
+ "zustand": "^4.5.0"
},
"devDependencies": {
"@babel/core": "^7.23.9",
diff --git a/frontend/sac-mobile/yarn.lock b/frontend/sac-mobile/yarn.lock
index 63eead6a2..ef959b6a9 100644
--- a/frontend/sac-mobile/yarn.lock
+++ b/frontend/sac-mobile/yarn.lock
@@ -10467,9 +10467,9 @@ zod@^3.22.4:
resolved "https://registry.yarnpkg.com/zod/-/zod-3.22.4.tgz#f31c3a9386f61b1f228af56faa9255e845cf3fff"
integrity sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==
-zustand@^4.4.7:
- version "4.4.7"
- resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.4.7.tgz#355406be6b11ab335f59a66d2cf9815e8f24038c"
- integrity sha512-QFJWJMdlETcI69paJwhSMJz7PPWjVP8Sjhclxmxmxv/RYI7ZOvR5BHX+ktH0we9gTWQMxcne8q1OY8xxz604gw==
+zustand@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.0.tgz#141354af56f91de378aa6c4b930032ab338f3ef0"
+ integrity sha512-zlVFqS5TQ21nwijjhJlx4f9iGrXSL0o/+Dpy4txAP22miJ8Ti6c1Ol1RLNN98BMib83lmDH/2KmLwaNXpjrO1A==
dependencies:
use-sync-external-store "1.2.0"
From 1405dc10b37cb0ef8a9462ace7600c0a7cc288b1 Mon Sep 17 00:00:00 2001
From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
Date: Tue, 6 Feb 2024 00:04:04 -0500
Subject: [PATCH 25/33] Leverage Mattress | Refactor Config (#164)
---
backend/go.mod | 8 +-
backend/go.sum | 7 ++
backend/src/auth/tokens.go | 49 ++++-----
backend/src/config/application.go | 7 ++
backend/src/config/auth.go | 40 ++++++++
backend/src/config/config.go | 159 ++++++------------------------
backend/src/config/database.go | 58 +++++++++++
backend/src/config/local.go | 30 ++++++
backend/src/config/production.go | 111 +++++++++++++++++++++
backend/src/config/super_user.go | 26 +++++
backend/src/controllers/auth.go | 4 +-
backend/src/database/super.go | 2 +-
backend/src/main.go | 4 +-
backend/src/middleware/auth.go | 4 +-
backend/src/middleware/club.go | 2 +-
backend/src/middleware/user.go | 2 +-
backend/tests/api/helpers/app.go | 6 +-
backend/tests/api/helpers/auth.go | 4 +-
backend/tests/auth_test.go | 84 +++++++++++-----
config/local.yml | 4 +-
go.work | 2 +-
go.work.sum | 3 +
22 files changed, 420 insertions(+), 196 deletions(-)
create mode 100644 backend/src/config/application.go
create mode 100644 backend/src/config/auth.go
create mode 100644 backend/src/config/database.go
create mode 100644 backend/src/config/local.go
create mode 100644 backend/src/config/production.go
create mode 100644 backend/src/config/super_user.go
diff --git a/backend/go.mod b/backend/go.mod
index b1fb7d436..6b0923594 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -1,6 +1,6 @@
module github.com/GenerateNU/sac/backend
-go 1.21.1
+go 1.21.6
require (
github.com/go-playground/validator/v10 v10.17.0
@@ -13,6 +13,11 @@ require (
gorm.io/gorm v1.25.6
)
+require (
+ github.com/awnumar/memcall v0.2.0 // indirect
+ github.com/awnumar/memguard v0.22.4 // indirect
+)
+
require (
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
@@ -21,6 +26,7 @@ require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
+ github.com/garrettladley/mattress v0.2.0
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
diff --git a/backend/go.sum b/backend/go.sum
index 6441c0fc3..496556c70 100644
--- a/backend/go.sum
+++ b/backend/go.sum
@@ -6,6 +6,10 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
+github.com/awnumar/memcall v0.2.0 h1:sRaogqExTOOkkNwO9pzJsL8jrOV29UuUW7teRMfbqtI=
+github.com/awnumar/memcall v0.2.0/go.mod h1:S911igBPR9CThzd/hYQQmTc9SWNu3ZHIlCGaWsWsoJo=
+github.com/awnumar/memguard v0.22.4 h1:1PLgKcgGPeExPHL8dCOWGVjIbQUBgJv9OL0F/yE1PqQ=
+github.com/awnumar/memguard v0.22.4/go.mod h1:+APmZGThMBWjnMlKiSM1X7MVpbIVewen2MTkqWkA/zE=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -17,6 +21,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
+github.com/garrettladley/mattress v0.2.0 h1:+XUdsv9NO2s4JL+8exvAFziw0b1kv/0WlQo2Dlxat+w=
+github.com/garrettladley/mattress v0.2.0/go.mod h1:OWKIRc9wC3gtD3Ng/nUuNEiR1TJvRYLmn/KZYw9nl5Q=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -153,6 +159,7 @@ golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
diff --git a/backend/src/auth/tokens.go b/backend/src/auth/tokens.go
index c065f7340..ca902803f 100644
--- a/backend/src/auth/tokens.go
+++ b/backend/src/auth/tokens.go
@@ -7,17 +7,18 @@ import (
"github.com/GenerateNU/sac/backend/src/errors"
"github.com/GenerateNU/sac/backend/src/types"
+ m "github.com/garrettladley/mattress"
"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt"
)
func CreateTokenPair(id string, role string, authSettings config.AuthSettings) (*string, *string, *errors.Error) {
- accessToken, catErr := CreateAccessToken(id, role, authSettings.AccessTokenExpiry, authSettings.AccessToken)
+ accessToken, catErr := CreateAccessToken(id, role, authSettings.AccessTokenExpiry, authSettings.AccessKey)
if catErr != nil {
return nil, nil, catErr
}
- refreshToken, crtErr := CreateRefreshToken(id, authSettings.RefreshTokenExpiry, authSettings.RefreshToken)
+ refreshToken, crtErr := CreateRefreshToken(id, authSettings.RefreshTokenExpiry, authSettings.RefreshKey)
if crtErr != nil {
return nil, nil, crtErr
}
@@ -26,7 +27,7 @@ func CreateTokenPair(id string, role string, authSettings config.AuthSettings) (
}
// CreateAccessToken creates a new access token for the user
-func CreateAccessToken(id string, role string, accessExpiresAfter uint, accessTokenSecret string) (*string, *errors.Error) {
+func CreateAccessToken(id string, role string, accessExpiresAfter uint, accessToken *m.Secret[string]) (*string, *errors.Error) {
if id == "" || role == "" {
return nil, &errors.FailedToCreateAccessToken
}
@@ -40,16 +41,16 @@ func CreateAccessToken(id string, role string, accessExpiresAfter uint, accessTo
Role: role,
})
- accessToken, err := SignToken(accessTokenClaims, accessTokenSecret)
+ returnedAccessToken, err := SignToken(accessTokenClaims, accessToken)
if err != nil {
return nil, err
}
- return accessToken, nil
+ return returnedAccessToken, nil
}
// CreateRefreshToken creates a new refresh token for the user
-func CreateRefreshToken(id string, refreshExpiresAfter uint, refreshTokenSecret string) (*string, *errors.Error) {
+func CreateRefreshToken(id string, refreshExpiresAfter uint, refreshKey *m.Secret[string]) (*string, *errors.Error) {
if id == "" {
return nil, &errors.FailedToCreateRefreshToken
}
@@ -60,20 +61,20 @@ func CreateRefreshToken(id string, refreshExpiresAfter uint, refreshTokenSecret
ExpiresAt: time.Now().Add(time.Hour * 24 * time.Duration(refreshExpiresAfter)).Unix(),
})
- refreshToken, err := SignToken(refreshTokenClaims, refreshTokenSecret)
+ returnedRefreshToken, err := SignToken(refreshTokenClaims, refreshKey)
if err != nil {
return nil, err
}
- return refreshToken, nil
+ return returnedRefreshToken, nil
}
-func SignToken(token *jwt.Token, secret string) (*string, *errors.Error) {
- if token == nil || secret == "" {
+func SignToken(token *jwt.Token, key *m.Secret[string]) (*string, *errors.Error) {
+ if token == nil || key.Expose() == "" {
return nil, &errors.FailedToSignToken
}
- tokenString, err := token.SignedString([]byte(secret))
+ tokenString, err := token.SignedString([]byte(key.Expose()))
if err != nil {
return nil, &errors.FailedToSignToken
}
@@ -101,9 +102,9 @@ func ExpireCookie(name string) *fiber.Cookie {
}
// RefreshAccessToken refreshes the access token
-func RefreshAccessToken(refreshCookie string, role string, refreshTokenSecret string, accessExpiresAfter uint, accessTokenSecret string) (*string, *errors.Error) {
+func RefreshAccessToken(refreshCookie string, role string, refreshKey *m.Secret[string], accessExpiresAfter uint, accessKey *m.Secret[string]) (*string, *errors.Error) {
// Parse the refresh token
- refreshToken, err := ParseRefreshToken(refreshCookie, refreshTokenSecret)
+ refreshToken, err := ParseRefreshToken(refreshCookie, refreshKey)
if err != nil {
return nil, &errors.FailedToParseRefreshToken
}
@@ -115,7 +116,7 @@ func RefreshAccessToken(refreshCookie string, role string, refreshTokenSecret st
}
// Create a new access token
- accessToken, catErr := CreateAccessToken(claims.Issuer, role, accessExpiresAfter, accessTokenSecret)
+ accessToken, catErr := CreateAccessToken(claims.Issuer, role, accessExpiresAfter, accessKey)
if catErr != nil {
return nil, &errors.FailedToCreateAccessToken
}
@@ -124,22 +125,22 @@ func RefreshAccessToken(refreshCookie string, role string, refreshTokenSecret st
}
// ParseAccessToken parses the access token
-func ParseAccessToken(cookie string, accessTokenSecret string) (*jwt.Token, error) {
+func ParseAccessToken(cookie string, accessKey *m.Secret[string]) (*jwt.Token, error) {
return jwt.ParseWithClaims(cookie, &types.CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
- return []byte(accessTokenSecret), nil
+ return []byte(accessKey.Expose()), nil
})
}
// ParseRefreshToken parses the refresh token
-func ParseRefreshToken(cookie string, refreshTokenSecret string) (*jwt.Token, error) {
+func ParseRefreshToken(cookie string, refreshKey *m.Secret[string]) (*jwt.Token, error) {
return jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) {
- return []byte(refreshTokenSecret), nil
+ return []byte(refreshKey.Expose()), nil
})
}
// GetRoleFromToken gets the role from the custom claims
-func GetRoleFromToken(tokenString string, accessTokenSecret string) (*string, error) {
- token, err := ParseAccessToken(tokenString, accessTokenSecret)
+func GetRoleFromToken(tokenString string, accessKey *m.Secret[string]) (*string, error) {
+ token, err := ParseAccessToken(tokenString, accessKey)
if err != nil {
return nil, err
}
@@ -153,8 +154,8 @@ func GetRoleFromToken(tokenString string, accessTokenSecret string) (*string, er
}
// ExtractClaims extracts the claims from the token
-func ExtractAccessClaims(tokenString string, accessTokenSecret string) (*types.CustomClaims, *errors.Error) {
- token, err := ParseAccessToken(tokenString, accessTokenSecret)
+func ExtractAccessClaims(tokenString string, accessKey *m.Secret[string]) (*types.CustomClaims, *errors.Error) {
+ token, err := ParseAccessToken(tokenString, accessKey)
if err != nil {
return nil, &errors.FailedToParseAccessToken
}
@@ -168,8 +169,8 @@ func ExtractAccessClaims(tokenString string, accessTokenSecret string) (*types.C
}
// ExtractClaims extracts the claims from the token
-func ExtractRefreshClaims(tokenString string, refreshTokenSecret string) (*jwt.StandardClaims, *errors.Error) {
- token, err := ParseRefreshToken(tokenString, refreshTokenSecret)
+func ExtractRefreshClaims(tokenString string, refreshKey *m.Secret[string]) (*jwt.StandardClaims, *errors.Error) {
+ token, err := ParseRefreshToken(tokenString, refreshKey)
if err != nil {
return nil, &errors.FailedToParseRefreshToken
}
diff --git a/backend/src/config/application.go b/backend/src/config/application.go
new file mode 100644
index 000000000..dd2591408
--- /dev/null
+++ b/backend/src/config/application.go
@@ -0,0 +1,7 @@
+package config
+
+type ApplicationSettings struct {
+ Port uint16 `yaml:"port"`
+ Host string `yaml:"host"`
+ BaseUrl string `yaml:"baseurl"`
+}
diff --git a/backend/src/config/auth.go b/backend/src/config/auth.go
new file mode 100644
index 000000000..babc0cc2e
--- /dev/null
+++ b/backend/src/config/auth.go
@@ -0,0 +1,40 @@
+package config
+
+import (
+ "errors"
+
+ m "github.com/garrettladley/mattress"
+)
+
+type AuthSettings struct {
+ AccessKey *m.Secret[string]
+ RefreshKey *m.Secret[string]
+ AccessTokenExpiry uint
+ RefreshTokenExpiry uint
+}
+
+type intermediateAuthSettings struct {
+ AccessKey string `yaml:"accesskey"`
+ RefreshKey string `yaml:"refreshkey"`
+ AccessTokenExpiry uint `yaml:"accesstokenexpiry"`
+ RefreshTokenExpiry uint `yaml:"refreshtokenexpiry"`
+}
+
+func (int *intermediateAuthSettings) into() (*AuthSettings, error) {
+ accessToken, err := m.NewSecret(int.AccessKey)
+ if err != nil {
+ return nil, errors.New("failed to create secret from access key")
+ }
+
+ refreshToken, err := m.NewSecret(int.RefreshKey)
+ if err != nil {
+ return nil, errors.New("failed to create secret from refresh key")
+ }
+
+ return &AuthSettings{
+ AccessKey: accessToken,
+ RefreshKey: refreshToken,
+ AccessTokenExpiry: int.AccessTokenExpiry,
+ RefreshTokenExpiry: int.RefreshTokenExpiry,
+ }, nil
+}
diff --git a/backend/src/config/config.go b/backend/src/config/config.go
index fdb435248..81bc6e7a9 100644
--- a/backend/src/config/config.go
+++ b/backend/src/config/config.go
@@ -1,74 +1,47 @@
package config
import (
- "fmt"
"os"
- "strconv"
"github.com/spf13/viper"
)
type Settings struct {
- Application ApplicationSettings `yaml:"application"`
- Database DatabaseSettings `yaml:"database"`
- SuperUser SuperUserSettings `yaml:"superuser"`
- Auth AuthSettings `yaml:"authsecret"`
+ Application ApplicationSettings
+ Database DatabaseSettings
+ SuperUser SuperUserSettings
+ Auth AuthSettings
}
-type ProductionSettings struct {
- Database ProductionDatabaseSettings `yaml:"database"`
- Application ProductionApplicationSettings `yaml:"application"`
+type intermediateSettings struct {
+ Application ApplicationSettings `yaml:"application"`
+ Database intermediateDatabaseSettings `yaml:"database"`
+ SuperUser intermediateSuperUserSettings `yaml:"superuser"`
+ Auth intermediateAuthSettings `yaml:"authsecret"`
}
-type ApplicationSettings struct {
- Port uint16 `yaml:"port"`
- Host string `yaml:"host"`
- BaseUrl string `yaml:"baseurl"`
-}
-
-type ProductionApplicationSettings struct {
- Port uint16 `yaml:"port"`
- Host string `yaml:"host"`
-}
-
-type DatabaseSettings struct {
- Username string `yaml:"username"`
- Password string `yaml:"password"`
- Port uint `yaml:"port"`
- Host string `yaml:"host"`
- DatabaseName string `yaml:"databasename"`
- RequireSSL bool `yaml:"requiressl"`
-}
-
-type ProductionDatabaseSettings struct {
- RequireSSL bool `yaml:"requiressl"`
-}
-
-func (s *DatabaseSettings) WithoutDb() string {
- var sslMode string
- if s.RequireSSL {
- sslMode = "require"
- } else {
- sslMode = "disable"
+func (int *intermediateSettings) into() (*Settings, error) {
+ databaseSettings, err := int.Database.into()
+ if err != nil {
+ return nil, err
}
- return fmt.Sprintf("host=%s port=%d user=%s password=%s sslmode=%s",
- s.Host, s.Port, s.Username, s.Password, sslMode)
-}
-
-func (s *DatabaseSettings) WithDb() string {
- return fmt.Sprintf("%s dbname=%s", s.WithoutDb(), s.DatabaseName)
-}
+ superUserSettings, err := int.SuperUser.into()
+ if err != nil {
+ return nil, err
+ }
-type SuperUserSettings struct {
- Password string `yaml:"password"`
-}
+ authSettings, err := int.Auth.into()
+ if err != nil {
+ return nil, err
+ }
-type AuthSettings struct {
- AccessToken string `yaml:"accesstoken"`
- RefreshToken string `yaml:"refreshtoken"`
- AccessTokenExpiry uint `yaml:"accesstokenexpiry"`
- RefreshTokenExpiry uint `yaml:"refreshtokenexpiry"`
+ return &Settings{
+ Application: int.Application,
+ Database: *databaseSettings,
+ SuperUser: *superUserSettings,
+ Auth: *authSettings,
+ }, nil
}
type Environment string
@@ -78,7 +51,7 @@ const (
EnvironmentProduction Environment = "production"
)
-func GetConfiguration(path string) (Settings, error) {
+func GetConfiguration(path string) (*Settings, error) {
var environment Environment
if env := os.Getenv("APP_ENVIRONMENT"); env != "" {
environment = Environment(env)
@@ -91,80 +64,8 @@ func GetConfiguration(path string) (Settings, error) {
v.AddConfigPath(path)
if environment == EnvironmentLocal {
- var settings Settings
-
- v.SetConfigName(string(environment))
-
- if err := v.ReadInConfig(); err != nil {
- return settings, fmt.Errorf("failed to read %s configuration: %w", string(environment), err)
- }
-
- if err := v.Unmarshal(&settings); err != nil {
- return settings, fmt.Errorf("failed to unmarshal configuration: %w", err)
- }
-
- return settings, nil
+ return readLocal(v)
} else {
- var prodSettings ProductionSettings
-
- v.SetConfigName(string(environment))
-
- if err := v.ReadInConfig(); err != nil {
- return Settings{}, fmt.Errorf("failed to read %s configuration: %w", string(environment), err)
- }
-
- if err := v.Unmarshal(&prodSettings); err != nil {
- return Settings{}, fmt.Errorf("failed to unmarshal configuration: %w", err)
- }
-
- appPrefix := "APP_"
- applicationPrefix := fmt.Sprintf("%sAPPLICATION__", appPrefix)
- dbPrefix := fmt.Sprintf("%sDATABASE__", appPrefix)
- superUserPrefix := fmt.Sprintf("%sSUPERUSER__", appPrefix)
- authSecretPrefix := fmt.Sprintf("%sAUTHSECRET__", appPrefix)
-
- authAccessExpiry := os.Getenv(fmt.Sprintf("%sACCESS_TOKEN_EXPIRY", authSecretPrefix))
- authRefreshExpiry := os.Getenv(fmt.Sprintf("%sREFRESH_TOKEN_EXPIRY", authSecretPrefix))
-
- authAccessExpiryInt, err := strconv.ParseUint(authAccessExpiry, 10, 16)
- if err != nil {
- return Settings{}, fmt.Errorf("failed to parse access token expiry: %w", err)
- }
-
- authRefreshExpiryInt, err := strconv.ParseUint(authRefreshExpiry, 10, 16)
- if err != nil {
- return Settings{}, fmt.Errorf("failed to parse refresh token expiry: %w", err)
- }
-
- portStr := os.Getenv(fmt.Sprintf("%sPORT", appPrefix))
- portInt, err := strconv.ParseUint(portStr, 10, 16)
- if err != nil {
- return Settings{}, fmt.Errorf("failed to parse port: %w", err)
- }
-
- return Settings{
- Application: ApplicationSettings{
- Port: uint16(portInt),
- Host: prodSettings.Application.Host,
- BaseUrl: os.Getenv(fmt.Sprintf("%sBASE_URL", applicationPrefix)),
- },
- Database: DatabaseSettings{
- Username: os.Getenv(fmt.Sprintf("%sUSERNAME", dbPrefix)),
- Password: os.Getenv(fmt.Sprintf("%sPASSWORD", dbPrefix)),
- Host: os.Getenv(fmt.Sprintf("%sHOST", dbPrefix)),
- Port: uint(portInt),
- DatabaseName: os.Getenv(fmt.Sprintf("%sDATABASE_NAME", dbPrefix)),
- RequireSSL: prodSettings.Database.RequireSSL,
- },
- SuperUser: SuperUserSettings{
- Password: os.Getenv(fmt.Sprintf("%sPASSWORD", superUserPrefix)),
- },
- Auth: AuthSettings{
- AccessToken: os.Getenv(fmt.Sprintf("%sACCESS_TOKEN", authSecretPrefix)),
- RefreshToken: os.Getenv(fmt.Sprintf("%sREFRESH_TOKEN", authSecretPrefix)),
- AccessTokenExpiry: uint(authAccessExpiryInt),
- RefreshTokenExpiry: uint(authRefreshExpiryInt),
- },
- }, nil
+ return readProd(v)
}
}
diff --git a/backend/src/config/database.go b/backend/src/config/database.go
new file mode 100644
index 000000000..422d17b69
--- /dev/null
+++ b/backend/src/config/database.go
@@ -0,0 +1,58 @@
+package config
+
+import (
+ "errors"
+ "fmt"
+
+ m "github.com/garrettladley/mattress"
+)
+
+type DatabaseSettings struct {
+ Username string
+ Password *m.Secret[string]
+ Port uint
+ Host string
+ DatabaseName string
+ RequireSSL bool
+}
+
+func (int *intermediateDatabaseSettings) into() (*DatabaseSettings, error) {
+ password, err := m.NewSecret(int.Password)
+ if err != nil {
+ return nil, errors.New("failed to create secret from password")
+ }
+
+ return &DatabaseSettings{
+ Username: int.Username,
+ Password: password,
+ Port: int.Port,
+ Host: int.Host,
+ DatabaseName: int.DatabaseName,
+ RequireSSL: int.RequireSSL,
+ }, nil
+}
+
+type intermediateDatabaseSettings struct {
+ Username string `yaml:"username"`
+ Password string `yaml:"password"`
+ Port uint `yaml:"port"`
+ Host string `yaml:"host"`
+ DatabaseName string `yaml:"databasename"`
+ RequireSSL bool `yaml:"requiressl"`
+}
+
+func (s *DatabaseSettings) WithoutDb() string {
+ var sslMode string
+ if s.RequireSSL {
+ sslMode = "require"
+ } else {
+ sslMode = "disable"
+ }
+
+ return fmt.Sprintf("host=%s port=%d user=%s password=%s sslmode=%s",
+ s.Host, s.Port, s.Username, s.Password.Expose(), sslMode)
+}
+
+func (s *DatabaseSettings) WithDb() string {
+ return fmt.Sprintf("%s dbname=%s", s.WithoutDb(), s.DatabaseName)
+}
diff --git a/backend/src/config/local.go b/backend/src/config/local.go
new file mode 100644
index 000000000..bac72a155
--- /dev/null
+++ b/backend/src/config/local.go
@@ -0,0 +1,30 @@
+package config
+
+import (
+ "fmt"
+
+ "github.com/spf13/viper"
+)
+
+func readLocal(v *viper.Viper) (*Settings, error) {
+ var intermediateSettings intermediateSettings
+
+ env := string(EnvironmentLocal)
+
+ v.SetConfigName(env)
+
+ if err := v.ReadInConfig(); err != nil {
+ return nil, fmt.Errorf("failed to read %s configuration: %w", env, err)
+ }
+
+ if err := v.Unmarshal(&intermediateSettings); err != nil {
+ return nil, fmt.Errorf("failed to unmarshal configuration: %w", err)
+ }
+
+ settings, err := intermediateSettings.into()
+ if err != nil {
+ return nil, fmt.Errorf("failed to convert intermediate settings into final settings: %w", err)
+ }
+
+ return settings, nil
+}
diff --git a/backend/src/config/production.go b/backend/src/config/production.go
new file mode 100644
index 000000000..469a43574
--- /dev/null
+++ b/backend/src/config/production.go
@@ -0,0 +1,111 @@
+package config
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "strconv"
+
+ m "github.com/garrettladley/mattress"
+ "github.com/spf13/viper"
+)
+
+type ProductionSettings struct {
+ Database ProductionDatabaseSettings `yaml:"database"`
+ Application ProductionApplicationSettings `yaml:"application"`
+}
+
+type ProductionDatabaseSettings struct {
+ RequireSSL bool `yaml:"requiressl"`
+}
+
+type ProductionApplicationSettings struct {
+ Port uint16 `yaml:"port"`
+ Host string `yaml:"host"`
+}
+
+func readProd(v *viper.Viper) (*Settings, error) {
+ var prodSettings ProductionSettings
+
+ env := string(EnvironmentProduction)
+
+ v.SetConfigName(env)
+
+ if err := v.ReadInConfig(); err != nil {
+ return nil, fmt.Errorf("failed to read %s configuration: %w", env, err)
+ }
+
+ if err := v.Unmarshal(&prodSettings); err != nil {
+ return nil, fmt.Errorf("failed to unmarshal configuration: %w", err)
+ }
+
+ appPrefix := "APP_"
+ applicationPrefix := fmt.Sprintf("%sAPPLICATION__", appPrefix)
+ dbPrefix := fmt.Sprintf("%sDATABASE__", appPrefix)
+ superUserPrefix := fmt.Sprintf("%sSUPERUSER__", appPrefix)
+ authSecretPrefix := fmt.Sprintf("%sAUTHSECRET__", appPrefix)
+
+ authAccessExpiry := os.Getenv(fmt.Sprintf("%sACCESS_TOKEN_EXPIRY", authSecretPrefix))
+ authRefreshExpiry := os.Getenv(fmt.Sprintf("%sREFRESH_TOKEN_EXPIRY", authSecretPrefix))
+
+ authAccessExpiryInt, err := strconv.ParseUint(authAccessExpiry, 10, 16)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse access token expiry: %w", err)
+ }
+
+ authRefreshExpiryInt, err := strconv.ParseUint(authRefreshExpiry, 10, 16)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse refresh token expiry: %w", err)
+ }
+
+ portStr := os.Getenv(fmt.Sprintf("%sPORT", appPrefix))
+ portInt, err := strconv.ParseUint(portStr, 10, 16)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse port: %w", err)
+ }
+
+ dbPassword, err := m.NewSecret(os.Getenv(fmt.Sprintf("%sUSERNAME", dbPrefix)))
+ if err != nil {
+ return nil, errors.New("failed to create secret from database password")
+ }
+
+ superPassword, err := m.NewSecret(os.Getenv(fmt.Sprintf("%sPASSWORD", superUserPrefix)))
+ if err != nil {
+ return nil, errors.New("failed to create secret from super user password")
+ }
+
+ authAccessKey, err := m.NewSecret(os.Getenv(fmt.Sprintf("%sACCESS_TOKEN", authSecretPrefix)))
+ if err != nil {
+ return nil, errors.New("failed to create secret from access token")
+ }
+
+ authRefreshKey, err := m.NewSecret(os.Getenv(fmt.Sprintf("%sREFRESH_TOKEN", authSecretPrefix)))
+ if err != nil {
+ return nil, errors.New("failed to create secret from refresh token")
+ }
+
+ return &Settings{
+ Application: ApplicationSettings{
+ Port: uint16(portInt),
+ Host: prodSettings.Application.Host,
+ BaseUrl: os.Getenv(fmt.Sprintf("%sBASE_URL", applicationPrefix)),
+ },
+ Database: DatabaseSettings{
+ Username: os.Getenv(fmt.Sprintf("%sUSERNAME", dbPrefix)),
+ Password: dbPassword,
+ Host: os.Getenv(fmt.Sprintf("%sHOST", dbPrefix)),
+ Port: uint(portInt),
+ DatabaseName: os.Getenv(fmt.Sprintf("%sDATABASE_NAME", dbPrefix)),
+ RequireSSL: prodSettings.Database.RequireSSL,
+ },
+ SuperUser: SuperUserSettings{
+ Password: superPassword,
+ },
+ Auth: AuthSettings{
+ AccessKey: authAccessKey,
+ RefreshKey: authRefreshKey,
+ AccessTokenExpiry: uint(authAccessExpiryInt),
+ RefreshTokenExpiry: uint(authRefreshExpiryInt),
+ },
+ }, nil
+}
diff --git a/backend/src/config/super_user.go b/backend/src/config/super_user.go
new file mode 100644
index 000000000..2a75c88ef
--- /dev/null
+++ b/backend/src/config/super_user.go
@@ -0,0 +1,26 @@
+package config
+
+import (
+ "errors"
+
+ m "github.com/garrettladley/mattress"
+)
+
+type SuperUserSettings struct {
+ Password *m.Secret[string]
+}
+
+type intermediateSuperUserSettings struct {
+ Password string `yaml:"password"`
+}
+
+func (int *intermediateSuperUserSettings) into() (*SuperUserSettings, error) {
+ password, err := m.NewSecret(int.Password)
+ if err != nil {
+ return nil, errors.New("failed to create secret from password")
+ }
+
+ return &SuperUserSettings{
+ Password: password,
+ }, nil
+}
diff --git a/backend/src/controllers/auth.go b/backend/src/controllers/auth.go
index 546f407e8..8ab0b7bd5 100644
--- a/backend/src/controllers/auth.go
+++ b/backend/src/controllers/auth.go
@@ -99,7 +99,7 @@ func (a *AuthController) Refresh(c *fiber.Ctx) error {
refreshTokenValue := c.Cookies("refresh_token")
// Extract id from refresh token
- claims, err := auth.ExtractRefreshClaims(refreshTokenValue, a.AuthSettings.RefreshToken)
+ claims, err := auth.ExtractRefreshClaims(refreshTokenValue, a.AuthSettings.RefreshKey)
if err != nil {
return err.FiberError(c)
}
@@ -109,7 +109,7 @@ func (a *AuthController) Refresh(c *fiber.Ctx) error {
return err.FiberError(c)
}
- accessToken, err := auth.RefreshAccessToken(refreshTokenValue, string(*role), a.AuthSettings.RefreshToken, a.AuthSettings.AccessTokenExpiry, a.AuthSettings.AccessToken)
+ accessToken, err := auth.RefreshAccessToken(refreshTokenValue, string(*role), a.AuthSettings.RefreshKey, a.AuthSettings.AccessTokenExpiry, a.AuthSettings.AccessKey)
if err != nil {
return err.FiberError(c)
}
diff --git a/backend/src/database/super.go b/backend/src/database/super.go
index 22700693e..93c065b66 100644
--- a/backend/src/database/super.go
+++ b/backend/src/database/super.go
@@ -11,7 +11,7 @@ import (
var SuperUserUUID uuid.UUID
func SuperUser(superUserSettings config.SuperUserSettings) (*models.User, *errors.Error) {
- passwordHash, err := auth.ComputePasswordHash(superUserSettings.Password)
+ passwordHash, err := auth.ComputePasswordHash(superUserSettings.Password.Expose())
if err != nil {
return nil, &errors.FailedToComputePasswordHash
}
diff --git a/backend/src/main.go b/backend/src/main.go
index 2c2f73672..ca83de46c 100644
--- a/backend/src/main.go
+++ b/backend/src/main.go
@@ -23,7 +23,7 @@ func main() {
panic(fmt.Sprintf("Error getting configuration: %s", err.Error()))
}
- db, err := database.ConfigureDB(config)
+ db, err := database.ConfigureDB(*config)
if err != nil {
panic(fmt.Sprintf("Error configuring database: %s", err.Error()))
}
@@ -33,7 +33,7 @@ func main() {
panic(err)
}
- app := server.Init(db, config)
+ app := server.Init(db, *config)
err = app.Listen(fmt.Sprintf("%s:%d", config.Application.Host, config.Application.Port))
if err != nil {
diff --git a/backend/src/middleware/auth.go b/backend/src/middleware/auth.go
index 3bba7c497..dc49e8f66 100644
--- a/backend/src/middleware/auth.go
+++ b/backend/src/middleware/auth.go
@@ -38,7 +38,7 @@ func (m *MiddlewareService) Authenticate(c *fiber.Ctx) error {
return c.Next()
}
- token, err := auth.ParseAccessToken(c.Cookies("access_token"), m.AuthSettings.AccessToken)
+ token, err := auth.ParseAccessToken(c.Cookies("access_token"), m.AuthSettings.AccessKey)
if err != nil {
return errors.FailedToParseAccessToken.FiberError(c)
}
@@ -68,7 +68,7 @@ func (m *MiddlewareService) Authorize(requiredPermissions ...types.Permission) f
return c.Next()
}
- role, err := auth.GetRoleFromToken(c.Cookies("access_token"), m.AuthSettings.AccessToken)
+ role, err := auth.GetRoleFromToken(c.Cookies("access_token"), m.AuthSettings.AccessKey)
if err != nil {
return errors.FailedToParseAccessToken.FiberError(c)
}
diff --git a/backend/src/middleware/club.go b/backend/src/middleware/club.go
index c64fa7068..dc848b75d 100644
--- a/backend/src/middleware/club.go
+++ b/backend/src/middleware/club.go
@@ -17,7 +17,7 @@ func (m *MiddlewareService) ClubAuthorizeById(c *fiber.Ctx) error {
return errors.FailedToValidateID.FiberError(c)
}
- token, tokenErr := auth.ParseAccessToken(c.Cookies("access_token"), m.AuthSettings.AccessToken)
+ token, tokenErr := auth.ParseAccessToken(c.Cookies("access_token"), m.AuthSettings.AccessKey)
if tokenErr != nil {
return errors.FailedToParseAccessToken.FiberError(c)
}
diff --git a/backend/src/middleware/user.go b/backend/src/middleware/user.go
index 308372a4f..804a1295b 100644
--- a/backend/src/middleware/user.go
+++ b/backend/src/middleware/user.go
@@ -14,7 +14,7 @@ func (m *MiddlewareService) UserAuthorizeById(c *fiber.Ctx) error {
return errors.FailedToValidateID.FiberError(c)
}
- token, tokenErr := auth.ParseAccessToken(c.Cookies("access_token"), m.AuthSettings.AccessToken)
+ token, tokenErr := auth.ParseAccessToken(c.Cookies("access_token"), m.AuthSettings.AccessKey)
if tokenErr != nil {
return err
}
diff --git a/backend/tests/api/helpers/app.go b/backend/tests/api/helpers/app.go
index cb1689e72..68b4acd55 100644
--- a/backend/tests/api/helpers/app.go
+++ b/backend/tests/api/helpers/app.go
@@ -33,16 +33,16 @@ func spawnApp() (*TestApp, error) {
configuration.Database.DatabaseName = generateRandomDBName()
- connectionWithDB, err := configureDatabase(configuration)
+ connectionWithDB, err := configureDatabase(*configuration)
if err != nil {
return nil, err
}
return &TestApp{
- App: server.Init(connectionWithDB, configuration),
+ App: server.Init(connectionWithDB, *configuration),
Address: fmt.Sprintf("http://%s", listener.Addr().String()),
Conn: connectionWithDB,
- Settings: configuration,
+ Settings: *configuration,
}, nil
}
diff --git a/backend/tests/api/helpers/auth.go b/backend/tests/api/helpers/auth.go
index 76fb3033a..eb65640e3 100644
--- a/backend/tests/api/helpers/auth.go
+++ b/backend/tests/api/helpers/auth.go
@@ -40,7 +40,7 @@ func (app *TestApp) authSuper() {
Path: "/api/v1/auth/login",
Body: &map[string]interface{}{
"email": email,
- "password": password,
+ "password": password.Expose(),
},
})
if err != nil {
@@ -65,7 +65,7 @@ func (app *TestApp) authSuper() {
app.TestUser = &TestUser{
UUID: database.SuperUserUUID,
Email: email,
- Password: password,
+ Password: password.Expose(),
AccessToken: accessToken,
RefreshToken: refreshToken,
}
diff --git a/backend/tests/auth_test.go b/backend/tests/auth_test.go
index d7d49bb40..3bfc1f530 100644
--- a/backend/tests/auth_test.go
+++ b/backend/tests/auth_test.go
@@ -6,17 +6,28 @@ import (
"github.com/GenerateNU/sac/backend/src/auth"
"github.com/GenerateNU/sac/backend/src/config"
+ m "github.com/garrettladley/mattress"
"github.com/golang-jwt/jwt"
"github.com/huandu/go-assert"
)
-func AuthSettings() config.AuthSettings {
- return config.AuthSettings{
- AccessToken: "g(r|##*?>\\Qp}h37e+,T2",
+func AuthSettings() (*config.AuthSettings, error) {
+ accessKey, err := m.NewSecret("g(r|##*?>\\Qp}h37e+,T2")
+ if err != nil {
+ return nil, err
+ }
+
+ refreshKey, err := m.NewSecret("amk*2!gG}1i\"8D9RwJS$p")
+ if err != nil {
+ return nil, err
+ }
+
+ return &config.AuthSettings{
+ AccessKey: accessKey,
AccessTokenExpiry: 60,
- RefreshToken: "amk*2!gG}1i\"8D9RwJS$p",
+ RefreshKey: refreshKey,
RefreshTokenExpiry: 30,
- }
+ }, nil
}
func TestCreateTokenPairSuccess(t *testing.T) {
@@ -25,9 +36,12 @@ func TestCreateTokenPairSuccess(t *testing.T) {
id := "user123"
role := "admin"
- accessToken, refreshToken, err := auth.CreateTokenPair(id, role, AuthSettings())
+ authSettings, err := AuthSettings()
+ assert.NilError(err)
+
+ accessToken, refreshToken, authErr := auth.CreateTokenPair(id, role, *authSettings)
- assert.Assert(err == nil)
+ assert.Assert(authErr == nil)
assert.Assert(accessToken != nil)
assert.Assert(refreshToken != nil)
@@ -39,9 +53,13 @@ func TestCreateTokenPairFailure(t *testing.T) {
id := "user123"
role := ""
- accessToken, refreshToken, err := auth.CreateTokenPair(id, role, AuthSettings())
+ authSettings, err := AuthSettings()
+
+ assert.NilError(err)
+
+ accessToken, refreshToken, authErr := auth.CreateTokenPair(id, role, *authSettings)
- assert.Assert(err != nil)
+ assert.Assert(authErr != nil)
assert.Assert(accessToken == nil)
assert.Assert(refreshToken == nil)
@@ -53,11 +71,13 @@ func TestCreateAccessTokenSuccess(t *testing.T) {
id := "user123"
role := "admin"
- authSettings := AuthSettings()
+ authSettings, err := AuthSettings()
- accessToken, err := auth.CreateAccessToken(id, role, authSettings.AccessTokenExpiry, authSettings.AccessToken)
+ assert.NilError(err)
- assert.Assert(err == nil)
+ accessToken, authErr := auth.CreateAccessToken(id, role, authSettings.AccessTokenExpiry, authSettings.AccessKey)
+
+ assert.Assert(authErr == nil)
assert.Assert(accessToken != nil)
}
@@ -68,11 +88,13 @@ func TestCreateAccessTokenFailure(t *testing.T) {
id := "user123"
role := ""
- authSettings := AuthSettings()
+ authSettings, err := AuthSettings()
+
+ assert.NilError(err)
- accessToken, err := auth.CreateAccessToken(id, role, authSettings.AccessTokenExpiry, authSettings.AccessToken)
+ accessToken, authErr := auth.CreateAccessToken(id, role, authSettings.AccessTokenExpiry, authSettings.AccessKey)
- assert.Assert(err != nil)
+ assert.Assert(authErr != nil)
assert.Assert(accessToken == nil)
}
@@ -82,11 +104,13 @@ func TestCreateRefreshTokenSuccess(t *testing.T) {
id := "user123"
- authSettings := AuthSettings()
+ authSettings, err := AuthSettings()
- refreshToken, err := auth.CreateRefreshToken(id, authSettings.RefreshTokenExpiry, authSettings.RefreshToken)
+ assert.NilError(err)
- assert.Assert(err == nil)
+ refreshToken, authErr := auth.CreateRefreshToken(id, authSettings.RefreshTokenExpiry, authSettings.RefreshKey)
+
+ assert.Assert(authErr == nil)
assert.Assert(refreshToken != nil)
}
@@ -96,11 +120,13 @@ func TestCreateRefreshTokenFailure(t *testing.T) {
id := ""
- authSettings := AuthSettings()
+ authSettings, err := AuthSettings()
+
+ assert.NilError(err)
- refreshToken, err := auth.CreateRefreshToken(id, authSettings.RefreshTokenExpiry, authSettings.RefreshToken)
+ refreshToken, authErr := auth.CreateRefreshToken(id, authSettings.RefreshTokenExpiry, authSettings.RefreshKey)
- assert.Assert(err != nil)
+ assert.Assert(authErr != nil)
assert.Assert(refreshToken == nil)
}
@@ -119,9 +145,13 @@ func TestSignTokenSuccess(t *testing.T) {
"iss": "sac",
}
- signedToken, err := auth.SignToken(token, "secret")
+ key, err := m.NewSecret("secret")
- assert.Assert(err == nil)
+ assert.NilError(err)
+
+ signedToken, authErr := auth.SignToken(token, key)
+
+ assert.Assert(authErr == nil)
assert.Assert(signedToken != nil)
}
@@ -140,9 +170,13 @@ func TestSignTokenFailure(t *testing.T) {
"iss": "sac",
}
- signedToken, err := auth.SignToken(token, "")
+ key, err := m.NewSecret("")
+
+ assert.NilError(err)
+
+ signedToken, authErr := auth.SignToken(token, key)
- assert.Assert(err != nil)
+ assert.Assert(authErr != nil)
assert.Assert(signedToken == nil)
}
diff --git a/config/local.yml b/config/local.yml
index 57da31f0f..2ad38c2dd 100644
--- a/config/local.yml
+++ b/config/local.yml
@@ -12,7 +12,7 @@ database:
superuser:
password: password
auth:
- accesstoken: g(r|##*?>\Qp}h37e+,T2
+ accesskey: g(r|##*?>\Qp}h37e+,T2
accesstokenexpiry: 60 # in minutes
- refreshtoken: amk*2!gG}1i"8D9RwJS$p
+ refreshkey: amk*2!gG}1i"8D9RwJS$p
refreshtokenexpiry: 30 # in days
diff --git a/go.work b/go.work
index 5a962e04b..9bb923f4e 100644
--- a/go.work
+++ b/go.work
@@ -1,4 +1,4 @@
-go 1.21.1
+go 1.21.6
use (
./backend
diff --git a/go.work.sum b/go.work.sum
index a9dfabbc1..52c5208f1 100644
--- a/go.work.sum
+++ b/go.work.sum
@@ -1,6 +1,8 @@
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/garrettladley/mattress v0.2.0 h1:+XUdsv9NO2s4JL+8exvAFziw0b1kv/0WlQo2Dlxat+w=
+github.com/garrettladley/mattress v0.2.0/go.mod h1:OWKIRc9wC3gtD3Ng/nUuNEiR1TJvRYLmn/KZYw9nl5Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
@@ -11,6 +13,7 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
From dcd59f4795784501f0d8ccdbfddbf98c3bfc00c4 Mon Sep 17 00:00:00 2001
From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
Date: Tue, 6 Feb 2024 21:06:51 -0500
Subject: [PATCH 26/33] Testing Refactor/Rename (#167)
---
backend/tests/README.md | 2 +-
backend/tests/api/category_tag_test.go | 35 ++--
backend/tests/api/category_test.go | 125 +++++++------
backend/tests/api/club_test.go | 239 ++++++++++++-------------
backend/tests/api/helpers/requests.go | 35 ++--
backend/tests/api/tag_test.go | 83 ++++-----
backend/tests/api/user_tag_test.go | 69 ++++---
backend/tests/api/user_test.go | 173 +++++++++---------
8 files changed, 373 insertions(+), 388 deletions(-)
diff --git a/backend/tests/README.md b/backend/tests/README.md
index b2b9c431a..e9dc17a0a 100644
--- a/backend/tests/README.md
+++ b/backend/tests/README.md
@@ -52,7 +52,7 @@ TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/categories/",
Body: SampleCategoryFactory(),
- }.TestOnStatusAndDB(t, nil,
+ }.TestOnStatusAndTester(t, nil,
TesterWithStatus{
Status: fiber.StatusCreated,
DBTester: AssertSampleCategoryBodyRespDB,
diff --git a/backend/tests/api/category_tag_test.go b/backend/tests/api/category_tag_test.go
index cdba4b6c9..d5650286d 100644
--- a/backend/tests/api/category_tag_test.go
+++ b/backend/tests/api/category_tag_test.go
@@ -12,28 +12,27 @@ import (
"github.com/goccy/go-json"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
- "github.com/huandu/go-assert"
)
-func AssertTagsWithBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, body *[]map[string]interface{}) []uuid.UUID {
+func AssertTagsWithBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response, body *[]map[string]interface{}) []uuid.UUID {
var respTags []models.Tag
err := json.NewDecoder(resp.Body).Decode(&respTags)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
var dbTags []models.Tag
- err = app.Conn.Find(&dbTags).Error
+ err = eaa.App.Conn.Find(&dbTags).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(len(dbTags), len(respTags))
+ eaa.Assert.Equal(len(dbTags), len(respTags))
for i, dbTag := range dbTags {
- assert.Equal(dbTag.ID, respTags[i].ID)
- assert.Equal(dbTag.Name, respTags[i].Name)
- assert.Equal(dbTag.CategoryID, respTags[i].CategoryID)
+ eaa.Assert.Equal(dbTag.ID, respTags[i].ID)
+ eaa.Assert.Equal(dbTag.Name, respTags[i].Name)
+ eaa.Assert.Equal(dbTag.CategoryID, respTags[i].CategoryID)
}
tagIDs := make([]uuid.UUID, len(dbTags))
@@ -50,7 +49,7 @@ func TestGetCategoryTagsWorks(t *testing.T) {
body := SampleTagFactory(categoryUUID)
(*body)["id"] = tagID
- appAssert.TestOnStatusAndDB(
+ appAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodGet,
Path: fmt.Sprintf("/api/v1/categories/%s/tags", categoryUUID),
@@ -58,8 +57,8 @@ func TestGetCategoryTagsWorks(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusOK,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertTagsWithBodyRespDB(app, assert, resp, &[]map[string]interface{}{*body})
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertTagsWithBodyRespDB(eaa, resp, &[]map[string]interface{}{*body})
},
},
).Close()
@@ -102,10 +101,10 @@ func TestGetCategoryTagsFailsCategoryNotFound(t *testing.T) {
Role: &models.Super,
}, h.ErrorWithTester{
Error: errors.CategoryNotFound,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var category models.Category
- err := app.Conn.Where("id = ?", uuid).First(&category).Error
- assert.Assert(err != nil)
+ err := eaa.App.Conn.Where("id = ?", uuid).First(&category).Error
+ eaa.Assert.Assert(err != nil)
},
},
).Close()
@@ -114,7 +113,7 @@ func TestGetCategoryTagsFailsCategoryNotFound(t *testing.T) {
func TestGetCategoryTagWorks(t *testing.T) {
existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(h.InitTest(t))
- existingAppAssert.TestOnStatusAndDB(
+ existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodGet,
Path: fmt.Sprintf("/api/v1/categories/%s/tags/%s", categoryUUID, tagUUID),
@@ -122,8 +121,8 @@ func TestGetCategoryTagWorks(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusOK,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory(categoryUUID))
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertTagWithBodyRespDB(eaa, resp, SampleTagFactory(categoryUUID))
},
},
).Close()
diff --git a/backend/tests/api/category_test.go b/backend/tests/api/category_test.go
index 736f92844..781831ded 100644
--- a/backend/tests/api/category_test.go
+++ b/backend/tests/api/category_test.go
@@ -10,7 +10,6 @@ import (
h "github.com/GenerateNU/sac/backend/tests/api/helpers"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
- "github.com/huandu/go-assert"
"github.com/goccy/go-json"
)
@@ -21,60 +20,60 @@ func SampleCategoryFactory() *map[string]interface{} {
}
}
-func AssertCategoryBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
+func AssertCategoryBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response, body *map[string]interface{}) uuid.UUID {
var respCategory models.Category
err := json.NewDecoder(resp.Body).Decode(&respCategory)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
var dbCategories []models.Category
- err = app.Conn.Find(&dbCategories).Error
+ err = eaa.App.Conn.Find(&dbCategories).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(1, len(dbCategories))
+ eaa.Assert.Equal(1, len(dbCategories))
dbCategory := dbCategories[0]
- assert.Equal(dbCategory.ID, respCategory.ID)
- assert.Equal(dbCategory.Name, respCategory.Name)
+ eaa.Assert.Equal(dbCategory.ID, respCategory.ID)
+ eaa.Assert.Equal(dbCategory.Name, respCategory.Name)
- assert.Equal((*body)["name"].(string), dbCategory.Name)
+ eaa.Assert.Equal((*body)["name"].(string), dbCategory.Name)
return dbCategory.ID
}
-func AssertCategoryWithBodyRespDBMostRecent(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
+func AssertCategoryWithBodyRespDBMostRecent(eaa h.ExistingAppAssert, resp *http.Response, body *map[string]interface{}) uuid.UUID {
var respCategory models.Category
err := json.NewDecoder(resp.Body).Decode(&respCategory)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
var dbCategory models.Category
- err = app.Conn.Order("created_at desc").First(&dbCategory).Error
+ err = eaa.App.Conn.Order("created_at desc").First(&dbCategory).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(dbCategory.ID, respCategory.ID)
- assert.Equal(dbCategory.Name, respCategory.Name)
+ eaa.Assert.Equal(dbCategory.ID, respCategory.ID)
+ eaa.Assert.Equal(dbCategory.Name, respCategory.Name)
- assert.Equal((*body)["name"].(string), dbCategory.Name)
+ eaa.Assert.Equal((*body)["name"].(string), dbCategory.Name)
return dbCategory.ID
}
-func AssertSampleCategoryBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response) uuid.UUID {
- return AssertCategoryBodyRespDB(app, assert, resp, SampleCategoryFactory())
+func AssertSampleCategoryBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response) uuid.UUID {
+ return AssertCategoryBodyRespDB(eaa, resp, SampleCategoryFactory())
}
func CreateSampleCategory(existingAppAssert h.ExistingAppAssert) (h.ExistingAppAssert, uuid.UUID) {
var sampleCategoryUUID uuid.UUID
- existingAppAssert.TestOnStatusAndDB(
+ existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/categories/",
@@ -83,8 +82,8 @@ func CreateSampleCategory(existingAppAssert h.ExistingAppAssert) (h.ExistingAppA
},
h.TesterWithStatus{
Status: fiber.StatusCreated,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
- sampleCategoryUUID = AssertSampleCategoryBodyRespDB(app, assert, resp)
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ sampleCategoryUUID = AssertSampleCategoryBodyRespDB(eaa, resp)
},
},
)
@@ -98,7 +97,7 @@ func TestCreateCategoryWorks(t *testing.T) {
}
func TestCreateCategoryIgnoresid(t *testing.T) {
- h.InitTest(t).TestOnStatusAndDB(
+ h.InitTest(t).TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/categories/",
@@ -110,29 +109,29 @@ func TestCreateCategoryIgnoresid(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusCreated,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertSampleCategoryBodyRespDB(app, assert, resp)
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertSampleCategoryBodyRespDB(eaa, resp)
},
},
).Close()
}
-func Assert1Category(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertNumCategoriesRemainsAtN(app, assert, resp, 1)
+func Assert1Category(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertNumCategoriesRemainsAtN(eaa, resp, 1)
}
-func AssertNoCategories(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertNumCategoriesRemainsAtN(app, assert, resp, 0)
+func AssertNoCategories(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertNumCategoriesRemainsAtN(eaa, resp, 0)
}
-func AssertNumCategoriesRemainsAtN(app h.TestApp, assert *assert.A, resp *http.Response, n int) {
+func AssertNumCategoriesRemainsAtN(eaa h.ExistingAppAssert, resp *http.Response, n int) {
var categories []models.Category
- err := app.Conn.Find(&categories).Error
+ err := eaa.App.Conn.Find(&categories).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(n, len(categories))
+ eaa.Assert.Equal(n, len(categories))
}
func TestCreateCategoryFailsIfNameIsNotString(t *testing.T) {
@@ -170,8 +169,8 @@ func TestCreateCategoryFailsIfNameIsMissing(t *testing.T) {
func TestCreateCategoryFailsIfCategoryWithThatNameAlreadyExists(t *testing.T) {
existingAppAssert, _ := CreateSampleCategory(h.InitTest(t))
- TestNumCategoriesRemainsAt1 := func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertNumCategoriesRemainsAtN(app, assert, resp, 1)
+ TestNumCategoriesRemainsAt1 := func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertNumCategoriesRemainsAtN(eaa, resp, 1)
}
for _, permutation := range h.AllCasingPermutations((*SampleCategoryFactory())["name"].(string)) {
@@ -198,7 +197,7 @@ func TestCreateCategoryFailsIfCategoryWithThatNameAlreadyExists(t *testing.T) {
func TestGetCategoryWorks(t *testing.T) {
existingAppAssert, uuid := CreateSampleCategory(h.InitTest(t))
- existingAppAssert.TestOnStatusAndDB(
+ existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodGet,
Path: fmt.Sprintf("/api/v1/categories/%s", uuid),
@@ -206,8 +205,8 @@ func TestGetCategoryWorks(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusOK,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertSampleCategoryBodyRespDB(app, assert, resp)
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertSampleCategoryBodyRespDB(eaa, resp)
},
},
).Close()
@@ -251,7 +250,7 @@ func TestGetCategoryFailsNotFound(t *testing.T) {
func TestGetCategoriesWorks(t *testing.T) {
existingAppAssert, _ := CreateSampleCategory(h.InitTest(t))
- existingAppAssert.TestOnStatusAndDB(
+ existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodGet,
Path: "/api/v1/categories/",
@@ -259,54 +258,54 @@ func TestGetCategoriesWorks(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusOK,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var categories []models.Category
- err := app.Conn.Find(&categories).Error
+ err := eaa.App.Conn.Find(&categories).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
var respCategories []models.Category
err = json.NewDecoder(resp.Body).Decode(&respCategories)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(1, len(respCategories))
- assert.Equal(1, len(categories))
+ eaa.Assert.Equal(1, len(respCategories))
+ eaa.Assert.Equal(1, len(categories))
- assert.Equal(categories[0].ID, respCategories[0].ID)
+ eaa.Assert.Equal(categories[0].ID, respCategories[0].ID)
- assert.Equal(categories[0].Name, respCategories[0].Name)
+ eaa.Assert.Equal(categories[0].Name, respCategories[0].Name)
- assert.Equal((*SampleCategoryFactory())["name"].(string), categories[0].Name)
+ eaa.Assert.Equal((*SampleCategoryFactory())["name"].(string), categories[0].Name)
},
},
).Close()
}
-func AssertUpdatedCategoryBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) {
+func AssertUpdatedCategoryBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response, body *map[string]interface{}) {
var respCategory models.Category
err := json.NewDecoder(resp.Body).Decode(&respCategory)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
var dbCategories []models.Category
- err = app.Conn.Find(&dbCategories).Error
+ err = eaa.App.Conn.Find(&dbCategories).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(1, len(dbCategories))
+ eaa.Assert.Equal(1, len(dbCategories))
dbCategory := dbCategories[0]
- assert.Equal(dbCategory.ID, respCategory.ID)
- assert.Equal(dbCategory.Name, respCategory.Name)
+ eaa.Assert.Equal(dbCategory.ID, respCategory.ID)
+ eaa.Assert.Equal(dbCategory.Name, respCategory.Name)
- assert.Equal((*body)["id"].(uuid.UUID), dbCategory.ID)
- assert.Equal((*body)["name"].(string), dbCategory.Name)
+ eaa.Assert.Equal((*body)["id"].(uuid.UUID), dbCategory.ID)
+ eaa.Assert.Equal((*body)["name"].(string), dbCategory.Name)
}
func TestUpdateCategoryWorks(t *testing.T) {
@@ -317,11 +316,11 @@ func TestUpdateCategoryWorks(t *testing.T) {
"name": "Arts & Crafts",
}
- AssertUpdatedCategoryBodyRespDB := func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertUpdatedCategoryBodyRespDB(app, assert, resp, &category)
+ AssertUpdatedCategoryBodyRespDB := func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertUpdatedCategoryBodyRespDB(eaa, resp, &category)
}
- existingAppAssert.TestOnStatusAndDB(
+ existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPatch,
Path: fmt.Sprintf("/api/v1/categories/%s", uuid),
@@ -341,11 +340,11 @@ func TestUpdateCategoryWorksWithSameDetails(t *testing.T) {
category := *SampleCategoryFactory()
category["id"] = uuid
- AssertSampleCategoryBodyRespDB := func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertUpdatedCategoryBodyRespDB(app, assert, resp, &category)
+ AssertSampleCategoryBodyRespDB := func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertUpdatedCategoryBodyRespDB(eaa, resp, &category)
}
- existingAppAssert.TestOnStatusAndDB(
+ existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPatch,
Path: fmt.Sprintf("/api/v1/categories/%s", uuid),
@@ -388,7 +387,7 @@ func TestUpdateCategoryFailsBadRequest(t *testing.T) {
func TestDeleteCategoryWorks(t *testing.T) {
existingAppAssert, uuid := CreateSampleCategory(h.InitTest(t))
- existingAppAssert.TestOnStatusAndDB(
+ existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/categories/%s", uuid),
diff --git a/backend/tests/api/club_test.go b/backend/tests/api/club_test.go
index 4a632dd42..f95783385 100644
--- a/backend/tests/api/club_test.go
+++ b/backend/tests/api/club_test.go
@@ -12,7 +12,6 @@ import (
"github.com/goccy/go-json"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
- "github.com/huandu/go-assert"
"gorm.io/gorm"
)
@@ -30,114 +29,114 @@ func SampleClubFactory(userID *uuid.UUID) *map[string]interface{} {
}
}
-func AssertClubBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
+func AssertClubBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response, body *map[string]interface{}) uuid.UUID {
var respClub models.Club
err := json.NewDecoder(resp.Body).Decode(&respClub)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
var dbClubs []models.Club
- err = app.Conn.Order("created_at desc").Find(&dbClubs).Error
+ err = eaa.App.Conn.Order("created_at desc").Find(&dbClubs).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(2, len(dbClubs))
+ eaa.Assert.Equal(2, len(dbClubs))
dbClub := dbClubs[0]
- assert.Equal(dbClub.ID, respClub.ID)
- assert.Equal(dbClub.Name, respClub.Name)
- assert.Equal(dbClub.Preview, respClub.Preview)
- assert.Equal(dbClub.Description, respClub.Description)
- assert.Equal(dbClub.NumMembers, respClub.NumMembers)
- assert.Equal(dbClub.IsRecruiting, respClub.IsRecruiting)
- assert.Equal(dbClub.RecruitmentCycle, respClub.RecruitmentCycle)
- assert.Equal(dbClub.RecruitmentType, respClub.RecruitmentType)
- assert.Equal(dbClub.ApplicationLink, respClub.ApplicationLink)
- assert.Equal(dbClub.Logo, respClub.Logo)
+ eaa.Assert.Equal(dbClub.ID, respClub.ID)
+ eaa.Assert.Equal(dbClub.Name, respClub.Name)
+ eaa.Assert.Equal(dbClub.Preview, respClub.Preview)
+ eaa.Assert.Equal(dbClub.Description, respClub.Description)
+ eaa.Assert.Equal(dbClub.NumMembers, respClub.NumMembers)
+ eaa.Assert.Equal(dbClub.IsRecruiting, respClub.IsRecruiting)
+ eaa.Assert.Equal(dbClub.RecruitmentCycle, respClub.RecruitmentCycle)
+ eaa.Assert.Equal(dbClub.RecruitmentType, respClub.RecruitmentType)
+ eaa.Assert.Equal(dbClub.ApplicationLink, respClub.ApplicationLink)
+ eaa.Assert.Equal(dbClub.Logo, respClub.Logo)
var dbAdmins []models.User
- err = app.Conn.Model(&dbClub).Association("Admin").Find(&dbAdmins)
+ err = eaa.App.Conn.Model(&dbClub).Association("Admin").Find(&dbAdmins)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(1, len(dbAdmins))
+ eaa.Assert.Equal(1, len(dbAdmins))
- assert.Equal(*(*body)["user_id"].(*uuid.UUID), dbAdmins[0].ID)
- assert.Equal((*body)["name"].(string), dbClub.Name)
- assert.Equal((*body)["preview"].(string), dbClub.Preview)
- assert.Equal((*body)["description"].(string), dbClub.Description)
- assert.Equal((*body)["is_recruiting"].(bool), dbClub.IsRecruiting)
- assert.Equal(models.RecruitmentCycle((*body)["recruitment_cycle"].(string)), dbClub.RecruitmentCycle)
- assert.Equal(models.RecruitmentType((*body)["recruitment_type"].(string)), dbClub.RecruitmentType)
- assert.Equal((*body)["application_link"].(string), dbClub.ApplicationLink)
- assert.Equal((*body)["logo"].(string), dbClub.Logo)
+ eaa.Assert.Equal(*(*body)["user_id"].(*uuid.UUID), dbAdmins[0].ID)
+ eaa.Assert.Equal((*body)["name"].(string), dbClub.Name)
+ eaa.Assert.Equal((*body)["preview"].(string), dbClub.Preview)
+ eaa.Assert.Equal((*body)["description"].(string), dbClub.Description)
+ eaa.Assert.Equal((*body)["is_recruiting"].(bool), dbClub.IsRecruiting)
+ eaa.Assert.Equal(models.RecruitmentCycle((*body)["recruitment_cycle"].(string)), dbClub.RecruitmentCycle)
+ eaa.Assert.Equal(models.RecruitmentType((*body)["recruitment_type"].(string)), dbClub.RecruitmentType)
+ eaa.Assert.Equal((*body)["application_link"].(string), dbClub.ApplicationLink)
+ eaa.Assert.Equal((*body)["logo"].(string), dbClub.Logo)
return dbClub.ID
}
-func AssertClubWithBodyRespDBMostRecent(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
+func AssertClubWithBodyRespDBMostRecent(eaa h.ExistingAppAssert, resp *http.Response, body *map[string]interface{}) uuid.UUID {
var respClub models.Club
err := json.NewDecoder(resp.Body).Decode(&respClub)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
var dbClub models.Club
- err = app.Conn.Order("created_at desc").First(&dbClub).Error
+ err = eaa.App.Conn.Order("created_at desc").First(&dbClub).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(dbClub.ID, respClub.ID)
- assert.Equal(dbClub.Name, respClub.Name)
- assert.Equal(dbClub.Preview, respClub.Preview)
- assert.Equal(dbClub.Description, respClub.Description)
- assert.Equal(dbClub.NumMembers, respClub.NumMembers)
- assert.Equal(dbClub.IsRecruiting, respClub.IsRecruiting)
- assert.Equal(dbClub.RecruitmentCycle, respClub.RecruitmentCycle)
- assert.Equal(dbClub.RecruitmentType, respClub.RecruitmentType)
- assert.Equal(dbClub.ApplicationLink, respClub.ApplicationLink)
- assert.Equal(dbClub.Logo, respClub.Logo)
+ eaa.Assert.Equal(dbClub.ID, respClub.ID)
+ eaa.Assert.Equal(dbClub.Name, respClub.Name)
+ eaa.Assert.Equal(dbClub.Preview, respClub.Preview)
+ eaa.Assert.Equal(dbClub.Description, respClub.Description)
+ eaa.Assert.Equal(dbClub.NumMembers, respClub.NumMembers)
+ eaa.Assert.Equal(dbClub.IsRecruiting, respClub.IsRecruiting)
+ eaa.Assert.Equal(dbClub.RecruitmentCycle, respClub.RecruitmentCycle)
+ eaa.Assert.Equal(dbClub.RecruitmentType, respClub.RecruitmentType)
+ eaa.Assert.Equal(dbClub.ApplicationLink, respClub.ApplicationLink)
+ eaa.Assert.Equal(dbClub.Logo, respClub.Logo)
var dbAdmins []models.User
- err = app.Conn.Model(&dbClub).Association("Admins").Find(&dbAdmins)
+ err = eaa.App.Conn.Model(&dbClub).Association("Admins").Find(&dbAdmins)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(1, len(dbAdmins))
+ eaa.Assert.Equal(1, len(dbAdmins))
dbAdmin := dbAdmins[0]
- assert.Equal((*body)["user_id"].(uuid.UUID), dbAdmin.ID)
- assert.Equal((*body)["name"].(string), dbClub.Name)
- assert.Equal((*body)["preview"].(string), dbClub.Preview)
- assert.Equal((*body)["description"].(string), dbClub.Description)
- assert.Equal((*body)["num_members"].(int), dbClub.NumMembers)
- assert.Equal((*body)["is_recruiting"].(bool), dbClub.IsRecruiting)
- assert.Equal((*body)["recruitment_cycle"].(string), dbClub.RecruitmentCycle)
- assert.Equal((*body)["recruitment_type"].(string), dbClub.RecruitmentType)
- assert.Equal((*body)["application_link"].(string), dbClub.ApplicationLink)
- assert.Equal((*body)["logo"].(string), dbClub.Logo)
+ eaa.Assert.Equal((*body)["user_id"].(uuid.UUID), dbAdmin.ID)
+ eaa.Assert.Equal((*body)["name"].(string), dbClub.Name)
+ eaa.Assert.Equal((*body)["preview"].(string), dbClub.Preview)
+ eaa.Assert.Equal((*body)["description"].(string), dbClub.Description)
+ eaa.Assert.Equal((*body)["num_members"].(int), dbClub.NumMembers)
+ eaa.Assert.Equal((*body)["is_recruiting"].(bool), dbClub.IsRecruiting)
+ eaa.Assert.Equal((*body)["recruitment_cycle"].(string), dbClub.RecruitmentCycle)
+ eaa.Assert.Equal((*body)["recruitment_type"].(string), dbClub.RecruitmentType)
+ eaa.Assert.Equal((*body)["application_link"].(string), dbClub.ApplicationLink)
+ eaa.Assert.Equal((*body)["logo"].(string), dbClub.Logo)
return dbClub.ID
}
-func AssertSampleClubBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, userID uuid.UUID) uuid.UUID {
+func AssertSampleClubBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response, userID uuid.UUID) uuid.UUID {
sampleClub := SampleClubFactory(&userID)
(*sampleClub)["num_members"] = 1
- return AssertClubBodyRespDB(app, assert, resp, sampleClub)
+ return AssertClubBodyRespDB(eaa, resp, sampleClub)
}
func CreateSampleClub(existingAppAssert h.ExistingAppAssert) (eaa h.ExistingAppAssert, studentUUID uuid.UUID, clubUUID uuid.UUID) {
var sampleClubUUID uuid.UUID
- newAppAssert := existingAppAssert.TestOnStatusAndDB(
+ newAppAssert := existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/clubs/",
@@ -147,8 +146,8 @@ func CreateSampleClub(existingAppAssert h.ExistingAppAssert) (eaa h.ExistingAppA
},
h.TesterWithStatus{
Status: fiber.StatusCreated,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
- sampleClubUUID = AssertSampleClubBodyRespDB(app, assert, resp, app.TestUser.UUID)
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ sampleClubUUID = AssertSampleClubBodyRespDB(eaa, resp, eaa.App.TestUser.UUID)
},
},
)
@@ -162,71 +161,71 @@ func TestCreateClubWorks(t *testing.T) {
}
func TestGetClubsWorks(t *testing.T) {
- h.InitTest(t).TestOnStatusAndDB(h.TestRequest{
+ h.InitTest(t).TestOnStatusAndTester(h.TestRequest{
Method: fiber.MethodGet,
Path: "/api/v1/clubs/",
Role: &models.Super,
},
h.TesterWithStatus{
Status: fiber.StatusOK,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var respClubs []models.Club
err := json.NewDecoder(resp.Body).Decode(&respClubs)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(1, len(respClubs))
+ eaa.Assert.Equal(1, len(respClubs))
respClub := respClubs[0]
var dbClubs []models.Club
- err = app.Conn.Order("created_at desc").Find(&dbClubs).Error
+ err = eaa.App.Conn.Order("created_at desc").Find(&dbClubs).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(1, len(dbClubs))
+ eaa.Assert.Equal(1, len(dbClubs))
dbClub := dbClubs[0]
- assert.Equal(dbClub.ID, respClub.ID)
- assert.Equal(dbClub.Name, respClub.Name)
- assert.Equal(dbClub.Preview, respClub.Preview)
- assert.Equal(dbClub.Description, respClub.Description)
- assert.Equal(dbClub.NumMembers, respClub.NumMembers)
- assert.Equal(dbClub.IsRecruiting, respClub.IsRecruiting)
- assert.Equal(dbClub.RecruitmentCycle, respClub.RecruitmentCycle)
- assert.Equal(dbClub.RecruitmentType, respClub.RecruitmentType)
- assert.Equal(dbClub.ApplicationLink, respClub.ApplicationLink)
- assert.Equal(dbClub.Logo, respClub.Logo)
-
- assert.Equal("SAC", dbClub.Name)
- assert.Equal("SAC", dbClub.Preview)
- assert.Equal("SAC", dbClub.Description)
- assert.Equal(1, dbClub.NumMembers)
- assert.Equal(true, dbClub.IsRecruiting)
- assert.Equal(models.Always, dbClub.RecruitmentCycle)
- assert.Equal(models.Application, dbClub.RecruitmentType)
- assert.Equal("https://generatenu.com/apply", dbClub.ApplicationLink)
- assert.Equal("https://aws.amazon.com/s3", dbClub.Logo)
+ eaa.Assert.Equal(dbClub.ID, respClub.ID)
+ eaa.Assert.Equal(dbClub.Name, respClub.Name)
+ eaa.Assert.Equal(dbClub.Preview, respClub.Preview)
+ eaa.Assert.Equal(dbClub.Description, respClub.Description)
+ eaa.Assert.Equal(dbClub.NumMembers, respClub.NumMembers)
+ eaa.Assert.Equal(dbClub.IsRecruiting, respClub.IsRecruiting)
+ eaa.Assert.Equal(dbClub.RecruitmentCycle, respClub.RecruitmentCycle)
+ eaa.Assert.Equal(dbClub.RecruitmentType, respClub.RecruitmentType)
+ eaa.Assert.Equal(dbClub.ApplicationLink, respClub.ApplicationLink)
+ eaa.Assert.Equal(dbClub.Logo, respClub.Logo)
+
+ eaa.Assert.Equal("SAC", dbClub.Name)
+ eaa.Assert.Equal("SAC", dbClub.Preview)
+ eaa.Assert.Equal("SAC", dbClub.Description)
+ eaa.Assert.Equal(1, dbClub.NumMembers)
+ eaa.Assert.Equal(true, dbClub.IsRecruiting)
+ eaa.Assert.Equal(models.Always, dbClub.RecruitmentCycle)
+ eaa.Assert.Equal(models.Application, dbClub.RecruitmentType)
+ eaa.Assert.Equal("https://generatenu.com/apply", dbClub.ApplicationLink)
+ eaa.Assert.Equal("https://aws.amazon.com/s3", dbClub.Logo)
},
},
).Close()
}
-func AssertNumClubsRemainsAtN(app h.TestApp, assert *assert.A, resp *http.Response, n int) {
+func AssertNumClubsRemainsAtN(eaa h.ExistingAppAssert, resp *http.Response, n int) {
var dbClubs []models.Club
- err := app.Conn.Order("created_at desc").Find(&dbClubs).Error
+ err := eaa.App.Conn.Order("created_at desc").Find(&dbClubs).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(n, len(dbClubs))
+ eaa.Assert.Equal(n, len(dbClubs))
}
-var TestNumClubsRemainsAt1 = func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertNumClubsRemainsAtN(app, assert, resp, 1)
+var TestNumClubsRemainsAt1 = func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertNumClubsRemainsAtN(eaa, resp, 1)
}
func AssertCreateBadClubDataFails(t *testing.T, jsonKey string, badValues []interface{}) {
@@ -315,7 +314,7 @@ func TestUpdateClubWorks(t *testing.T) {
(*updatedClub)["name"] = "Updated Name"
(*updatedClub)["preview"] = "Updated Preview"
- appAssert.TestOnStatusAndDB(
+ appAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPatch,
Path: fmt.Sprintf("/api/v1/clubs/%s", clubUUID),
@@ -324,8 +323,8 @@ func TestUpdateClubWorks(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusOK,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertClubBodyRespDB(app, assert, resp, updatedClub)
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertClubBodyRespDB(eaa, resp, updatedClub)
},
},
).Close()
@@ -353,34 +352,34 @@ func TestUpdateClubFailsOnInvalidBody(t *testing.T) {
},
h.ErrorWithTester{
Error: errors.FailedToValidateClub,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var dbClubs []models.Club
- err := app.Conn.Order("created_at desc").Find(&dbClubs).Error
+ err := eaa.App.Conn.Order("created_at desc").Find(&dbClubs).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(2, len(dbClubs))
+ eaa.Assert.Equal(2, len(dbClubs))
dbClub := dbClubs[0]
var dbAdmins []models.User
- err = app.Conn.Model(&dbClub).Association("Admin").Find(&dbAdmins)
+ err = eaa.App.Conn.Model(&dbClub).Association("Admin").Find(&dbAdmins)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(1, len(dbAdmins))
+ eaa.Assert.Equal(1, len(dbAdmins))
- assert.Equal(*(*body)["user_id"].(*uuid.UUID), dbAdmins[0].ID)
- assert.Equal((*body)["name"].(string), dbClub.Name)
- assert.Equal((*body)["preview"].(string), dbClub.Preview)
- assert.Equal((*body)["description"].(string), dbClub.Description)
- assert.Equal((*body)["is_recruiting"].(bool), dbClub.IsRecruiting)
- assert.Equal(models.RecruitmentCycle((*body)["recruitment_cycle"].(string)), dbClub.RecruitmentCycle)
- assert.Equal(models.RecruitmentType((*body)["recruitment_type"].(string)), dbClub.RecruitmentType)
- assert.Equal((*body)["application_link"].(string), dbClub.ApplicationLink)
- assert.Equal((*body)["logo"].(string), dbClub.Logo)
+ eaa.Assert.Equal(*(*body)["user_id"].(*uuid.UUID), dbAdmins[0].ID)
+ eaa.Assert.Equal((*body)["name"].(string), dbClub.Name)
+ eaa.Assert.Equal((*body)["preview"].(string), dbClub.Preview)
+ eaa.Assert.Equal((*body)["description"].(string), dbClub.Description)
+ eaa.Assert.Equal((*body)["is_recruiting"].(bool), dbClub.IsRecruiting)
+ eaa.Assert.Equal(models.RecruitmentCycle((*body)["recruitment_cycle"].(string)), dbClub.RecruitmentCycle)
+ eaa.Assert.Equal(models.RecruitmentType((*body)["recruitment_type"].(string)), dbClub.RecruitmentType)
+ eaa.Assert.Equal((*body)["application_link"].(string), dbClub.ApplicationLink)
+ eaa.Assert.Equal((*body)["logo"].(string), dbClub.Logo)
},
},
)
@@ -428,12 +427,12 @@ func TestUpdateClubFailsOnClubIdNotExist(t *testing.T) {
},
h.ErrorWithTester{
Error: errors.ClubNotFound,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var club models.Club
- err := app.Conn.Where("id = ?", uuid).First(&club).Error
+ err := eaa.App.Conn.Where("id = ?", uuid).First(&club).Error
- assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
+ eaa.Assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
},
},
).Close()
@@ -442,7 +441,7 @@ func TestUpdateClubFailsOnClubIdNotExist(t *testing.T) {
func TestDeleteClubWorks(t *testing.T) {
appAssert, _, clubUUID := CreateSampleClub(h.InitTest(t))
- appAssert.TestOnStatusAndDB(
+ appAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/clubs/%s", clubUUID),
@@ -465,14 +464,14 @@ func TestDeleteClubNotExist(t *testing.T) {
},
h.ErrorWithTester{
Error: errors.ClubNotFound,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var club models.Club
- err := app.Conn.Where("id = ?", uuid).First(&club).Error
+ err := eaa.App.Conn.Where("id = ?", uuid).First(&club).Error
- assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
+ eaa.Assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
- AssertNumClubsRemainsAtN(app, assert, resp, 1)
+ AssertNumClubsRemainsAtN(eaa, resp, 1)
},
},
).Close()
diff --git a/backend/tests/api/helpers/requests.go b/backend/tests/api/helpers/requests.go
index 3e76dc5bb..5920ec6fe 100644
--- a/backend/tests/api/helpers/requests.go
+++ b/backend/tests/api/helpers/requests.go
@@ -11,8 +11,6 @@ import (
"github.com/GenerateNU/sac/backend/src/models"
"github.com/goccy/go-json"
-
- "github.com/huandu/go-assert"
)
type TestRequest struct {
@@ -24,7 +22,6 @@ type TestRequest struct {
TestUserIDReplaces *string
}
-//gocyclo:ignore
func (app TestApp) Send(request TestRequest) (*http.Response, error) {
address := fmt.Sprintf("%s%s", app.Address, request.Path)
@@ -97,25 +94,24 @@ func (request TestRequest) test(existingAppAssert ExistingAppAssert) (ExistingAp
func (existingAppAssert ExistingAppAssert) TestOnStatus(request TestRequest, status int) ExistingAppAssert {
appAssert, resp := request.test(existingAppAssert)
- _, assert := appAssert.App, appAssert.Assert
-
- assert.Equal(status, resp.StatusCode)
+ appAssert.Assert.Equal(status, resp.StatusCode)
return appAssert
}
func (request *TestRequest) testOn(existingAppAssert ExistingAppAssert, status int, key string, value string) (ExistingAppAssert, *http.Response) {
appAssert, resp := request.test(existingAppAssert)
- assert := appAssert.Assert
var respBody map[string]interface{}
err := json.NewDecoder(resp.Body).Decode(&respBody)
- assert.NilError(err)
- assert.Equal(value, respBody[key].(string))
+ appAssert.Assert.NilError(err)
+
+ appAssert.Assert.Equal(value, respBody[key].(string))
+
+ appAssert.Assert.Equal(status, resp.StatusCode)
- assert.Equal(status, resp.StatusCode)
return appAssert, resp
}
@@ -129,9 +125,9 @@ type ErrorWithTester struct {
Tester Tester
}
-func (existingAppAssert ExistingAppAssert) TestOnErrorAndDB(request TestRequest, errorWithDBTester ErrorWithTester) ExistingAppAssert {
- appAssert, resp := request.testOn(existingAppAssert, errorWithDBTester.Error.StatusCode, "error", errorWithDBTester.Error.Message)
- errorWithDBTester.Tester(appAssert.App, appAssert.Assert, resp)
+func (existingAppAssert ExistingAppAssert) TestOnErrorAndDB(request TestRequest, errorWithTester ErrorWithTester) ExistingAppAssert {
+ appAssert, resp := request.testOn(existingAppAssert, errorWithTester.Error.StatusCode, "error", errorWithTester.Error.Message)
+ errorWithTester.Tester(appAssert, resp)
return appAssert
}
@@ -140,26 +136,25 @@ func (existingAppAssert ExistingAppAssert) TestOnMessage(request TestRequest, st
return existingAppAssert
}
-func (existingAppAssert ExistingAppAssert) TestOnMessageAndDB(request TestRequest, status int, message string, dbTester Tester) ExistingAppAssert {
+func (existingAppAssert ExistingAppAssert) TestOnMessageAndTester(request TestRequest, status int, message string, tester Tester) ExistingAppAssert {
appAssert, resp := request.testOn(existingAppAssert, status, "message", message)
- dbTester(appAssert.App, appAssert.Assert, resp)
+ tester(appAssert, resp)
return appAssert
}
-type Tester func(app TestApp, assert *assert.A, resp *http.Response)
+type Tester func(eaa ExistingAppAssert, resp *http.Response)
type TesterWithStatus struct {
Status int
Tester
}
-func (existingAppAssert ExistingAppAssert) TestOnStatusAndDB(request TestRequest, testerStatus TesterWithStatus) ExistingAppAssert {
+func (existingAppAssert ExistingAppAssert) TestOnStatusAndTester(request TestRequest, testerStatus TesterWithStatus) ExistingAppAssert {
appAssert, resp := request.test(existingAppAssert)
- app, assert := appAssert.App, appAssert.Assert
- assert.Equal(testerStatus.Status, resp.StatusCode)
+ appAssert.Assert.Equal(testerStatus.Status, resp.StatusCode)
- testerStatus.Tester(app, assert, resp)
+ testerStatus.Tester(appAssert, resp)
return appAssert
}
diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go
index f61cbe7d0..3004c459f 100644
--- a/backend/tests/api/tag_test.go
+++ b/backend/tests/api/tag_test.go
@@ -10,7 +10,6 @@ import (
h "github.com/GenerateNU/sac/backend/tests/api/helpers"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
- "github.com/huandu/go-assert"
"github.com/goccy/go-json"
)
@@ -22,46 +21,42 @@ func SampleTagFactory(categoryID uuid.UUID) *map[string]interface{} {
}
}
-func AssertTagWithBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
+func AssertTagWithBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response, body *map[string]interface{}) uuid.UUID {
var respTag models.Tag
err := json.NewDecoder(resp.Body).Decode(&respTag)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
var dbTag models.Tag
- err = app.Conn.First(&dbTag).Error
+ err = eaa.App.Conn.First(&dbTag).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(dbTag.ID, respTag.ID)
- assert.Equal(dbTag.Name, respTag.Name)
- assert.Equal(dbTag.CategoryID, respTag.CategoryID)
+ eaa.Assert.Equal(dbTag.ID, respTag.ID)
+ eaa.Assert.Equal(dbTag.Name, respTag.Name)
+ eaa.Assert.Equal(dbTag.CategoryID, respTag.CategoryID)
- assert.Equal((*body)["name"].(string), dbTag.Name)
- assert.Equal((*body)["category_id"].(uuid.UUID), dbTag.CategoryID)
+ eaa.Assert.Equal((*body)["name"].(string), dbTag.Name)
+ eaa.Assert.Equal((*body)["category_id"].(uuid.UUID), dbTag.CategoryID)
return dbTag.ID
}
-func AssertSampleTagBodyRespDB(t *testing.T, app h.TestApp, assert *assert.A, resp *http.Response) uuid.UUID {
- appAssert, uuid := CreateSampleCategory(
- h.ExistingAppAssert{
- App: app,
- Assert: assert,
- })
- return AssertTagWithBodyRespDB(appAssert.App, appAssert.Assert, resp, SampleTagFactory(uuid))
+func AssertSampleTagBodyRespDB(t *testing.T, eaa h.ExistingAppAssert, resp *http.Response) uuid.UUID {
+ appAssert, uuid := CreateSampleCategory(eaa)
+ return AssertTagWithBodyRespDB(appAssert, resp, SampleTagFactory(uuid))
}
func CreateSampleTag(appAssert h.ExistingAppAssert) (existingAppAssert h.ExistingAppAssert, categoryUUID uuid.UUID, tagUUID uuid.UUID) {
appAssert, categoryUUID = CreateSampleCategory(appAssert)
- AssertSampleTagBodyRespDB := func(app h.TestApp, assert *assert.A, resp *http.Response) {
- tagUUID = AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory(categoryUUID))
+ AssertSampleTagBodyRespDB := func(eaa h.ExistingAppAssert, resp *http.Response) {
+ tagUUID = AssertTagWithBodyRespDB(appAssert, resp, SampleTagFactory(categoryUUID))
}
- appAssert.TestOnStatusAndDB(
+ appAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/tags/",
@@ -82,22 +77,22 @@ func TestCreateTagWorks(t *testing.T) {
appAssert.Close()
}
-func AssertNumTagsRemainsAtN(app h.TestApp, assert *assert.A, resp *http.Response, n int) {
+func AssertNumTagsRemainsAtN(eaa h.ExistingAppAssert, resp *http.Response, n int) {
var tags []models.Tag
- err := app.Conn.Find(&tags).Error
+ err := eaa.App.Conn.Find(&tags).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(n, len(tags))
+ eaa.Assert.Equal(n, len(tags))
}
-func AssertNoTags(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertNumTagsRemainsAtN(app, assert, resp, 0)
+func AssertNoTags(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertNumTagsRemainsAtN(eaa, resp, 0)
}
-func Assert1Tag(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertNumTagsRemainsAtN(app, assert, resp, 1)
+func Assert1Tag(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertNumTagsRemainsAtN(eaa, resp, 1)
}
func TestCreateTagFailsBadRequest(t *testing.T) {
@@ -168,7 +163,7 @@ func TestCreateTagFailsValidation(t *testing.T) {
func TestGetTagWorks(t *testing.T) {
existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(h.InitTest(t))
- existingAppAssert.TestOnStatusAndDB(
+ existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodGet,
Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
@@ -176,8 +171,8 @@ func TestGetTagWorks(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusOK,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory(categoryUUID))
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertTagWithBodyRespDB(eaa, resp, SampleTagFactory(categoryUUID))
},
},
).Close()
@@ -225,11 +220,11 @@ func TestUpdateTagWorksUpdateName(t *testing.T) {
generateNUTag := *SampleTagFactory(categoryUUID)
generateNUTag["name"] = "GenerateNU"
- AssertUpdatedTagBodyRespDB := func(app h.TestApp, assert *assert.A, resp *http.Response) {
- tagUUID = AssertTagWithBodyRespDB(app, assert, resp, &generateNUTag)
+ AssertUpdatedTagBodyRespDB := func(eaa h.ExistingAppAssert, resp *http.Response) {
+ tagUUID = AssertTagWithBodyRespDB(eaa, resp, &generateNUTag)
}
- existingAppAssert.TestOnStatusAndDB(
+ existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPatch,
Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
@@ -251,11 +246,11 @@ func TestUpdateTagWorksUpdateCategory(t *testing.T) {
var technologyCategoryUUID uuid.UUID
- AssertNewCategoryBodyRespDB := func(app h.TestApp, assert *assert.A, resp *http.Response) {
- technologyCategoryUUID = AssertCategoryWithBodyRespDBMostRecent(app, assert, resp, &technologyCategory)
+ AssertNewCategoryBodyRespDB := func(eaa h.ExistingAppAssert, resp *http.Response) {
+ technologyCategoryUUID = AssertCategoryWithBodyRespDBMostRecent(eaa, resp, &technologyCategory)
}
- existingAppAssert.TestOnStatusAndDB(
+ existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/categories/",
@@ -270,11 +265,11 @@ func TestUpdateTagWorksUpdateCategory(t *testing.T) {
technologyTag := *SampleTagFactory(technologyCategoryUUID)
- AssertUpdatedTagBodyRespDB := func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertTagWithBodyRespDB(app, assert, resp, &technologyTag)
+ AssertUpdatedTagBodyRespDB := func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertTagWithBodyRespDB(eaa, resp, &technologyTag)
}
- existingAppAssert.TestOnStatusAndDB(
+ existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPatch,
Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
@@ -291,7 +286,7 @@ func TestUpdateTagWorksUpdateCategory(t *testing.T) {
func TestUpdateTagWorksWithSameDetails(t *testing.T) {
existingAppAssert, categoryUUID, tagUUID := CreateSampleTag(h.InitTest(t))
- existingAppAssert.TestOnStatusAndDB(
+ existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPatch,
Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
@@ -300,8 +295,8 @@ func TestUpdateTagWorksWithSameDetails(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusOK,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertTagWithBodyRespDB(app, assert, resp, SampleTagFactory(categoryUUID))
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertTagWithBodyRespDB(eaa, resp, SampleTagFactory(categoryUUID))
},
},
).Close()
@@ -336,7 +331,7 @@ func TestUpdateTagFailsBadRequest(t *testing.T) {
func TestDeleteTagWorks(t *testing.T) {
existingAppAssert, _, tagUUID := CreateSampleTag(h.InitTest(t))
- existingAppAssert.TestOnStatusAndDB(
+ existingAppAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/tags/%s", tagUUID),
diff --git a/backend/tests/api/user_tag_test.go b/backend/tests/api/user_tag_test.go
index 9d8a2ecc4..77944020d 100644
--- a/backend/tests/api/user_tag_test.go
+++ b/backend/tests/api/user_tag_test.go
@@ -11,7 +11,6 @@ import (
"github.com/goccy/go-json"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
- "github.com/huandu/go-assert"
)
func SampleCategoriesFactory() *[]map[string]interface{} {
@@ -67,7 +66,7 @@ func CreateSetOfTags(t *testing.T, appAssert *h.ExistingAppAssert) ([]uuid.UUID,
categoryIDs := []uuid.UUID{}
for _, category := range *categories {
category := category
- appAssert.TestOnStatusAndDB(
+ appAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/categories/",
@@ -76,12 +75,12 @@ func CreateSetOfTags(t *testing.T, appAssert *h.ExistingAppAssert) ([]uuid.UUID,
},
h.TesterWithStatus{
Status: fiber.StatusCreated,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var respCategory models.Category
err := json.NewDecoder(resp.Body).Decode(&respCategory)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
categoryIDs = append(categoryIDs, respCategory.ID)
},
@@ -94,7 +93,7 @@ func CreateSetOfTags(t *testing.T, appAssert *h.ExistingAppAssert) ([]uuid.UUID,
tagIDs := []uuid.UUID{}
for _, tag := range *tags {
tag := tag
- appAssert.TestOnStatusAndDB(
+ appAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/tags/",
@@ -103,12 +102,12 @@ func CreateSetOfTags(t *testing.T, appAssert *h.ExistingAppAssert) ([]uuid.UUID,
},
h.TesterWithStatus{
Status: fiber.StatusCreated,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var respTag models.Tag
err := json.NewDecoder(resp.Body).Decode(&respTag)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
tagIDs = append(tagIDs, respTag.ID)
},
@@ -119,36 +118,36 @@ func CreateSetOfTags(t *testing.T, appAssert *h.ExistingAppAssert) ([]uuid.UUID,
return tagIDs, appAssert
}
-func AssertUserTagsRespDB(app h.TestApp, assert *assert.A, resp *http.Response, id uuid.UUID) {
+func AssertUserTagsRespDB(eaa h.ExistingAppAssert, resp *http.Response, id uuid.UUID) {
var respTags []models.Tag
// Retrieve the tags from the response:
err := json.NewDecoder(resp.Body).Decode(&respTags)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
// Retrieve the user connected to the tags:
var dbUser models.User
- err = app.Conn.First(&dbUser, id).Error
+ err = eaa.App.Conn.First(&dbUser, id).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
// Retrieve the tags in the bridge table associated with the user:
var dbTags []models.Tag
- err = app.Conn.Model(&dbUser).Association("Tag").Find(&dbTags)
+ err = eaa.App.Conn.Model(&dbUser).Association("Tag").Find(&dbTags)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
// Confirm all the resp tags are equal to the db tags:
for i, respTag := range respTags {
- assert.Equal(respTag.ID, dbTags[i].ID)
- assert.Equal(respTag.Name, dbTags[i].Name)
- assert.Equal(respTag.CategoryID, dbTags[i].CategoryID)
+ eaa.Assert.Equal(respTag.ID, dbTags[i].ID)
+ eaa.Assert.Equal(respTag.Name, dbTags[i].Name)
+ eaa.Assert.Equal(respTag.CategoryID, dbTags[i].CategoryID)
}
}
-func AssertSampleUserTagsRespDB(app h.TestApp, assert *assert.A, resp *http.Response, uuid uuid.UUID) {
- AssertUserTagsRespDB(app, assert, resp, uuid)
+func AssertSampleUserTagsRespDB(eaa h.ExistingAppAssert, resp *http.Response, uuid uuid.UUID) {
+ AssertUserTagsRespDB(eaa, resp, uuid)
}
func TestCreateUserTagsFailsOnInvalidDataType(t *testing.T) {
@@ -240,11 +239,11 @@ func TestCreateUserTagsFailsOnNonExistentUser(t *testing.T) {
},
h.ErrorWithTester{
Error: errors.UserNotFound,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var dbUser models.User
- err := app.Conn.First(&dbUser, uuid).Error
+ err := eaa.App.Conn.First(&dbUser, uuid).Error
- assert.Assert(err != nil)
+ eaa.Assert.Assert(err != nil)
},
},
).Close()
@@ -255,7 +254,7 @@ func TestCreateUserTagsWorks(t *testing.T) {
tagUUIDs, appAssert := CreateSetOfTags(t, nil)
// Confirm adding real tags adds them to the user:
- appAssert.TestOnStatusAndDB(
+ appAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/users/:userID/tags/",
@@ -265,8 +264,8 @@ func TestCreateUserTagsWorks(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusCreated,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertSampleUserTagsRespDB(app, assert, resp, app.TestUser.UUID)
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertSampleUserTagsRespDB(eaa, resp, eaa.App.TestUser.UUID)
},
},
)
@@ -275,7 +274,7 @@ func TestCreateUserTagsWorks(t *testing.T) {
}
func TestCreateUserTagsNoneAddedIfInvalid(t *testing.T) {
- h.InitTest(t).TestOnStatusAndDB(
+ h.InitTest(t).TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/users/:userID/tags/",
@@ -285,14 +284,14 @@ func TestCreateUserTagsNoneAddedIfInvalid(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusCreated,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var respTags []models.Tag
err := json.NewDecoder(resp.Body).Decode(&respTags)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(len(respTags), 0)
+ eaa.Assert.Equal(len(respTags), 0)
},
},
).Close()
@@ -309,7 +308,7 @@ func TestGetUserTagsFailsOnNonExistentUser(t *testing.T) {
}
func TestGetUserTagsReturnsEmptyListWhenNoneAdded(t *testing.T) {
- h.InitTest(t).TestOnStatusAndDB(
+ h.InitTest(t).TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodGet,
Path: "/api/v1/users/:userID/tags/",
@@ -318,14 +317,14 @@ func TestGetUserTagsReturnsEmptyListWhenNoneAdded(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusOK,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var respTags []models.Tag
err := json.NewDecoder(resp.Body).Decode(&respTags)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(len(respTags), 0)
+ eaa.Assert.Equal(len(respTags), 0)
},
},
).Close()
@@ -345,7 +344,7 @@ func TestGetUserTagsReturnsCorrectList(t *testing.T) {
TestUserIDReplaces: h.StringToPointer(":userID"),
},
fiber.StatusCreated,
- ).TestOnStatusAndDB(
+ ).TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodGet,
Path: "/api/v1/users/:userID/tags/",
@@ -354,8 +353,8 @@ func TestGetUserTagsReturnsCorrectList(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusOK,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertSampleUserTagsRespDB(app, assert, resp, app.TestUser.UUID)
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertSampleUserTagsRespDB(eaa, resp, eaa.App.TestUser.UUID)
},
},
).Close()
diff --git a/backend/tests/api/user_test.go b/backend/tests/api/user_test.go
index 66bd629c4..7213d247a 100644
--- a/backend/tests/api/user_test.go
+++ b/backend/tests/api/user_test.go
@@ -17,11 +17,10 @@ import (
"gorm.io/gorm"
"github.com/goccy/go-json"
- "github.com/huandu/go-assert"
)
func TestGetUsersWorksForSuper(t *testing.T) {
- h.InitTest(t).TestOnStatusAndDB(
+ h.InitTest(t).TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodGet,
Path: "/api/v1/users/",
@@ -29,33 +28,33 @@ func TestGetUsersWorksForSuper(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusOK,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var users []models.User
err := json.NewDecoder(resp.Body).Decode(&users)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(1, len(users))
+ eaa.Assert.Equal(1, len(users))
respUser := users[0]
- assert.Equal("SAC", respUser.FirstName)
- assert.Equal("Super", respUser.LastName)
- assert.Equal("generatesac@gmail.com", respUser.Email)
- assert.Equal("000000000", respUser.NUID)
- assert.Equal(models.College("KCCS"), respUser.College)
- assert.Equal(models.Year(1), respUser.Year)
+ eaa.Assert.Equal("SAC", respUser.FirstName)
+ eaa.Assert.Equal("Super", respUser.LastName)
+ eaa.Assert.Equal("generatesac@gmail.com", respUser.Email)
+ eaa.Assert.Equal("000000000", respUser.NUID)
+ eaa.Assert.Equal(models.College("KCCS"), respUser.College)
+ eaa.Assert.Equal(models.Year(1), respUser.Year)
- dbUsers, err := transactions.GetUsers(app.Conn, 1, 0)
+ dbUsers, err := transactions.GetUsers(eaa.App.Conn, 1, 0)
- assert.NilError(&err)
+ eaa.Assert.NilError(&err)
- assert.Equal(1, len(dbUsers))
+ eaa.Assert.Equal(1, len(dbUsers))
dbUser := dbUsers[0]
- assert.Equal(dbUser, respUser)
+ eaa.Assert.Equal(dbUser, respUser)
},
},
).Close()
@@ -73,7 +72,7 @@ func TestGetUsersFailsForStudent(t *testing.T) {
}
func TestGetUserWorks(t *testing.T) {
- h.InitTest(t).TestOnStatusAndDB(
+ h.InitTest(t).TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodGet,
Path: "/api/v1/users/:userID",
@@ -82,29 +81,29 @@ func TestGetUserWorks(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusOK,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var respUser models.User
err := json.NewDecoder(resp.Body).Decode(&respUser)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
sampleStudent, rawPassword := h.SampleStudentFactory()
sampleUser := *h.SampleStudentJSONFactory(sampleStudent, rawPassword)
- assert.Equal(sampleUser["first_name"].(string), respUser.FirstName)
- assert.Equal(sampleUser["last_name"].(string), respUser.LastName)
- assert.Equal(sampleUser["email"].(string), respUser.Email)
- assert.Equal(sampleUser["nuid"].(string), respUser.NUID)
- assert.Equal(models.College(sampleUser["college"].(string)), respUser.College)
- assert.Equal(models.Year(sampleUser["year"].(int)), respUser.Year)
+ 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(sampleUser["nuid"].(string), respUser.NUID)
+ eaa.Assert.Equal(models.College(sampleUser["college"].(string)), respUser.College)
+ eaa.Assert.Equal(models.Year(sampleUser["year"].(int)), respUser.Year)
- dbUser, err := transactions.GetUser(app.Conn, app.TestUser.UUID)
+ dbUser, err := transactions.GetUser(eaa.App.Conn, eaa.App.TestUser.UUID)
- assert.NilError(&err)
+ eaa.Assert.NilError(&err)
- assert.Equal(dbUser, &respUser)
+ eaa.Assert.Equal(dbUser, &respUser)
},
},
).Close()
@@ -146,12 +145,12 @@ func TestGetUserFailsNotExist(t *testing.T) {
},
h.ErrorWithTester{
Error: errors.UserNotFound,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var user models.User
- err := app.Conn.Where("id = ?", uuid).First(&user).Error
+ err := eaa.App.Conn.Where("id = ?", uuid).First(&user).Error
- assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
+ eaa.Assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
},
},
).Close()
@@ -161,7 +160,7 @@ func TestUpdateUserWorks(t *testing.T) {
newFirstName := "Michael"
newLastName := "Brennan"
- h.InitTest(t).TestOnStatusAndDB(
+ h.InitTest(t).TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPatch,
Path: "/api/v1/users/:userID",
@@ -174,36 +173,36 @@ func TestUpdateUserWorks(t *testing.T) {
},
h.TesterWithStatus{
Status: fiber.StatusOK,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var respUser models.User
err := json.NewDecoder(resp.Body).Decode(&respUser)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
sampleStudent, rawPassword := h.SampleStudentFactory()
sampleStudentJSON := *h.SampleStudentJSONFactory(sampleStudent, rawPassword)
- assert.Equal(newFirstName, respUser.FirstName)
- assert.Equal(newLastName, respUser.LastName)
- assert.Equal((sampleStudentJSON)["email"].(string), respUser.Email)
- assert.Equal((sampleStudentJSON)["nuid"].(string), respUser.NUID)
- assert.Equal(models.College((sampleStudentJSON)["college"].(string)), respUser.College)
- assert.Equal(models.Year((sampleStudentJSON)["year"].(int)), respUser.Year)
+ eaa.Assert.Equal(newFirstName, respUser.FirstName)
+ eaa.Assert.Equal(newLastName, respUser.LastName)
+ eaa.Assert.Equal((sampleStudentJSON)["email"].(string), respUser.Email)
+ eaa.Assert.Equal((sampleStudentJSON)["nuid"].(string), respUser.NUID)
+ eaa.Assert.Equal(models.College((sampleStudentJSON)["college"].(string)), respUser.College)
+ eaa.Assert.Equal(models.Year((sampleStudentJSON)["year"].(int)), respUser.Year)
var dbUser models.User
- err = app.Conn.First(&dbUser, app.TestUser.UUID).Error
+ err = eaa.App.Conn.First(&dbUser, eaa.App.TestUser.UUID).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(dbUser.FirstName, respUser.FirstName)
- assert.Equal(dbUser.LastName, respUser.LastName)
- assert.Equal(dbUser.Email, respUser.Email)
- assert.Equal(dbUser.NUID, respUser.NUID)
- assert.Equal(dbUser.College, respUser.College)
- assert.Equal(dbUser.Year, respUser.Year)
+ 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.NUID, respUser.NUID)
+ eaa.Assert.Equal(dbUser.College, respUser.College)
+ eaa.Assert.Equal(dbUser.Year, respUser.Year)
},
},
).Close()
@@ -272,19 +271,19 @@ func TestUpdateUserFailsOnIdNotExist(t *testing.T) {
},
h.ErrorWithTester{
Error: errors.UserNotFound,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var user models.User
- err := app.Conn.Where("id = ?", uuid).First(&user).Error
+ err := eaa.App.Conn.Where("id = ?", uuid).First(&user).Error
- assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
+ eaa.Assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
},
},
).Close()
}
func TestDeleteUserWorks(t *testing.T) {
- h.InitTest(t).TestOnStatusAndDB(
+ h.InitTest(t).TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: "/api/v1/users/:userID",
@@ -308,14 +307,14 @@ func TestDeleteUserNotExist(t *testing.T) {
},
h.ErrorWithTester{
Error: errors.UserNotFound,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var user models.User
- err := app.Conn.Where("id = ?", uuid).First(&user).Error
+ err := eaa.App.Conn.Where("id = ?", uuid).First(&user).Error
- assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
+ eaa.Assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
- TestNumUsersRemainsAt1(app, assert, resp)
+ TestNumUsersRemainsAt1(eaa, resp)
},
},
).Close()
@@ -349,50 +348,50 @@ func TestDeleteUserBadRequest(t *testing.T) {
appAssert.Close()
}
-func AssertUserWithIDBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response, body *map[string]interface{}) uuid.UUID {
+func AssertUserWithIDBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response, body *map[string]interface{}) uuid.UUID {
var respUser models.User
err := json.NewDecoder(resp.Body).Decode(&respUser)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
var dbUsers []models.User
- err = app.Conn.Find(&dbUsers).Error
+ err = eaa.App.Conn.Find(&dbUsers).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(2, len(dbUsers))
+ eaa.Assert.Equal(2, len(dbUsers))
dbUser := dbUsers[1]
- assert.Equal(dbUser.FirstName, respUser.FirstName)
- assert.Equal(dbUser.LastName, respUser.LastName)
- assert.Equal(dbUser.Email, respUser.Email)
- assert.Equal(dbUser.NUID, respUser.NUID)
- assert.Equal(dbUser.College, respUser.College)
- assert.Equal(dbUser.Year, respUser.Year)
+ 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.NUID, respUser.NUID)
+ eaa.Assert.Equal(dbUser.College, respUser.College)
+ eaa.Assert.Equal(dbUser.Year, respUser.Year)
match, err := auth.ComparePasswordAndHash((*body)["password"].(string), dbUser.PasswordHash)
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Assert(match)
+ eaa.Assert.Assert(match)
- assert.Equal((*body)["first_name"].(string), dbUser.FirstName)
- assert.Equal((*body)["last_name"].(string), dbUser.LastName)
- assert.Equal((*body)["email"].(string), dbUser.Email)
- assert.Equal((*body)["nuid"].(string), dbUser.NUID)
- assert.Equal(models.College((*body)["college"].(string)), dbUser.College)
- assert.Equal(models.Year((*body)["year"].(int)), dbUser.Year)
+ 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((*body)["nuid"].(string), dbUser.NUID)
+ eaa.Assert.Equal(models.College((*body)["college"].(string)), dbUser.College)
+ eaa.Assert.Equal(models.Year((*body)["year"].(int)), dbUser.Year)
return dbUser.ID
}
-func AssertSampleUserBodyRespDB(app h.TestApp, assert *assert.A, resp *http.Response) uuid.UUID {
+func AssertSampleUserBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response) uuid.UUID {
sampleStudent, rawPassword := h.SampleStudentFactory()
- return AssertUserWithIDBodyRespDB(app, assert, resp, h.SampleStudentJSONFactory(sampleStudent, rawPassword))
+ return AssertUserWithIDBodyRespDB(eaa, resp, h.SampleStudentJSONFactory(sampleStudent, rawPassword))
}
func CreateSampleStudent(t *testing.T, existingAppAssert *h.ExistingAppAssert) (h.ExistingAppAssert, uuid.UUID, *map[string]interface{}) {
@@ -405,7 +404,7 @@ func CreateSampleStudent(t *testing.T, existingAppAssert *h.ExistingAppAssert) (
sampleStudent, rawPassword := h.SampleStudentFactory()
- existingAppAssert.TestOnStatusAndDB(h.TestRequest{
+ existingAppAssert.TestOnStatusAndTester(h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/users/",
Body: h.SampleStudentJSONFactory(sampleStudent, rawPassword),
@@ -413,8 +412,8 @@ func CreateSampleStudent(t *testing.T, existingAppAssert *h.ExistingAppAssert) (
},
h.TesterWithStatus{
Status: fiber.StatusCreated,
- Tester: func(app h.TestApp, assert *assert.A, resp *http.Response) {
- uuid = AssertSampleUserBodyRespDB(app, assert, resp)
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ uuid = AssertSampleUserBodyRespDB(eaa, resp)
},
},
)
@@ -422,22 +421,22 @@ func CreateSampleStudent(t *testing.T, existingAppAssert *h.ExistingAppAssert) (
return *existingAppAssert, uuid, h.SampleStudentJSONFactory(sampleStudent, rawPassword)
}
-func AssertNumUsersRemainsAtN(app h.TestApp, assert *assert.A, resp *http.Response, n int) {
+func AssertNumUsersRemainsAtN(eaa h.ExistingAppAssert, resp *http.Response, n int) {
var users []models.User
- err := app.Conn.Find(&users).Error
+ err := eaa.App.Conn.Find(&users).Error
- assert.NilError(err)
+ eaa.Assert.NilError(err)
- assert.Equal(n, len(users))
+ eaa.Assert.Equal(n, len(users))
}
-var TestNumUsersRemainsAt1 = func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertNumUsersRemainsAtN(app, assert, resp, 1)
+var TestNumUsersRemainsAt1 = func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertNumUsersRemainsAtN(eaa, resp, 1)
}
-var TestNumUsersRemainsAt2 = func(app h.TestApp, assert *assert.A, resp *http.Response) {
- AssertNumUsersRemainsAtN(app, assert, resp, 2)
+var TestNumUsersRemainsAt2 = func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertNumUsersRemainsAtN(eaa, resp, 2)
}
func TestCreateUserWorks(t *testing.T) {
From fb0f713dea7c96099fec694fa456a5affd024f4c Mon Sep 17 00:00:00 2001
From: Olivier John Nzia <116421013+ojn03@users.noreply.github.com>
Date: Wed, 7 Feb 2024 20:30:34 -0500
Subject: [PATCH 27/33] Sac cli (#171)
---
cli/main.go | 2 --
1 file changed, 2 deletions(-)
diff --git a/cli/main.go b/cli/main.go
index 7ed2f9a0b..6967add90 100755
--- a/cli/main.go
+++ b/cli/main.go
@@ -19,8 +19,6 @@ func main() {
commands.ResetCommand(),
commands.InsertCommand(),
commands.DropCommand(),
- commands.ResetCommand(),
- commands.DropCommand(),
commands.RunBackendCommand(),
commands.TestCommand(), // TODO: frontend tests
commands.FormatCommand(), // TODO: frontend format
From c7c93bbd1bb72648f22de1dd52a1b3fceb912b7b Mon Sep 17 00:00:00 2001
From: David Oduneye <44040421+DOOduneye@users.noreply.github.com>
Date: Thu, 8 Feb 2024 10:18:00 -0500
Subject: [PATCH 28/33] fix: error handling (#176)
---
backend/src/main.go | 4 ++--
go.work.sum | 1 -
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/backend/src/main.go b/backend/src/main.go
index ca83de46c..4274c05ec 100644
--- a/backend/src/main.go
+++ b/backend/src/main.go
@@ -30,13 +30,13 @@ func main() {
err = database.ConnPooling(db)
if err != nil {
- panic(err)
+ panic(fmt.Sprintf("Error connecting to database: %s", err.Error()))
}
app := server.Init(db, *config)
err = app.Listen(fmt.Sprintf("%s:%d", config.Application.Host, config.Application.Port))
if err != nil {
- panic(err)
+ panic(fmt.Sprintf("Error starting server: %s", err.Error()))
}
}
diff --git a/go.work.sum b/go.work.sum
index 52c5208f1..344c08ef1 100644
--- a/go.work.sum
+++ b/go.work.sum
@@ -13,7 +13,6 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
-github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
From 9a5f1f8b651363066be58ca3e3a82f4765f29e95 Mon Sep 17 00:00:00 2001
From: David Oduneye <44040421+DOOduneye@users.noreply.github.com>
Date: Fri, 9 Feb 2024 14:30:06 -0500
Subject: [PATCH 29/33] Patch go mod (#179)
---
CONTRIBUTING.md | 1 +
backend/go.mod | 36 ++++++++++------------
backend/go.sum | 31 ++++++++++---------
cli/go.mod | 33 ++++++++++++++++++--
cli/go.sum | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
go.work | 4 +--
go.work.sum | 11 +++++--
install.sh | 29 ++++++++++--------
8 files changed, 169 insertions(+), 56 deletions(-)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6be217e80..7b2f62a13 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -11,6 +11,7 @@
- [Node.js](https://nodejs.org/en/)
- [Yarn](https://yarnpkg.com/)
- [Go](https://golang.org/)
+ > Do not install through brew, use the official website
- [Docker](https://www.docker.com/)
- [PostgreSQL](https://www.postgresql.org/)
- Install through brew: `brew install postgresql@15`
diff --git a/backend/go.mod b/backend/go.mod
index 6b0923594..cecc317a1 100644
--- a/backend/go.mod
+++ b/backend/go.mod
@@ -1,21 +1,24 @@
module github.com/GenerateNU/sac/backend
-go 1.21.6
+go 1.22.0
require (
+ github.com/garrettladley/mattress v0.2.2
github.com/go-playground/validator/v10 v10.17.0
+ github.com/goccy/go-json v0.10.2
github.com/gofiber/fiber/v2 v2.52.0
github.com/gofiber/swagger v1.0.0
github.com/golang-jwt/jwt v3.2.2+incompatible
+ github.com/google/uuid v1.6.0
+ github.com/huandu/go-assert v1.1.6
+ github.com/mcnijman/go-emailaddress v1.1.1
+ github.com/mitchellh/mapstructure v1.5.0
github.com/spf13/viper v1.18.2
github.com/swaggo/swag v1.16.3
- gorm.io/driver/postgres v1.5.4
- gorm.io/gorm v1.25.6
-)
-
-require (
- github.com/awnumar/memcall v0.2.0 // indirect
- github.com/awnumar/memguard v0.22.4 // indirect
+ golang.org/x/crypto v0.19.0
+ golang.org/x/text v0.14.0
+ gorm.io/driver/postgres v1.5.6
+ gorm.io/gorm v1.25.7
)
require (
@@ -23,20 +26,18 @@ require (
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/andybalholm/brotli v1.0.5 // indirect
+ github.com/awnumar/memcall v0.2.0 // indirect
+ github.com/awnumar/memguard v0.22.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
- github.com/garrettladley/mattress v0.2.0
github.com/go-openapi/jsonpointer v0.19.5 // indirect
github.com/go-openapi/jsonreference v0.19.6 // indirect
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-openapi/swag v0.19.15 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
- github.com/goccy/go-json v0.10.2
- github.com/google/uuid v1.6.0
github.com/hashicorp/hcl v1.0.0 // indirect
- github.com/huandu/go-assert v1.1.6
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/pgx/v5 v5.4.3 // indirect
@@ -46,15 +47,12 @@ require (
github.com/klauspost/compress v1.17.0 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/magiconair/properties v1.8.7 // indirect
- github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/mailru/easyjson v0.7.6 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
- github.com/mcnijman/go-emailaddress v1.1.1
- github.com/mitchellh/mapstructure v1.5.0
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
- github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
@@ -68,11 +66,9 @@ require (
github.com/valyala/tcplisten v1.0.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
- golang.org/x/crypto v0.18.0
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
- golang.org/x/net v0.20.0 // indirect
- golang.org/x/sys v0.16.0 // indirect
- golang.org/x/text v0.14.0
+ golang.org/x/net v0.19.0 // indirect
+ golang.org/x/sys v0.17.0 // indirect
golang.org/x/tools v0.13.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
diff --git a/backend/go.sum b/backend/go.sum
index 496556c70..7212a934f 100644
--- a/backend/go.sum
+++ b/backend/go.sum
@@ -21,8 +21,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
-github.com/garrettladley/mattress v0.2.0 h1:+XUdsv9NO2s4JL+8exvAFziw0b1kv/0WlQo2Dlxat+w=
-github.com/garrettladley/mattress v0.2.0/go.mod h1:OWKIRc9wC3gtD3Ng/nUuNEiR1TJvRYLmn/KZYw9nl5Q=
+github.com/garrettladley/mattress v0.2.2 h1:kL/AvDmas6DaAweHwdsm3IosY9zJIGj2uO+byxzhyrU=
+github.com/garrettladley/mattress v0.2.2/go.mod h1:OWKIRc9wC3gtD3Ng/nUuNEiR1TJvRYLmn/KZYw9nl5Q=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -84,9 +84,8 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
-github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
@@ -106,8 +105,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
-github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
@@ -149,23 +148,23 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
-golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
-golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
-golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
-golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
+golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
-golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
@@ -188,7 +187,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo=
-gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0=
-gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A=
-gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
+gorm.io/driver/postgres v1.5.6 h1:ydr9xEd5YAM0vxVDY0X139dyzNz10spDiDlC7+ibLeU=
+gorm.io/driver/postgres v1.5.6/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA=
+gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A=
+gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
diff --git a/cli/go.mod b/cli/go.mod
index 279a6c129..ef0743b53 100644
--- a/cli/go.mod
+++ b/cli/go.mod
@@ -1,12 +1,39 @@
module github.com/GenerateNU/sac/cli
-go 1.21.1
+go 1.22.0
-require github.com/urfave/cli/v2 v2.27.1
+require (
+ github.com/GenerateNU/sac/backend v0.0.0-20240208151800-c7c93bbd1bb7
+ github.com/lib/pq v1.10.9
+ github.com/urfave/cli/v2 v2.27.1
+)
require (
+ github.com/awnumar/memcall v0.2.0 // indirect
+ github.com/awnumar/memguard v0.22.4 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
- github.com/lib/pq v1.10.9
+ github.com/fsnotify/fsnotify v1.7.0 // indirect
+ github.com/garrettladley/mattress v0.2.0 // indirect
+ github.com/hashicorp/hcl v1.0.0 // indirect
+ github.com/magiconair/properties v1.8.7 // indirect
+ github.com/mitchellh/mapstructure v1.5.0 // indirect
+ github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
+ github.com/sagikazarmark/locafero v0.4.0 // indirect
+ github.com/sagikazarmark/slog-shim v0.1.0 // indirect
+ github.com/sourcegraph/conc v0.3.0 // indirect
+ github.com/spf13/afero v1.11.0 // indirect
+ github.com/spf13/cast v1.6.0 // indirect
+ github.com/spf13/pflag v1.0.5 // indirect
+ github.com/spf13/viper v1.18.2 // indirect
+ github.com/subosito/gotenv v1.6.0 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
+ go.uber.org/atomic v1.9.0 // indirect
+ go.uber.org/multierr v1.9.0 // indirect
+ golang.org/x/crypto v0.18.0 // indirect
+ golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
+ golang.org/x/sys v0.16.0 // indirect
+ golang.org/x/text v0.14.0 // indirect
+ gopkg.in/ini.v1 v1.67.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/cli/go.sum b/cli/go.sum
index b030e3334..df51b4c76 100644
--- a/cli/go.sum
+++ b/cli/go.sum
@@ -1,10 +1,90 @@
+github.com/GenerateNU/sac/backend v0.0.0-20240208151800-c7c93bbd1bb7 h1:PiMBcw9KZxoH1pSfyyQsiisyemef2TwCG/WSSYailYw=
+github.com/GenerateNU/sac/backend v0.0.0-20240208151800-c7c93bbd1bb7/go.mod h1:x3FsVBrEq6k60rGtxaOLg3UJ35BIOh6pyjRdGdte5bo=
+github.com/awnumar/memcall v0.2.0 h1:sRaogqExTOOkkNwO9pzJsL8jrOV29UuUW7teRMfbqtI=
+github.com/awnumar/memcall v0.2.0/go.mod h1:S911igBPR9CThzd/hYQQmTc9SWNu3ZHIlCGaWsWsoJo=
+github.com/awnumar/memguard v0.22.4 h1:1PLgKcgGPeExPHL8dCOWGVjIbQUBgJv9OL0F/yE1PqQ=
+github.com/awnumar/memguard v0.22.4/go.mod h1:+APmZGThMBWjnMlKiSM1X7MVpbIVewen2MTkqWkA/zE=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
+github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/garrettladley/mattress v0.2.0 h1:+XUdsv9NO2s4JL+8exvAFziw0b1kv/0WlQo2Dlxat+w=
+github.com/garrettladley/mattress v0.2.0/go.mod h1:OWKIRc9wC3gtD3Ng/nUuNEiR1TJvRYLmn/KZYw9nl5Q=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
+github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
+github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
+github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
+github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
+github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
+github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
+github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
+github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
+github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
+github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
+github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
+github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
+github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho=
github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
+go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
+go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
+go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
+golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
+golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
+golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
+golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/go.work b/go.work
index 9bb923f4e..ca90035f7 100644
--- a/go.work
+++ b/go.work
@@ -1,6 +1,6 @@
-go 1.21.6
+go 1.22.0
use (
./backend
./cli
-)
+)
\ No newline at end of file
diff --git a/go.work.sum b/go.work.sum
index 344c08ef1..a6e34c243 100644
--- a/go.work.sum
+++ b/go.work.sum
@@ -1,14 +1,14 @@
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/garrettladley/mattress v0.2.0 h1:+XUdsv9NO2s4JL+8exvAFziw0b1kv/0WlQo2Dlxat+w=
-github.com/garrettladley/mattress v0.2.0/go.mod h1:OWKIRc9wC3gtD3Ng/nUuNEiR1TJvRYLmn/KZYw9nl5Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
@@ -18,6 +18,13 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
+golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
+golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
+golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
diff --git a/install.sh b/install.sh
index e7b0bee9d..990c9456e 100755
--- a/install.sh
+++ b/install.sh
@@ -1,9 +1,10 @@
#!/bin/zsh
+
set -e
set -o pipefail
# Get the absolute path to the current script
-SCRIPT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+SCRIPT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Build the Go CLI tool
go build -o "sac-cli" "cli/main.go"
@@ -11,22 +12,24 @@ go build -o "sac-cli" "cli/main.go"
# Identify the user's shell
SHELL_NAME=$(basename "$SHELL")
-COMMAND_NAME="sac-cli"
+# # Check if the command is already installed
+# COMMAND_NAME="sac-cli"
-if command -v "$COMMAND_NAME" >/dev/null 2>&1; then
- exit 1
-fi
+# if command -v "$COMMAND_NAME" >/dev/null 2>&1; then
+# echo "The command '$COMMAND_NAME' is already installed. Please uninstall it before installing a new version."
+# exit 1
+# fi
# Add sac-cli to the user's PATH
-if [[ "$SHELL_NAME" == "zsh" ]]; then
- echo "export PATH=\"$SCRIPT_PATH:\$PATH\"" >> ~/.zshrc
- source ~/.zshrc
-elif [[ "$SHELL_NAME" == "bash" ]]; then
- echo "export PATH=\"$SCRIPT_PATH:\$PATH\"" >> ~/.bashrc
- source ~/.bashrc
+if [[ $SHELL_NAME == "zsh" ]]; then
+ echo "export PATH=\"$SCRIPT_PATH:\$PATH\"" >>~/.zshrc
+ source ~/.zshrc
+elif [[ $SHELL_NAME == "bash" ]]; then
+ echo "export PATH=\"$SCRIPT_PATH:\$PATH\"" >>~/.bashrc
+ source ~/.bashrc
else
- echo "Unsupported shell: $SHELL_NAME"
- exit 1
+ echo "Unsupported shell: $SHELL_NAME"
+ exit 1
fi
# Inform the user
From 5adba7e772f159f3e11dc3dc465d38f07ced063e Mon Sep 17 00:00:00 2001
From: David Oduneye <44040421+DOOduneye@users.noreply.github.com>
Date: Fri, 9 Feb 2024 17:41:52 -0500
Subject: [PATCH 30/33] resolve issue 181 (#182)
---
backend/src/auth/tokens.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/backend/src/auth/tokens.go b/backend/src/auth/tokens.go
index ca902803f..3f2c96543 100644
--- a/backend/src/auth/tokens.go
+++ b/backend/src/auth/tokens.go
@@ -36,7 +36,7 @@ func CreateAccessToken(id string, role string, accessExpiresAfter uint, accessTo
StandardClaims: jwt.StandardClaims{
IssuedAt: time.Now().Unix(),
Issuer: id,
- ExpiresAt: time.Now().Add(time.Duration(accessExpiresAfter)).Unix(),
+ ExpiresAt: time.Now().Add(time.Hour * time.Duration(accessExpiresAfter)).Unix(),
},
Role: role,
})
From c3198acba846c99c4f490b9cd06a7cd7b137fddf Mon Sep 17 00:00:00 2001
From: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
Date: Fri, 9 Feb 2024 19:54:09 -0500
Subject: [PATCH 31/33] Added Back ./backend/src/main.go Command Line Flags
(#184)
---
backend/src/main.go | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/backend/src/main.go b/backend/src/main.go
index 4274c05ec..f5b1ffcc3 100644
--- a/backend/src/main.go
+++ b/backend/src/main.go
@@ -1,6 +1,7 @@
package main
import (
+ "flag"
"fmt"
"path/filepath"
@@ -18,7 +19,12 @@ import (
// @host 127.0.0.1:8080
// @BasePath /api/v1
func main() {
- config, err := config.GetConfiguration(filepath.Join("..", "..", "config"))
+ onlyMigrate := flag.Bool("only-migrate", false, "Specify if you want to only perform the database migration")
+ configPath := flag.String("config", filepath.Join("..", "..", "config"), "Specify the path to the config directory")
+
+ flag.Parse()
+
+ config, err := config.GetConfiguration(*configPath)
if err != nil {
panic(fmt.Sprintf("Error getting configuration: %s", err.Error()))
}
@@ -28,6 +34,10 @@ func main() {
panic(fmt.Sprintf("Error configuring database: %s", err.Error()))
}
+ if *onlyMigrate {
+ return
+ }
+
err = database.ConnPooling(db)
if err != nil {
panic(fmt.Sprintf("Error connecting to database: %s", err.Error()))
From a6d848d3ff1b8772e001a1b0be45908c097611a6 Mon Sep 17 00:00:00 2001
From: edwinliiiii <91173669+edwinliiiii@users.noreply.github.com>
Date: Sat, 10 Feb 2024 15:49:17 -0500
Subject: [PATCH 32/33] SAC-22 Contact CRUD (#75)
Co-authored-by: garrettladley
Co-authored-by: Michael Brennan
Co-authored-by: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
Co-authored-by: OJisMe
Co-authored-by: Olivier Ndjike Nzia
---
backend/src/controllers/club_contact.go | 40 ++++
backend/src/controllers/contact.go | 46 ++++
backend/src/errors/club.go | 1 +
backend/src/errors/contact.go | 30 +++
backend/src/models/contact.go | 11 +-
backend/src/server/routes/club.go | 6 +-
backend/src/server/routes/club_contact.go | 17 ++
backend/src/server/routes/contact.go | 18 ++
backend/src/server/server.go | 5 +-
backend/src/services/club_contact.go | 53 +++++
backend/src/services/contact.go | 59 +++++
backend/src/transactions/club.go | 41 +---
backend/src/transactions/club_contact.go | 45 ++++
backend/src/transactions/contacts.go | 83 +++++++
backend/src/utilities/validator.go | 4 +-
backend/tests/api/category_tag_test.go | 2 +-
backend/tests/api/category_test.go | 10 +-
backend/tests/api/club_contact_test.go | 138 ++++++++++++
backend/tests/api/club_test.go | 8 +-
backend/tests/api/contact_test.go | 258 ++++++++++++++++++++++
backend/tests/api/helpers/requests.go | 2 +-
backend/tests/api/tag_test.go | 8 +-
backend/tests/api/user_tag_test.go | 2 +-
backend/tests/api/user_test.go | 18 +-
cli/commands/lint.go | 2 +-
go.work.sum | 76 +++++++
26 files changed, 908 insertions(+), 75 deletions(-)
create mode 100644 backend/src/controllers/club_contact.go
create mode 100644 backend/src/controllers/contact.go
create mode 100644 backend/src/errors/contact.go
create mode 100644 backend/src/server/routes/club_contact.go
create mode 100644 backend/src/server/routes/contact.go
create mode 100644 backend/src/services/club_contact.go
create mode 100644 backend/src/services/contact.go
create mode 100644 backend/src/transactions/club_contact.go
create mode 100644 backend/src/transactions/contacts.go
create mode 100644 backend/tests/api/club_contact_test.go
create mode 100644 backend/tests/api/contact_test.go
diff --git a/backend/src/controllers/club_contact.go b/backend/src/controllers/club_contact.go
new file mode 100644
index 000000000..f23ddf2e0
--- /dev/null
+++ b/backend/src/controllers/club_contact.go
@@ -0,0 +1,40 @@
+package controllers
+
+import (
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+type ClubContactController struct {
+ clubContactService services.ClubContactServiceInterface
+}
+
+func NewClubContactController(clubContactService services.ClubContactServiceInterface) *ClubContactController {
+ return &ClubContactController{clubContactService: clubContactService}
+}
+
+func (cc *ClubContactController) GetClubContacts(c *fiber.Ctx) error {
+ contacts, err := cc.clubContactService.GetClubContacts(c.Params("clubID"))
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ return c.Status(fiber.StatusOK).JSON(contacts)
+}
+
+func (cc *ClubContactController) PutContact(c *fiber.Ctx) error {
+ var contactBody models.PutContactRequestBody
+
+ if err := c.BodyParser(&contactBody); err != nil {
+ return errors.FailedToParseRequestBody.FiberError(c)
+ }
+
+ contact, err := cc.clubContactService.PutClubContact(c.Params("clubID"), contactBody)
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ return c.Status(fiber.StatusOK).JSON(contact)
+}
diff --git a/backend/src/controllers/contact.go b/backend/src/controllers/contact.go
new file mode 100644
index 000000000..4ce650882
--- /dev/null
+++ b/backend/src/controllers/contact.go
@@ -0,0 +1,46 @@
+package controllers
+
+import (
+ "strconv"
+
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+type ContactController struct {
+ contactService services.ContactServiceInterface
+}
+
+func NewContactController(contactService services.ContactServiceInterface) *ContactController {
+ return &ContactController{contactService: contactService}
+}
+
+func (co *ContactController) GetContact(c *fiber.Ctx) error {
+ contact, err := co.contactService.GetContact(c.Params("contactID"))
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ return c.Status(fiber.StatusOK).JSON(contact)
+}
+
+func (co *ContactController) GetContacts(c *fiber.Ctx) error {
+ defaultLimit := 10
+ defaultPage := 1
+
+ contacts, err := co.contactService.GetContacts(c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage)))
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ return c.Status(fiber.StatusOK).JSON(contacts)
+}
+
+func (co *ContactController) DeleteContact(c *fiber.Ctx) error {
+ err := co.contactService.DeleteContact(c.Params("contactID"))
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ return c.SendStatus(fiber.StatusNoContent)
+}
diff --git a/backend/src/errors/club.go b/backend/src/errors/club.go
index 7b05d03bd..aef90251c 100644
--- a/backend/src/errors/club.go
+++ b/backend/src/errors/club.go
@@ -35,6 +35,7 @@ var (
StatusCode: fiber.StatusNotFound,
Message: "club not found",
}
+
FailedtoGetAdminIDs = Error{
StatusCode: fiber.StatusInternalServerError,
Message: "failed to get admin ids",
diff --git a/backend/src/errors/contact.go b/backend/src/errors/contact.go
new file mode 100644
index 000000000..d408a5a37
--- /dev/null
+++ b/backend/src/errors/contact.go
@@ -0,0 +1,30 @@
+package errors
+
+import "github.com/gofiber/fiber/v2"
+
+var (
+ FailedToGetContacts = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "failed to get contacts",
+ }
+ FailedToGetContact = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "failed to get contact",
+ }
+ ContactNotFound = Error{
+ StatusCode: fiber.StatusNotFound,
+ Message: "contact not found",
+ }
+ FailedToPutContact = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "failed to put contact",
+ }
+ FailedToDeleteContact = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "failed to delete contact",
+ }
+ FailedToValidateContact = Error{
+ StatusCode: fiber.StatusBadRequest,
+ Message: "failed to validate contact",
+ }
+)
diff --git a/backend/src/models/contact.go b/backend/src/models/contact.go
index 8c86ada90..3ab4baa30 100644
--- a/backend/src/models/contact.go
+++ b/backend/src/models/contact.go
@@ -20,8 +20,13 @@ const (
type Contact struct {
Model
- Type ContactType `gorm:"type:varchar(255)" json:"type" validate:"required,max=255"`
- Content string `gorm:"type:varchar(255)" json:"content" validate:"required,contact_pointer,max=255"`
+ Type ContactType `gorm:"type:varchar(255);uniqueIndex:idx_contact_type" json:"type" validate:"required,max=255,oneof=facebook instagram twitter linkedin youtube github slack discord email customSite"`
+ Content string `gorm:"type:varchar(255)" json:"content" validate:"required,max=255"`
- ClubID uuid.UUID `gorm:"foreignKey:ClubID" json:"-" validate:"uuid4"`
+ ClubID uuid.UUID `gorm:"foreignKey:ClubID;uniqueIndex:idx_contact_type" json:"-" validate:"uuid4"`
+}
+
+type PutContactRequestBody struct {
+ Type ContactType `json:"type" validate:"required,max=255,oneof=facebook instagram twitter linkedin youtube github slack discord email customSite,contact_pointer"`
+ Content string `json:"content" validate:"required,max=255"`
}
diff --git a/backend/src/server/routes/club.go b/backend/src/server/routes/club.go
index 91d2e440a..f593389ea 100644
--- a/backend/src/server/routes/club.go
+++ b/backend/src/server/routes/club.go
@@ -8,7 +8,7 @@ import (
"github.com/gofiber/fiber/v2"
)
-func Club(router fiber.Router, clubService services.ClubServiceInterface, middlewareService middleware.MiddlewareInterface) {
+func Club(router fiber.Router, clubService services.ClubServiceInterface, middlewareService middleware.MiddlewareInterface) fiber.Router {
clubController := controllers.NewClubController(clubService)
clubs := router.Group("/clubs")
@@ -22,5 +22,7 @@ func Club(router fiber.Router, clubService services.ClubServiceInterface, middle
clubsID.Get("/", clubController.GetClub)
clubsID.Patch("/", middlewareService.Authorize(types.ClubWrite), clubController.UpdateClub)
- clubsID.Delete("/", middleware.SuperSkipper(middlewareService.Authorize(types.ClubDelete)), clubController.DeleteClub)
+ clubsID.Delete("/", middlewareService.Authorize(types.ClubDelete), clubController.DeleteClub)
+
+ return clubsID
}
diff --git a/backend/src/server/routes/club_contact.go b/backend/src/server/routes/club_contact.go
new file mode 100644
index 000000000..4d0055c5d
--- /dev/null
+++ b/backend/src/server/routes/club_contact.go
@@ -0,0 +1,17 @@
+package routes
+
+import (
+ "github.com/GenerateNU/sac/backend/src/controllers"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+func ClubContact(clubsIDRouter fiber.Router, clubContactService services.ClubContactServiceInterface) {
+ clubContactController := controllers.NewClubContactController(clubContactService)
+
+ clubContacts := clubsIDRouter.Group("/contacts")
+
+ // api/v1/clubs/:clubID/contacts/*
+ clubContacts.Get("/", clubContactController.GetClubContacts)
+ clubContacts.Put("/", clubContactController.PutContact)
+}
diff --git a/backend/src/server/routes/contact.go b/backend/src/server/routes/contact.go
new file mode 100644
index 000000000..a5c7beddb
--- /dev/null
+++ b/backend/src/server/routes/contact.go
@@ -0,0 +1,18 @@
+package routes
+
+import (
+ "github.com/GenerateNU/sac/backend/src/controllers"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+func Contact(router fiber.Router, contactService services.ContactServiceInterface) {
+ contactController := controllers.NewContactController(contactService)
+
+ // api/v1/contacts/*
+ contacts := router.Group("/contacts")
+
+ contacts.Get("/", contactController.GetContacts)
+ contacts.Get("/:contactID", contactController.GetContact)
+ contacts.Delete("/:contactID", contactController.DeleteContact)
+}
diff --git a/backend/src/server/server.go b/backend/src/server/server.go
index 5d249a0a3..c1a8d08ab 100644
--- a/backend/src/server/server.go
+++ b/backend/src/server/server.go
@@ -43,7 +43,10 @@ func Init(db *gorm.DB, settings config.Settings) *fiber.App {
userRouter := routes.User(apiv1, services.NewUserService(db, validate), middlewareService)
routes.UserTag(userRouter, services.NewUserTagService(db, validate))
- routes.Club(apiv1, services.NewClubService(db, validate), middlewareService)
+ routes.Contact(apiv1, services.NewContactService(db, validate))
+
+ clubsRouter := routes.Club(apiv1, services.NewClubService(db, validate), middlewareService)
+ routes.ClubContact(clubsRouter, services.NewClubContactService(db, validate))
routes.Tag(apiv1, services.NewTagService(db, validate))
diff --git a/backend/src/services/club_contact.go b/backend/src/services/club_contact.go
new file mode 100644
index 000000000..b39bf471b
--- /dev/null
+++ b/backend/src/services/club_contact.go
@@ -0,0 +1,53 @@
+package services
+
+import (
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/GenerateNU/sac/backend/src/transactions"
+ "github.com/GenerateNU/sac/backend/src/utilities"
+ "github.com/go-playground/validator/v10"
+ "gorm.io/gorm"
+)
+
+type ClubContactServiceInterface interface {
+ GetClubContacts(clubID string) ([]models.Contact, *errors.Error)
+ PutClubContact(clubID string, contactBody models.PutContactRequestBody) (*models.Contact, *errors.Error)
+}
+
+type ClubContactService struct {
+ DB *gorm.DB
+ Validate *validator.Validate
+}
+
+func NewClubContactService(db *gorm.DB, validate *validator.Validate) *ClubContactService {
+ return &ClubContactService{DB: db, Validate: validate}
+}
+
+func (c *ClubContactService) GetClubContacts(clubID string) ([]models.Contact, *errors.Error) {
+ idAsUUID, err := utilities.ValidateID(clubID)
+ if err != nil {
+ return nil, &errors.FailedToValidateID
+ }
+
+ return transactions.GetClubContacts(c.DB, *idAsUUID)
+}
+
+func (c *ClubContactService) PutClubContact(clubID string, contactBody models.PutContactRequestBody) (*models.Contact, *errors.Error) {
+ idAsUUID, idErr := utilities.ValidateID(clubID)
+ if idErr != nil {
+ return nil, idErr
+ }
+
+ if err := c.Validate.Struct(contactBody); err != nil {
+ return nil, &errors.FailedToValidateContact
+ }
+
+ contact, err := utilities.MapRequestToModel(contactBody, &models.Contact{})
+ if err != nil {
+ return nil, &errors.FailedToMapRequestToModel
+ }
+
+ contact.ClubID = *idAsUUID
+
+ return transactions.PutClubContact(c.DB, *contact)
+}
diff --git a/backend/src/services/contact.go b/backend/src/services/contact.go
new file mode 100644
index 000000000..1d04ea695
--- /dev/null
+++ b/backend/src/services/contact.go
@@ -0,0 +1,59 @@
+package services
+
+import (
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/GenerateNU/sac/backend/src/transactions"
+ "github.com/GenerateNU/sac/backend/src/utilities"
+ "github.com/go-playground/validator/v10"
+ "gorm.io/gorm"
+)
+
+type ContactServiceInterface interface {
+ GetContacts(limit string, page string) ([]models.Contact, *errors.Error)
+ GetContact(contactID string) (*models.Contact, *errors.Error)
+ DeleteContact(contactID string) *errors.Error
+}
+
+type ContactService struct {
+ DB *gorm.DB
+ Validate *validator.Validate
+}
+
+func NewContactService(db *gorm.DB, validate *validator.Validate) *ContactService {
+ return &ContactService{DB: db, Validate: validate}
+}
+
+func (c *ContactService) GetContacts(limit string, page string) ([]models.Contact, *errors.Error) {
+ limitAsInt, err := utilities.ValidateNonNegative(limit)
+ if err != nil {
+ return nil, &errors.FailedToValidateLimit
+ }
+
+ pageAsInt, err := utilities.ValidateNonNegative(page)
+ if err != nil {
+ return nil, &errors.FailedToValidatePage
+ }
+
+ offset := (*pageAsInt - 1) * *limitAsInt
+
+ return transactions.GetContacts(c.DB, *limitAsInt, offset)
+}
+
+func (c *ContactService) GetContact(contactID string) (*models.Contact, *errors.Error) {
+ idAsUUID, err := utilities.ValidateID(contactID)
+ if err != nil {
+ return nil, &errors.FailedToValidateID
+ }
+
+ return transactions.GetContact(c.DB, *idAsUUID)
+}
+
+func (c *ContactService) DeleteContact(contactID string) *errors.Error {
+ idAsUUID, err := utilities.ValidateID(contactID)
+ if err != nil {
+ return &errors.FailedToValidateID
+ }
+
+ return transactions.DeleteContact(c.DB, *idAsUUID)
+}
diff --git a/backend/src/transactions/club.go b/backend/src/transactions/club.go
index 016e2d0ef..418101a34 100644
--- a/backend/src/transactions/club.go
+++ b/backend/src/transactions/club.go
@@ -5,8 +5,8 @@ import (
"github.com/GenerateNU/sac/backend/src/errors"
"github.com/GenerateNU/sac/backend/src/models"
- "github.com/google/uuid"
+ "github.com/google/uuid"
"gorm.io/gorm"
)
@@ -73,42 +73,3 @@ func GetClub(db *gorm.DB, id uuid.UUID) (*models.Club, *errors.Error) {
return &club, nil
}
-
-func UpdateClub(db *gorm.DB, id uuid.UUID, club models.Club) (*models.Club, *errors.Error) {
- result := db.Model(&models.User{}).Where("id = ?", id).Updates(club)
- if result.Error != nil {
- if stdliberrors.Is(result.Error, gorm.ErrRecordNotFound) {
- return nil, &errors.UserNotFound
- } else {
- return nil, &errors.FailedToUpdateClub
- }
- }
- var existingClub models.Club
-
- err := db.First(&existingClub, id).Error
- if err != nil {
- if stdliberrors.Is(err, gorm.ErrRecordNotFound) {
- return nil, &errors.ClubNotFound
- } else {
- return nil, &errors.FailedToCreateClub
- }
- }
-
- if err := db.Model(&existingClub).Updates(&club).Error; err != nil {
- return nil, &errors.FailedToUpdateUser
- }
-
- return &existingClub, nil
-}
-
-func DeleteClub(db *gorm.DB, id uuid.UUID) *errors.Error {
- if result := db.Delete(&models.Club{}, id); result.RowsAffected == 0 {
- if result.Error == nil {
- return &errors.ClubNotFound
- } else {
- return &errors.FailedToDeleteClub
- }
- }
-
- return nil
-}
diff --git a/backend/src/transactions/club_contact.go b/backend/src/transactions/club_contact.go
new file mode 100644
index 000000000..7cb7afb87
--- /dev/null
+++ b/backend/src/transactions/club_contact.go
@@ -0,0 +1,45 @@
+package transactions
+
+import (
+ stdliberrors "errors"
+ "fmt"
+
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/google/uuid"
+ "gorm.io/gorm"
+ "gorm.io/gorm/clause"
+)
+
+func PutClubContact(db *gorm.DB, contact models.Contact) (*models.Contact, *errors.Error) {
+ err := db.Clauses(clause.OnConflict{
+ Columns: []clause.Column{{Name: "club_id"}, {Name: "type"}},
+ DoUpdates: clause.AssignmentColumns([]string{"content"}),
+ }).Create(&contact).Error
+ if err != nil {
+ fmt.Println(err)
+ if stdliberrors.Is(err, gorm.ErrRecordNotFound) || stdliberrors.Is(err, gorm.ErrForeignKeyViolated) {
+ return nil, &errors.ClubNotFound
+ } else {
+ return nil, &errors.FailedToPutContact
+ }
+ }
+ return &contact, nil
+}
+
+func GetClubContacts(db *gorm.DB, clubID uuid.UUID) ([]models.Contact, *errors.Error) {
+ var club models.Club
+ if err := db.Preload("Contact").First(&club, clubID).Error; err != nil {
+ if stdliberrors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, &errors.ClubNotFound
+ } else {
+ return nil, &errors.FailedToGetContacts
+ }
+ }
+
+ if club.Contact == nil {
+ return nil, &errors.FailedToGetContacts
+ }
+
+ return club.Contact, nil
+}
diff --git a/backend/src/transactions/contacts.go b/backend/src/transactions/contacts.go
new file mode 100644
index 000000000..51ac2bb00
--- /dev/null
+++ b/backend/src/transactions/contacts.go
@@ -0,0 +1,83 @@
+package transactions
+
+import (
+ stdliberrors "errors"
+
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/google/uuid"
+ "gorm.io/gorm"
+)
+
+func GetContacts(db *gorm.DB, limit int, offset int) ([]models.Contact, *errors.Error) {
+ var contacts []models.Contact
+ result := db.Limit(limit).Offset(offset).Find(&contacts)
+ if result.Error != nil {
+ return nil, &errors.FailedToGetContacts
+ }
+
+ return contacts, nil
+}
+
+func GetContact(db *gorm.DB, id uuid.UUID) (*models.Contact, *errors.Error) {
+ var contact models.Contact
+ if err := db.First(&contact, id).Error; err != nil {
+ if stdliberrors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, &errors.ContactNotFound
+ } else {
+ return nil, &errors.FailedToGetContact
+ }
+ }
+
+ return &contact, nil
+}
+
+func DeleteContact(db *gorm.DB, id uuid.UUID) *errors.Error {
+ if result := db.Delete(&models.Contact{}, id); result.RowsAffected == 0 {
+ if result.Error == nil {
+ return &errors.ContactNotFound
+ } else {
+ return &errors.FailedToDeleteContact
+ }
+ }
+ return nil
+}
+
+func UpdateClub(db *gorm.DB, id uuid.UUID, club models.Club) (*models.Club, *errors.Error) {
+ result := db.Model(&models.User{}).Where("id = ?", id).Updates(club)
+ if result.Error != nil {
+ if stdliberrors.Is(result.Error, gorm.ErrRecordNotFound) {
+ return nil, &errors.UserNotFound
+ } else {
+ return nil, &errors.FailedToUpdateClub
+ }
+ }
+ var existingClub models.Club
+
+ err := db.First(&existingClub, id).Error
+ if err != nil {
+ if stdliberrors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, &errors.ClubNotFound
+ } else {
+ return nil, &errors.FailedToCreateClub
+ }
+ }
+
+ if err := db.Model(&existingClub).Updates(&club).Error; err != nil {
+ return nil, &errors.FailedToUpdateUser
+ }
+
+ return &existingClub, nil
+}
+
+func DeleteClub(db *gorm.DB, id uuid.UUID) *errors.Error {
+ if result := db.Delete(&models.Club{}, id); result.RowsAffected == 0 {
+ if result.Error == nil {
+ return &errors.ClubNotFound
+ } else {
+ return &errors.FailedToDeleteClub
+ }
+ }
+
+ return nil
+}
diff --git a/backend/src/utilities/validator.go b/backend/src/utilities/validator.go
index 701f3c595..3529839cb 100644
--- a/backend/src/utilities/validator.go
+++ b/backend/src/utilities/validator.go
@@ -72,12 +72,10 @@ func validateS3URL(fl validator.FieldLevel) bool {
}
func validateContactPointer(validate *validator.Validate, fl validator.FieldLevel) bool {
- contact, ok := fl.Parent().Interface().(models.Contact)
-
+ contact, ok := fl.Parent().Interface().(models.PutContactRequestBody)
if !ok {
return false
}
-
switch contact.Type {
case models.Email:
return validate.Var(contact.Content, "email") == nil
diff --git a/backend/tests/api/category_tag_test.go b/backend/tests/api/category_tag_test.go
index d5650286d..888c28147 100644
--- a/backend/tests/api/category_tag_test.go
+++ b/backend/tests/api/category_tag_test.go
@@ -94,7 +94,7 @@ func TestGetCategoryTagsFailsCategoryNotFound(t *testing.T) {
uuid := uuid.New()
- appAssert.TestOnErrorAndDB(
+ appAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodGet,
Path: fmt.Sprintf("/api/v1/categories/%s/tags", uuid),
diff --git a/backend/tests/api/category_test.go b/backend/tests/api/category_test.go
index 781831ded..921adf4bf 100644
--- a/backend/tests/api/category_test.go
+++ b/backend/tests/api/category_test.go
@@ -135,7 +135,7 @@ func AssertNumCategoriesRemainsAtN(eaa h.ExistingAppAssert, resp *http.Response,
}
func TestCreateCategoryFailsIfNameIsNotString(t *testing.T) {
- h.InitTest(t).TestOnErrorAndDB(
+ h.InitTest(t).TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/categories/",
@@ -152,7 +152,7 @@ func TestCreateCategoryFailsIfNameIsNotString(t *testing.T) {
}
func TestCreateCategoryFailsIfNameIsMissing(t *testing.T) {
- h.InitTest(t).TestOnErrorAndDB(
+ h.InitTest(t).TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/categories/",
@@ -177,7 +177,7 @@ func TestCreateCategoryFailsIfCategoryWithThatNameAlreadyExists(t *testing.T) {
modifiedSampleCategoryBody := *SampleCategoryFactory()
modifiedSampleCategoryBody["name"] = permutation
- existingAppAssert.TestOnErrorAndDB(
+ existingAppAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/categories/",
@@ -412,7 +412,7 @@ func TestDeleteCategoryFailsBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- existingAppAssert.TestOnErrorAndDB(
+ existingAppAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/categories/%s", badRequest),
@@ -431,7 +431,7 @@ func TestDeleteCategoryFailsBadRequest(t *testing.T) {
func TestDeleteCategoryFailsNotFound(t *testing.T) {
existingAppAssert, _ := CreateSampleCategory(h.InitTest(t))
- existingAppAssert.TestOnErrorAndDB(
+ existingAppAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/categories/%s", uuid.New()),
diff --git a/backend/tests/api/club_contact_test.go b/backend/tests/api/club_contact_test.go
new file mode 100644
index 000000000..ead3052bb
--- /dev/null
+++ b/backend/tests/api/club_contact_test.go
@@ -0,0 +1,138 @@
+package tests
+
+import (
+ stdliberrors "errors"
+ "fmt"
+ "net/http"
+ "testing"
+
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ h "github.com/GenerateNU/sac/backend/tests/api/helpers"
+ "github.com/goccy/go-json"
+ "github.com/gofiber/fiber/v2"
+ "github.com/google/uuid"
+ "gorm.io/gorm"
+)
+
+func AssertCreateBadContactDataFails(t *testing.T, jsonKey string, badValues []interface{}) {
+ appAssert, _, clubUUID := CreateSampleClub(h.InitTest(t))
+
+ for _, badValue := range badValues {
+ sampleContactPermutation := *SampleContactFactory()
+ sampleContactPermutation[jsonKey] = badValue
+
+ appAssert = appAssert.TestOnErrorAndTester(h.TestRequest{
+ Method: fiber.MethodPut,
+ Path: fmt.Sprintf("/api/v1/clubs/%s/contacts", clubUUID),
+ Body: &sampleContactPermutation,
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.FailedToValidateContact,
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ AssertNumContactsRemainsAtN(eaa, resp, 0)
+ },
+ },
+ )
+ }
+ appAssert.Close()
+}
+
+func TestCreateContactFailsOnInvalidType(t *testing.T) {
+ AssertCreateBadContactDataFails(t,
+ "type",
+ []interface{}{
+ "Not a valid type",
+ "@#139081#$Ad_Axf",
+ },
+ )
+}
+
+func TestCreateContactFailsOnInvalidContent(t *testing.T) {
+ AssertCreateBadContactDataFails(t,
+ "content",
+ []interface{}{
+ "Not a valid url",
+ "@#139081#$Ad_Axf",
+ },
+ )
+}
+
+func TestPutContactFailsOnClubIdNotExist(t *testing.T) {
+ appAssert, _, _ := CreateSampleClub(h.InitTest(t))
+
+ uuid := uuid.New()
+
+ appAssert.TestOnErrorAndTester(h.TestRequest{
+ Method: fiber.MethodPut,
+ Path: fmt.Sprintf("/api/v1/clubs/%s/contacts", uuid),
+ Body: SampleContactFactory(),
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.ClubNotFound,
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ var club models.Club
+
+ err := eaa.App.Conn.Where("id = ?", uuid).First(&club).Error
+
+ eaa.Assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
+ },
+ },
+ ).Close()
+}
+
+func TestPutContactUpdatesExistingContact(t *testing.T) {
+ appAssert, clubUUID, contactUUID := CreateSampleContact(h.InitTest(t))
+
+ updatedContact := SampleContactFactory()
+ (*updatedContact)["content"] = "nedFlanders@gmail.com"
+
+ appAssert.TestOnStatusAndTester(h.TestRequest{
+ Method: fiber.MethodPut,
+ Path: fmt.Sprintf("/api/v1/clubs/%s/contacts", clubUUID),
+ Body: updatedContact,
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusOK,
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ var dbContact models.Contact
+
+ err := eaa.App.Conn.Where("id = ?", contactUUID).First(&dbContact).Error
+
+ eaa.Assert.NilError(err)
+
+ eaa.Assert.Equal(dbContact.Content, (*updatedContact)["content"])
+ },
+ },
+ ).Close()
+}
+
+func TestGetClubContacts(t *testing.T) {
+ appAssert, clubUUID, _ := CreateManyContacts(h.InitTest(t))
+
+ appAssert.TestOnStatusAndTester(h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/clubs/%s/contacts", clubUUID),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusOK,
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ var respContacts []models.Contact
+ var dbContacts []models.Contact
+ err := json.NewDecoder(resp.Body).Decode(&respContacts)
+ eaa.Assert.NilError(err)
+
+ err = eaa.App.Conn.Where("club_id = ?", clubUUID).Find(&dbContacts).Error
+ eaa.Assert.NilError(err)
+
+ eaa.Assert.Equal(len(respContacts), len(dbContacts))
+ },
+ },
+ )
+
+ appAssert.Close()
+}
diff --git a/backend/tests/api/club_test.go b/backend/tests/api/club_test.go
index f95783385..bea54a011 100644
--- a/backend/tests/api/club_test.go
+++ b/backend/tests/api/club_test.go
@@ -235,7 +235,7 @@ func AssertCreateBadClubDataFails(t *testing.T, jsonKey string, badValues []inte
sampleClubPermutation := *SampleClubFactory(&uuid)
sampleClubPermutation[jsonKey] = badValue
- appAssert.TestOnErrorAndDB(
+ appAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/clubs/",
@@ -343,7 +343,7 @@ func TestUpdateClubFailsOnInvalidBody(t *testing.T) {
{"logo": "@12394X_2"},
} {
invalidData := invalidData
- appAssert.TestOnErrorAndDB(
+ appAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPatch,
Path: fmt.Sprintf("/api/v1/clubs/%s", clubUUID),
@@ -418,7 +418,7 @@ func TestUpdateClubFailsBadRequest(t *testing.T) {
func TestUpdateClubFailsOnClubIdNotExist(t *testing.T) {
uuid := uuid.New()
- h.InitTest(t).TestOnErrorAndDB(h.TestRequest{
+ h.InitTest(t).TestOnErrorAndTester(h.TestRequest{
Method: fiber.MethodPatch,
Path: fmt.Sprintf("/api/v1/clubs/%s", uuid),
Body: SampleClubFactory(nil),
@@ -456,7 +456,7 @@ func TestDeleteClubWorks(t *testing.T) {
func TestDeleteClubNotExist(t *testing.T) {
uuid := uuid.New()
- h.InitTest(t).TestOnErrorAndDB(
+ h.InitTest(t).TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/clubs/%s", uuid),
diff --git a/backend/tests/api/contact_test.go b/backend/tests/api/contact_test.go
new file mode 100644
index 000000000..4dfc76a22
--- /dev/null
+++ b/backend/tests/api/contact_test.go
@@ -0,0 +1,258 @@
+package tests
+
+import (
+ stdliberrors "errors"
+ "fmt"
+ "net/http"
+ "testing"
+
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ h "github.com/GenerateNU/sac/backend/tests/api/helpers"
+ "github.com/goccy/go-json"
+ "github.com/gofiber/fiber/v2"
+ "github.com/google/uuid"
+ "gorm.io/gorm"
+)
+
+func SampleContactFactory() *map[string]interface{} {
+ return &map[string]interface{}{
+ "type": "email",
+ "content": "jermaine@gmail.com",
+ }
+}
+
+func ManyContactsFactory() map[string](*map[string]interface{}) {
+ arr := make(map[string]*map[string]interface{})
+
+ arr["email"] = &map[string]interface{}{
+ "type": "email",
+ "content": "cheeseClub@gmail.com",
+ }
+
+ arr["youtube"] = &map[string]interface{}{
+ "type": "youtube",
+ "content": "https://youtube.com/cheeseClub",
+ }
+
+ arr["facebook"] = &map[string]interface{}{
+ "type": "facebook",
+ "content": "https://facebook.com/cheeseClub",
+ }
+
+ arr["discord"] = &map[string]interface{}{
+ "type": "discord",
+ "content": "https://discord.com/cheeseClub",
+ }
+
+ arr["instagram"] = &map[string]interface{}{
+ "type": "instagram",
+ "content": "https://instagram.com/cheeseClub",
+ }
+ arr["github"] = &map[string]interface{}{
+ "type": "github",
+ "content": "https://github.com/cheeseClub",
+ }
+
+ return arr
+}
+
+func AssertContactBodyRespDB(eaa h.ExistingAppAssert, resp *http.Response, body *map[string]interface{}) uuid.UUID {
+ var respContact models.Contact
+
+ err := json.NewDecoder(resp.Body).Decode(&respContact)
+
+ eaa.Assert.NilError(err)
+
+ var dbContacts []models.Contact
+
+ err = eaa.App.Conn.Order("created_at desc").Find(&dbContacts).Error
+
+ eaa.Assert.NilError(err)
+
+ dbContact := dbContacts[0]
+
+ eaa.Assert.Equal(dbContact.ID, respContact.ID)
+ eaa.Assert.Equal(dbContact.Type, respContact.Type)
+ eaa.Assert.Equal(dbContact.Content, respContact.Content)
+
+ return dbContact.ID
+}
+
+func CreateSampleContact(existingAppAssert h.ExistingAppAssert) (eaa h.ExistingAppAssert, clubUUID uuid.UUID, contactUUID uuid.UUID) {
+ appAssert, _, clubUUID := CreateSampleClub(existingAppAssert)
+
+ var sampleContactUUID uuid.UUID
+
+ return appAssert.TestOnStatusAndTester(
+ h.TestRequest{
+ Method: fiber.MethodPut,
+ Path: fmt.Sprintf("/api/v1/clubs/%s/contacts", clubUUID),
+ Body: SampleContactFactory(),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusOK,
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ sampleContactUUID = AssertContactBodyRespDB(eaa, resp, SampleContactFactory())
+ AssertNumContactsRemainsAtN(eaa, resp, 1)
+ },
+ },
+ ), clubUUID, sampleContactUUID
+}
+
+func CreateManyContacts(existingAppAssert h.ExistingAppAssert) (eaa h.ExistingAppAssert, clubUUID uuid.UUID, contactUUIDs map[string]uuid.UUID) {
+ existingAppAssert, _, clubUUID = CreateSampleClub(existingAppAssert)
+
+ contactUUIDs = make(map[string]uuid.UUID)
+
+ currentLength := 0
+ for key, contact := range ManyContactsFactory() {
+ existingAppAssert = existingAppAssert.TestOnStatusAndTester(h.TestRequest{
+ Method: fiber.MethodPut,
+ Path: fmt.Sprintf("/api/v1/clubs/%s/contacts", clubUUID),
+ Body: contact,
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusOK,
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ contactUUIDs[key] = AssertContactBodyRespDB(eaa, resp, contact)
+ currentLength++
+ AssertNumContactsRemainsAtN(eaa, resp, currentLength)
+ },
+ },
+ )
+ }
+
+ return existingAppAssert, clubUUID, contactUUIDs
+}
+
+func TestCreateManyContactsWorks(t *testing.T) {
+ existingAppAssert, _, _ := CreateManyContacts(h.InitTest(t))
+ existingAppAssert.Close()
+}
+
+func TestCreateContactWorks(t *testing.T) {
+ existingAppAssert, _, _ := CreateSampleContact(h.InitTest(t))
+ existingAppAssert.Close()
+}
+
+func AssertNumContactsRemainsAtN(eaa h.ExistingAppAssert, resp *http.Response, n int) {
+ var dbContacts []models.Contact
+
+ err := eaa.App.Conn.Order("created_at desc").Find(&dbContacts).Error
+
+ eaa.Assert.NilError(err)
+
+ eaa.Assert.Equal(n, len(dbContacts))
+}
+
+func TestGetContactByIdWorks(t *testing.T) {
+ appAssert, _, contactUUID := CreateSampleContact(h.InitTest(t))
+
+ appAssert.TestOnStatusAndTester(h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/contacts/%s", contactUUID),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusOK,
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ var respContact models.Contact
+
+ err := json.NewDecoder(resp.Body).Decode(&respContact)
+
+ eaa.Assert.NilError(err)
+
+ var dbContacts []models.Contact
+
+ err = eaa.App.Conn.Order("created_at desc").Find(&dbContacts).Error
+
+ eaa.Assert.NilError(err)
+
+ eaa.Assert.Equal(dbContacts[0].ID, respContact.ID)
+ eaa.Assert.Equal(dbContacts[0].Type, respContact.Type)
+ eaa.Assert.Equal(dbContacts[0].Content, respContact.Content)
+ },
+ },
+ ).Close()
+}
+
+func TestGetContactFailsOnContactIdNotExist(t *testing.T) {
+ appAssert, _, _ := CreateSampleContact(h.InitTest(t))
+
+ uuid := uuid.New()
+
+ appAssert.TestOnErrorAndTester(h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: fmt.Sprintf("/api/v1/contacts/%s", uuid),
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.ContactNotFound,
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ var contact models.Contact
+
+ err := eaa.App.Conn.Where("id = ?", uuid).First(&contact).Error
+
+ eaa.Assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
+ },
+ },
+ ).Close()
+}
+
+func TestDeleteContactWorks(t *testing.T) {
+ appAssert, _, contactUUID := CreateSampleContact(h.InitTest(t))
+
+ appAssert.TestOnStatusAndTester(h.TestRequest{
+ Method: fiber.MethodDelete,
+ Path: fmt.Sprintf("/api/v1/contacts/%s", contactUUID),
+ Role: &models.Super,
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusNoContent,
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ var contact models.Contact
+
+ err := eaa.App.Conn.Where("id = ?", contactUUID).First(&contact).Error
+
+ eaa.Assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
+ },
+ },
+ ).Close()
+}
+
+func TestDeleteContactFailsOnContactIdNotExist(t *testing.T) {
+ appAssert, _, _ := CreateSampleContact(h.InitTest(t))
+ uuid := uuid.New()
+
+ appAssert.TestOnErrorAndTester(h.TestRequest{
+ Method: fiber.MethodDelete,
+ Path: fmt.Sprintf("/api/v1/contacts/%s", uuid),
+ Role: &models.Super,
+ },
+ h.ErrorWithTester{
+ Error: errors.ContactNotFound,
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ var contact models.Contact
+ err := eaa.App.Conn.Where("id = ?", uuid).First(&contact).Error
+ eaa.Assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound))
+
+ AssertNumContactsRemainsAtN(eaa, resp, 1)
+ },
+ },
+ ).Close()
+}
+
+// test that the request returns paginated contacts
+func TestGetContactsWorks(t *testing.T) {
+ appAssert, _, _ := CreateManyContacts(h.InitTest(t))
+
+ appAssert.TestOnStatus(h.TestRequest{
+ Method: fiber.MethodGet,
+ Path: "/api/v1/contacts",
+ Role: &models.Super,
+ }, fiber.StatusOK,
+ ).Close()
+}
diff --git a/backend/tests/api/helpers/requests.go b/backend/tests/api/helpers/requests.go
index 5920ec6fe..2071b8ef7 100644
--- a/backend/tests/api/helpers/requests.go
+++ b/backend/tests/api/helpers/requests.go
@@ -125,7 +125,7 @@ type ErrorWithTester struct {
Tester Tester
}
-func (existingAppAssert ExistingAppAssert) TestOnErrorAndDB(request TestRequest, errorWithTester ErrorWithTester) ExistingAppAssert {
+func (existingAppAssert ExistingAppAssert) TestOnErrorAndTester(request TestRequest, errorWithTester ErrorWithTester) ExistingAppAssert {
appAssert, resp := request.testOn(existingAppAssert, errorWithTester.Error.StatusCode, "error", errorWithTester.Error.Message)
errorWithTester.Tester(appAssert, resp)
return appAssert
diff --git a/backend/tests/api/tag_test.go b/backend/tests/api/tag_test.go
index 3004c459f..77c42fea7 100644
--- a/backend/tests/api/tag_test.go
+++ b/backend/tests/api/tag_test.go
@@ -111,7 +111,7 @@ func TestCreateTagFailsBadRequest(t *testing.T) {
for _, badBody := range badBodys {
badBody := badBody
- appAssert.TestOnErrorAndDB(
+ appAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/tags/",
@@ -143,7 +143,7 @@ func TestCreateTagFailsValidation(t *testing.T) {
for _, badBody := range badBodys {
badBody := badBody
- appAssert.TestOnErrorAndDB(
+ appAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/tags/",
@@ -356,7 +356,7 @@ func TestDeleteTagFailsBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- appAssert.TestOnErrorAndDB(
+ appAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/tags/%s", badRequest),
@@ -375,7 +375,7 @@ func TestDeleteTagFailsBadRequest(t *testing.T) {
func TestDeleteTagFailsNotFound(t *testing.T) {
appAssert, _, _ := CreateSampleTag(h.InitTest(t))
- appAssert.TestOnErrorAndDB(
+ appAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/tags/%s", uuid.New()),
diff --git a/backend/tests/api/user_tag_test.go b/backend/tests/api/user_tag_test.go
index 77944020d..a7196e318 100644
--- a/backend/tests/api/user_tag_test.go
+++ b/backend/tests/api/user_tag_test.go
@@ -230,7 +230,7 @@ func TestCreateUserTagsFailsOnInvalidKey(t *testing.T) {
func TestCreateUserTagsFailsOnNonExistentUser(t *testing.T) {
uuid := uuid.New()
- h.InitTest(t).TestOnErrorAndDB(
+ h.InitTest(t).TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: fmt.Sprintf("/api/v1/users/%s/tags/", uuid),
diff --git a/backend/tests/api/user_test.go b/backend/tests/api/user_test.go
index 7213d247a..7f790c49e 100644
--- a/backend/tests/api/user_test.go
+++ b/backend/tests/api/user_test.go
@@ -137,7 +137,7 @@ func TestGetUserFailsBadRequest(t *testing.T) {
func TestGetUserFailsNotExist(t *testing.T) {
uuid := uuid.New()
- h.InitTest(t).TestOnErrorAndDB(
+ h.InitTest(t).TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodGet,
Path: fmt.Sprintf("/api/v1/users/%s", uuid),
@@ -216,7 +216,7 @@ func TestUpdateUserFailsOnInvalidBody(t *testing.T) {
{"college": "UT-Austin"},
} {
invalidData := invalidData
- h.InitTest(t).TestOnErrorAndDB(
+ h.InitTest(t).TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPatch,
Path: "/api/v1/users/:userID",
@@ -262,7 +262,7 @@ func TestUpdateUserFailsOnIdNotExist(t *testing.T) {
sampleStudent, rawPassword := h.SampleStudentFactory()
- h.InitTest(t).TestOnErrorAndDB(
+ h.InitTest(t).TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPatch,
Path: fmt.Sprintf("/api/v1/users/%s", uuid),
@@ -300,7 +300,7 @@ func TestDeleteUserWorks(t *testing.T) {
func TestDeleteUserNotExist(t *testing.T) {
uuid := uuid.New()
- h.InitTest(t).TestOnErrorAndDB(h.TestRequest{
+ h.InitTest(t).TestOnErrorAndTester(h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/users/%s", uuid),
Role: &models.Super,
@@ -332,7 +332,7 @@ func TestDeleteUserBadRequest(t *testing.T) {
}
for _, badRequest := range badRequests {
- appAssert.TestOnErrorAndDB(
+ appAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/users/%s", badRequest),
@@ -449,7 +449,7 @@ func TestCreateUserFailsIfUserWithEmailAlreadyExists(t *testing.T) {
(*body)["id"] = studentUUID
- appAssert.TestOnErrorAndDB(
+ appAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/users/",
@@ -474,7 +474,7 @@ func TestCreateUserFailsIfUserWithNUIDAlreadyExists(t *testing.T) {
(*slightlyDifferentSampleStudentJSON)["last_name"] = "Doe"
(*slightlyDifferentSampleStudentJSON)["email"] = "doe.john@northeastern.edu"
- appAssert.TestOnErrorAndDB(
+ appAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/users/",
@@ -497,7 +497,7 @@ func AssertCreateBadDataFails(t *testing.T, jsonKey string, badValues []interfac
sampleUserPermutation := *h.SampleStudentJSONFactory(sampleStudent, rawPassword)
sampleUserPermutation[jsonKey] = badValue
- appAssert.TestOnErrorAndDB(
+ appAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/users/",
@@ -591,7 +591,7 @@ func TestCreateUserFailsOnMissingFields(t *testing.T) {
sampleUserPermutation := *h.SampleStudentJSONFactory(sampleStudent, rawPassword)
delete(sampleUserPermutation, missingField)
- appAssert.TestOnErrorAndDB(
+ appAssert.TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/users/",
diff --git a/cli/commands/lint.go b/cli/commands/lint.go
index e6836ce55..921a45eb8 100644
--- a/cli/commands/lint.go
+++ b/cli/commands/lint.go
@@ -99,7 +99,7 @@ func Lint(folder string, runFrontend bool, runBackend bool) error {
func BackendLint() error {
fmt.Println("Linting backend")
- cmd := exec.Command("golangci-lint", "run")
+ cmd := exec.Command("golangci-lint", "run", "--fix")
cmd.Dir = BACKEND_DIR
err := cmd.Run()
diff --git a/go.work.sum b/go.work.sum
index a6e34c243..494c07de0 100644
--- a/go.work.sum
+++ b/go.work.sum
@@ -1,30 +1,106 @@
+github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/gorilla/schema v1.1.0 h1:CamqUDOFUBqzrvxuz2vEwo8+SUdwsluFh7IlzJh30LY=
+github.com/gorilla/schema v1.1.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
+github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/sagikazarmark/crypt v0.17.0 h1:ZA/7pXyjkHoK4bW4mIdnCLvL8hd+Nrbiw7Dqk7D4qUk=
+github.com/sagikazarmark/crypt v0.17.0/go.mod h1:SMtHTvdmsZMuY/bpZoqokSoChIrcJ/epOxZN58PbZDg=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
+github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04=
+github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E=
+github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
+github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
+github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.etcd.io/etcd/api/v3 v3.5.10 h1:szRajuUUbLyppkhs9K6BRtjY37l66XQQmw7oZRANE4k=
+go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI=
+go.etcd.io/etcd/client/pkg/v3 v3.5.10 h1:kfYIdQftBnbAq8pUWFXfpuuxFSKzlmM5cSn76JByiT0=
+go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U=
+go.etcd.io/etcd/client/v2 v2.305.10 h1:MrmRktzv/XF8CvtQt+P6wLUlURaNpSDJHFZhe//2QE4=
+go.etcd.io/etcd/client/v2 v2.305.10/go.mod h1:m3CKZi69HzilhVqtPDcjhSGp+kA1OmbNn0qamH80xjA=
+go.etcd.io/etcd/client/v3 v3.5.10 h1:W9TXNZ+oB3MCd/8UjxHTWK5J9Nquw9fQBLJd5ne5/Ao=
+go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc=
+go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
+go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
+go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ=
+golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM=
+golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
+golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
+golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
+golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
+google.golang.org/api v0.153.0 h1:N1AwGhielyKFaUqH07/ZSIQR3uNPcV7NVw0vj+j4iR4=
+google.golang.org/api v0.153.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY=
+google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo=
+gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0=
+gorm.io/gorm v1.25.6 h1:V92+vVda1wEISSOMtodHVRcUIOPYa2tgQtyF+DfFx+A=
+gorm.io/gorm v1.25.6/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
+sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
From 6ca4f1180d891c32802d161f5f67d0802ddd96e1 Mon Sep 17 00:00:00 2001
From: Zackary Lassetter <93090968+zacklassetter@users.noreply.github.com>
Date: Sun, 11 Feb 2024 14:01:34 -0500
Subject: [PATCH 33/33] Finished follower endpoints (#142)
Co-authored-by: Mai Nguyen
Co-authored-by: garrettladley
Co-authored-by: mai
Co-authored-by: Garrett Ladley <92384606+garrettladley@users.noreply.github.com>
---
backend/src/controllers/category.go | 20 ++++----
backend/src/controllers/category_tag.go | 8 +--
backend/src/controllers/club_follower.go | 28 +++++++++++
backend/src/controllers/user_follower.go | 38 ++++++++++++++
backend/src/controllers/user_tag.go | 8 +--
backend/src/errors/club.go | 4 ++
backend/src/errors/user.go | 4 ++
backend/src/middleware/club.go | 2 +-
backend/src/models/user.go | 2 +-
backend/src/server/routes/club_follower.go | 16 ++++++
backend/src/server/routes/user_follower.go | 18 +++++++
backend/src/server/server.go | 2 +
backend/src/services/club.go | 9 ++++
backend/src/services/club_follower.go | 42 ++++++++++++++++
backend/src/services/user.go | 32 ++++++++++++
backend/src/services/user_follower.go | 58 ++++++++++++++++++++++
backend/src/transactions/club.go | 51 +++++++++++++++++++
backend/src/transactions/club_contact.go | 2 -
backend/src/transactions/club_follower.go | 14 ++++++
backend/src/transactions/contacts.go | 39 ---------------
backend/src/transactions/user.go | 13 +++++
backend/src/transactions/user_follower.go | 57 +++++++++++++++++++++
backend/tests/api/club_follower_test.go | 1 +
backend/tests/api/club_test.go | 2 +-
backend/tests/api/user_follower_test.go | 46 +++++++++++++++++
backend/tests/auth_test.go | 2 +-
go.work.sum | 1 +
27 files changed, 456 insertions(+), 63 deletions(-)
create mode 100644 backend/src/controllers/club_follower.go
create mode 100644 backend/src/controllers/user_follower.go
create mode 100644 backend/src/server/routes/club_follower.go
create mode 100644 backend/src/server/routes/user_follower.go
create mode 100644 backend/src/services/club_follower.go
create mode 100644 backend/src/services/user_follower.go
create mode 100644 backend/src/transactions/club_follower.go
create mode 100644 backend/src/transactions/user_follower.go
create mode 100644 backend/tests/api/club_follower_test.go
create mode 100644 backend/tests/api/user_follower_test.go
diff --git a/backend/src/controllers/category.go b/backend/src/controllers/category.go
index c06eef82d..40858ea89 100644
--- a/backend/src/controllers/category.go
+++ b/backend/src/controllers/category.go
@@ -31,14 +31,14 @@ func NewCategoryController(categoryService services.CategoryServiceInterface) *C
// @Failure 400 {string} string "category with that name already exists"
// @Failure 500 {string} string "failed to create category"
// @Router /api/v1/category/ [post]
-func (t *CategoryController) CreateCategory(c *fiber.Ctx) error {
+func (cat *CategoryController) CreateCategory(c *fiber.Ctx) error {
var categoryBody models.CategoryRequestBody
if err := c.BodyParser(&categoryBody); err != nil {
return errors.FailedToParseRequestBody.FiberError(c)
}
- newCategory, err := t.categoryService.CreateCategory(categoryBody)
+ newCategory, err := cat.categoryService.CreateCategory(categoryBody)
if err != nil {
return err.FiberError(c)
}
@@ -56,11 +56,11 @@ func (t *CategoryController) CreateCategory(c *fiber.Ctx) error {
// @Success 200 {object} []models.Category
// @Failure 500 {string} string "unable to retrieve categories"
// @Router /api/v1/category/ [get]
-func (t *CategoryController) GetCategories(c *fiber.Ctx) error {
+func (cat *CategoryController) GetCategories(c *fiber.Ctx) error {
defaultLimit := 10
defaultPage := 1
- categories, err := t.categoryService.GetCategories(c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage)))
+ categories, err := cat.categoryService.GetCategories(c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage)))
if err != nil {
return err.FiberError(c)
}
@@ -80,8 +80,8 @@ func (t *CategoryController) GetCategories(c *fiber.Ctx) error {
// @Failure 404 {string} string "faied to find category"
// @Failure 500 {string} string "failed to retrieve category"
// @Router /api/v1/category/{id} [get]
-func (t *CategoryController) GetCategory(c *fiber.Ctx) error {
- category, err := t.categoryService.GetCategory(c.Params("categoryID"))
+func (cat *CategoryController) GetCategory(c *fiber.Ctx) error {
+ category, err := cat.categoryService.GetCategory(c.Params("categoryID"))
if err != nil {
return err.FiberError(c)
}
@@ -101,8 +101,8 @@ func (t *CategoryController) GetCategory(c *fiber.Ctx) error {
// @Failure 404 {string} string "failed to find category"
// @Failure 500 {string} string "failed to delete category"
// @Router /api/v1/category/{id} [delete]
-func (t *CategoryController) DeleteCategory(c *fiber.Ctx) error {
- if err := t.categoryService.DeleteCategory(c.Params("categoryID")); err != nil {
+func (cat *CategoryController) DeleteCategory(c *fiber.Ctx) error {
+ if err := cat.categoryService.DeleteCategory(c.Params("categoryID")); err != nil {
return err.FiberError(c)
}
@@ -121,14 +121,14 @@ func (t *CategoryController) DeleteCategory(c *fiber.Ctx) error {
// @Failure 404 {string} string "failed to find category"
// @Failure 500 {string} string "failed to update category"
// @Router /api/v1/category/{id} [patch]
-func (t *CategoryController) UpdateCategory(c *fiber.Ctx) error {
+func (cat *CategoryController) UpdateCategory(c *fiber.Ctx) error {
var category models.CategoryRequestBody
if err := c.BodyParser(&category); err != nil {
return errors.FailedToValidateCategory.FiberError(c)
}
- updatedCategory, err := t.categoryService.UpdateCategory(c.Params("categoryID"), category)
+ updatedCategory, err := cat.categoryService.UpdateCategory(c.Params("categoryID"), category)
if err != nil {
return err.FiberError(c)
}
diff --git a/backend/src/controllers/category_tag.go b/backend/src/controllers/category_tag.go
index 0c3fa7a27..65c189ae6 100644
--- a/backend/src/controllers/category_tag.go
+++ b/backend/src/controllers/category_tag.go
@@ -16,11 +16,11 @@ func NewCategoryTagController(categoryTagService services.CategoryTagServiceInte
return &CategoryTagController{categoryTagService: categoryTagService}
}
-func (t *CategoryTagController) GetTagsByCategory(c *fiber.Ctx) error {
+func (ct *CategoryTagController) GetTagsByCategory(c *fiber.Ctx) error {
defaultLimit := 10
defaultPage := 1
- tags, err := t.categoryTagService.GetTagsByCategory(c.Params("categoryID"), c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage)))
+ tags, err := ct.categoryTagService.GetTagsByCategory(c.Params("categoryID"), c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage)))
if err != nil {
return err.FiberError(c)
}
@@ -28,8 +28,8 @@ func (t *CategoryTagController) GetTagsByCategory(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(&tags)
}
-func (t *CategoryTagController) GetTagByCategory(c *fiber.Ctx) error {
- tag, err := t.categoryTagService.GetTagByCategory(c.Params("categoryID"), c.Params("tagID"))
+func (ct *CategoryTagController) GetTagByCategory(c *fiber.Ctx) error {
+ tag, err := ct.categoryTagService.GetTagByCategory(c.Params("categoryID"), c.Params("tagID"))
if err != nil {
return err.FiberError(c)
}
diff --git a/backend/src/controllers/club_follower.go b/backend/src/controllers/club_follower.go
new file mode 100644
index 000000000..76239e150
--- /dev/null
+++ b/backend/src/controllers/club_follower.go
@@ -0,0 +1,28 @@
+package controllers
+
+import (
+ "strconv"
+
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+type ClubFollowerController struct {
+ clubFollowerService services.ClubFollowerServiceInterface
+}
+
+func NewClubFollowerController(clubFollowerService services.ClubFollowerServiceInterface) *ClubFollowerController {
+ return &ClubFollowerController{clubFollowerService: clubFollowerService}
+}
+
+func (cf *ClubFollowerController) GetClubFollowers(c *fiber.Ctx) error {
+ defaultLimit := 10
+ defaultPage := 1
+
+ followers, err := cf.clubFollowerService.GetClubFollowers(c.Params("clubID"), c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage)))
+ if err != nil {
+ return err.FiberError(c)
+ }
+
+ return c.Status(fiber.StatusOK).JSON(followers)
+}
diff --git a/backend/src/controllers/user_follower.go b/backend/src/controllers/user_follower.go
new file mode 100644
index 000000000..67c0ff36e
--- /dev/null
+++ b/backend/src/controllers/user_follower.go
@@ -0,0 +1,38 @@
+package controllers
+
+import (
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+type UserFollowerController struct {
+ userFollowerService services.UserFollowerServiceInterface
+}
+
+func NewUserFollowerController(userFollowerService services.UserFollowerServiceInterface) *UserFollowerController {
+ return &UserFollowerController{userFollowerService: userFollowerService}
+}
+
+func (uf *UserFollowerController) CreateFollowing(c *fiber.Ctx) error {
+ err := uf.userFollowerService.CreateFollowing(c.Params("userID"), c.Params("clubID"))
+ if err != nil {
+ return err.FiberError(c)
+ }
+ return c.SendStatus(fiber.StatusCreated)
+}
+
+func (uf *UserFollowerController) DeleteFollowing(c *fiber.Ctx) error {
+ err := uf.userFollowerService.DeleteFollowing(c.Params("userID"), c.Params("clubID"))
+ if err != nil {
+ return err.FiberError(c)
+ }
+ return c.SendStatus(fiber.StatusNoContent)
+}
+
+func (uf *UserFollowerController) GetAllFollowing(c *fiber.Ctx) error {
+ clubs, err := uf.userFollowerService.GetFollowing(c.Params("userID"))
+ if err != nil {
+ return err.FiberError(c)
+ }
+ return c.Status(fiber.StatusOK).JSON(clubs)
+}
diff --git a/backend/src/controllers/user_tag.go b/backend/src/controllers/user_tag.go
index eb1f7bd0b..228baa77a 100644
--- a/backend/src/controllers/user_tag.go
+++ b/backend/src/controllers/user_tag.go
@@ -15,21 +15,21 @@ func NewUserTagController(userTagService services.UserTagServiceInterface) *User
return &UserTagController{userTagService: userTagService}
}
-func (u *UserTagController) GetUserTags(c *fiber.Ctx) error {
- tags, err := u.userTagService.GetUserTags(c.Params("userID"))
+func (ut *UserTagController) GetUserTags(c *fiber.Ctx) error {
+ tags, err := ut.userTagService.GetUserTags(c.Params("userID"))
if err != nil {
return err.FiberError(c)
}
return c.Status(fiber.StatusOK).JSON(&tags)
}
-func (u *UserTagController) CreateUserTags(c *fiber.Ctx) error {
+func (ut *UserTagController) CreateUserTags(c *fiber.Ctx) error {
var requestBody models.CreateUserTagsBody
if err := c.BodyParser(&requestBody); err != nil {
return errors.FailedToParseRequestBody.FiberError(c)
}
- tags, err := u.userTagService.CreateUserTags(c.Params("userID"), requestBody)
+ tags, err := ut.userTagService.CreateUserTags(c.Params("userID"), requestBody)
if err != nil {
return err.FiberError(c)
}
diff --git a/backend/src/errors/club.go b/backend/src/errors/club.go
index aef90251c..982b16795 100644
--- a/backend/src/errors/club.go
+++ b/backend/src/errors/club.go
@@ -40,4 +40,8 @@ var (
StatusCode: fiber.StatusInternalServerError,
Message: "failed to get admin ids",
}
+ FailedToGetClubFollowers = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "failed to get club followers",
+ }
)
diff --git a/backend/src/errors/user.go b/backend/src/errors/user.go
index de07afbb7..dbc70d6d8 100644
--- a/backend/src/errors/user.go
+++ b/backend/src/errors/user.go
@@ -43,4 +43,8 @@ var (
StatusCode: fiber.StatusInternalServerError,
Message: "failed to compute password hash",
}
+ FailedToGetUserFollowing = Error{
+ StatusCode: fiber.StatusInternalServerError,
+ Message: "failed to get user following",
+ }
)
diff --git a/backend/src/middleware/club.go b/backend/src/middleware/club.go
index dc848b75d..f645a79a9 100644
--- a/backend/src/middleware/club.go
+++ b/backend/src/middleware/club.go
@@ -32,7 +32,7 @@ func (m *MiddlewareService) ClubAuthorizeById(c *fiber.Ctx) error {
return errors.FailedToParseAccessToken.FiberError(c)
}
- // use club_id to get the list of admin for a certain club
+ // use clubID to get the list of admin for a certain club
clubAdmin, clubErr := transactions.GetAdminIDs(m.DB, *clubUUID)
if clubErr != nil {
return err
diff --git a/backend/src/models/user.go b/backend/src/models/user.go
index fefd911b6..bff5d2c24 100644
--- a/backend/src/models/user.go
+++ b/backend/src/models/user.go
@@ -49,7 +49,7 @@ type User struct {
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:"-"`
Member []Club `gorm:"many2many:user_club_members;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
- Follower []Club `gorm:"many2many:user_club_followers;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
+ Follower []Club `gorm:"many2many:user_club_followers;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"clubs_followed,omitempty" validate:"-"`
IntendedApplicant []Club `gorm:"many2many:user_club_intended_applicants;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
Asked []Comment `gorm:"foreignKey:AskedByID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"-" validate:"-"`
Answered []Comment `gorm:"foreignKey:AnsweredByID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"-" validate:"-"`
diff --git a/backend/src/server/routes/club_follower.go b/backend/src/server/routes/club_follower.go
new file mode 100644
index 000000000..4050538e2
--- /dev/null
+++ b/backend/src/server/routes/club_follower.go
@@ -0,0 +1,16 @@
+package routes
+
+import (
+ "github.com/GenerateNU/sac/backend/src/controllers"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+func ClubFollower(clubsIDRouter fiber.Router, clubFollowerService services.ClubFollowerServiceInterface) {
+ clubFollowerController := controllers.NewClubFollowerController(clubFollowerService)
+
+ clubFollower := clubsIDRouter.Group("/followers")
+
+ // api/clubs/:clubID/followers/*
+ clubFollower.Get("/", clubFollowerController.GetClubFollowers)
+}
diff --git a/backend/src/server/routes/user_follower.go b/backend/src/server/routes/user_follower.go
new file mode 100644
index 000000000..a6f355280
--- /dev/null
+++ b/backend/src/server/routes/user_follower.go
@@ -0,0 +1,18 @@
+package routes
+
+import (
+ "github.com/GenerateNU/sac/backend/src/controllers"
+ "github.com/GenerateNU/sac/backend/src/services"
+ "github.com/gofiber/fiber/v2"
+)
+
+func UserFollower(usersIDRouter fiber.Router, userFollowerService services.UserFollowerServiceInterface) {
+ userFollowerController := controllers.NewUserFollowerController(userFollowerService)
+
+ userFollower := usersIDRouter.Group("/:userID/follower")
+
+ // api/v1/users/:userID/follower/*
+ userFollower.Post("/:clubID", userFollowerController.CreateFollowing)
+ userFollower.Delete("/:clubID", userFollowerController.DeleteFollowing)
+ userFollower.Get("/", userFollowerController.GetAllFollowing)
+}
diff --git a/backend/src/server/server.go b/backend/src/server/server.go
index c1a8d08ab..c8287fd2a 100644
--- a/backend/src/server/server.go
+++ b/backend/src/server/server.go
@@ -42,10 +42,12 @@ func Init(db *gorm.DB, settings config.Settings) *fiber.App {
userRouter := routes.User(apiv1, services.NewUserService(db, validate), middlewareService)
routes.UserTag(userRouter, services.NewUserTagService(db, validate))
+ routes.UserFollower(userRouter, services.NewUserFollowerService(db, validate))
routes.Contact(apiv1, services.NewContactService(db, validate))
clubsRouter := routes.Club(apiv1, services.NewClubService(db, validate), middlewareService)
+ routes.ClubFollower(clubsRouter, services.NewClubFollowerService(db, validate))
routes.ClubContact(clubsRouter, services.NewClubContactService(db, validate))
routes.Tag(apiv1, services.NewTagService(db, validate))
diff --git a/backend/src/services/club.go b/backend/src/services/club.go
index c05610c1f..011a1f6fa 100644
--- a/backend/src/services/club.go
+++ b/backend/src/services/club.go
@@ -16,6 +16,7 @@ type ClubServiceInterface interface {
CreateClub(clubBody models.CreateClubRequestBody) (*models.Club, *errors.Error)
UpdateClub(id string, clubBody models.UpdateClubRequestBody) (*models.Club, *errors.Error)
DeleteClub(id string) *errors.Error
+ GetUserFollowersForClub(id string) ([]models.User, *errors.Error)
}
type ClubService struct {
@@ -91,3 +92,11 @@ func (c *ClubService) DeleteClub(id string) *errors.Error {
return transactions.DeleteClub(c.DB, *idAsUUID)
}
+
+func (c *ClubService) GetUserFollowersForClub(id string) ([]models.User, *errors.Error) {
+ idAsUUID, err := utilities.ValidateID(id)
+ if err != nil {
+ return nil, &errors.FailedToValidateID
+ }
+ return transactions.GetUserFollowersForClub(c.DB, *idAsUUID)
+}
diff --git a/backend/src/services/club_follower.go b/backend/src/services/club_follower.go
new file mode 100644
index 000000000..1cd636e04
--- /dev/null
+++ b/backend/src/services/club_follower.go
@@ -0,0 +1,42 @@
+package services
+
+import (
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/GenerateNU/sac/backend/src/transactions"
+ "github.com/GenerateNU/sac/backend/src/utilities"
+ "github.com/go-playground/validator/v10"
+ "gorm.io/gorm"
+)
+
+type ClubFollowerServiceInterface interface {
+ GetClubFollowers(clubID string, limit string, page string) ([]models.User, *errors.Error)
+}
+
+type ClubFollowerService struct {
+ DB *gorm.DB
+ Validate *validator.Validate
+}
+
+func NewClubFollowerService(db *gorm.DB, validate *validator.Validate) *ClubFollowerService {
+ return &ClubFollowerService{DB: db, Validate: validate}
+}
+
+func (cf *ClubFollowerService) GetClubFollowers(clubID string, limit string, page string) ([]models.User, *errors.Error) {
+ idAsUUID, err := utilities.ValidateID(clubID)
+ if err != nil {
+ return nil, &errors.FailedToValidateID
+ }
+
+ limitAsInt, err := utilities.ValidateNonNegative(limit)
+ if err != nil {
+ return nil, &errors.FailedToValidateLimit
+ }
+
+ pageAsInt, err := utilities.ValidateNonNegative(page)
+ if err != nil {
+ return nil, &errors.FailedToValidatePage
+ }
+
+ return transactions.GetClubFollowers(cf.DB, *idAsUUID, *limitAsInt, *pageAsInt)
+}
diff --git a/backend/src/services/user.go b/backend/src/services/user.go
index a940a6b33..0a9a6f79a 100644
--- a/backend/src/services/user.go
+++ b/backend/src/services/user.go
@@ -19,6 +19,8 @@ type UserServiceInterface interface {
GetUser(id string) (*models.User, *errors.Error)
UpdateUser(id string, userBody models.UpdateUserRequestBody) (*models.User, *errors.Error)
DeleteUser(id string) *errors.Error
+ GetUserTags(id string) ([]models.Tag, *errors.Error)
+ CreateUserTags(id string, tagIDs models.CreateUserTagsBody) ([]models.Tag, *errors.Error)
}
type UserService struct {
@@ -102,3 +104,33 @@ func (u *UserService) DeleteUser(id string) *errors.Error {
return transactions.DeleteUser(u.DB, *idAsUUID)
}
+
+func (u *UserService) GetUserTags(id string) ([]models.Tag, *errors.Error) {
+ idAsUUID, err := utilities.ValidateID(id)
+ if err != nil {
+ return nil, err
+ }
+
+ return transactions.GetUserTags(u.DB, *idAsUUID)
+}
+
+func (u *UserService) CreateUserTags(id string, tagIDs models.CreateUserTagsBody) ([]models.Tag, *errors.Error) {
+ // Validate the id:
+ idAsUUID, err := utilities.ValidateID(id)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := u.Validate.Struct(tagIDs); err != nil {
+ return nil, &errors.FailedToValidateUserTags
+ }
+
+ // Retrieve a list of valid tags from the ids:
+ tags, err := transactions.GetTagsByIDs(u.DB, tagIDs.Tags)
+ if err != nil {
+ return nil, err
+ }
+
+ // Update the user to reflect the new tags:
+ return transactions.CreateUserTags(u.DB, *idAsUUID, tags)
+}
diff --git a/backend/src/services/user_follower.go b/backend/src/services/user_follower.go
new file mode 100644
index 000000000..aea27ab2f
--- /dev/null
+++ b/backend/src/services/user_follower.go
@@ -0,0 +1,58 @@
+package services
+
+import (
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/GenerateNU/sac/backend/src/transactions"
+ "github.com/GenerateNU/sac/backend/src/utilities"
+ "github.com/go-playground/validator/v10"
+ "gorm.io/gorm"
+)
+
+type UserFollowerServiceInterface interface {
+ CreateFollowing(userId string, clubId string) *errors.Error
+ DeleteFollowing(userId string, clubId string) *errors.Error
+ GetFollowing(userId string) ([]models.Club, *errors.Error)
+}
+
+type UserFollowerService struct {
+ DB *gorm.DB
+ Validate *validator.Validate
+}
+
+func NewUserFollowerService(db *gorm.DB, validate *validator.Validate) *UserFollowerService {
+ return &UserFollowerService{DB: db, Validate: validate}
+}
+
+func (u *UserFollowerService) CreateFollowing(userId string, clubId string) *errors.Error {
+ userIdAsUUID, err := utilities.ValidateID(userId)
+ if err != nil {
+ return err
+ }
+ clubIdAsUUID, err := utilities.ValidateID(clubId)
+ if err != nil {
+ return err
+ }
+ return transactions.CreateFollowing(u.DB, *userIdAsUUID, *clubIdAsUUID)
+}
+
+func (u *UserFollowerService) DeleteFollowing(userId string, clubId string) *errors.Error {
+ userIdAsUUID, err := utilities.ValidateID(userId)
+ if err != nil {
+ return err
+ }
+ clubIdAsUUID, err := utilities.ValidateID(clubId)
+ if err != nil {
+ return err
+ }
+ return transactions.DeleteFollowing(u.DB, *userIdAsUUID, *clubIdAsUUID)
+}
+
+func (u *UserFollowerService) GetFollowing(userId string) ([]models.Club, *errors.Error) {
+ userIdAsUUID, err := utilities.ValidateID(userId)
+ if err != nil {
+ return nil, err
+ }
+
+ return transactions.GetClubFollowing(u.DB, *userIdAsUUID)
+}
diff --git a/backend/src/transactions/club.go b/backend/src/transactions/club.go
index 418101a34..4b2908ef1 100644
--- a/backend/src/transactions/club.go
+++ b/backend/src/transactions/club.go
@@ -73,3 +73,54 @@ func GetClub(db *gorm.DB, id uuid.UUID) (*models.Club, *errors.Error) {
return &club, nil
}
+
+func UpdateClub(db *gorm.DB, id uuid.UUID, club models.Club) (*models.Club, *errors.Error) {
+ result := db.Model(&models.User{}).Where("id = ?", id).Updates(club)
+ if result.Error != nil {
+ if stdliberrors.Is(result.Error, gorm.ErrRecordNotFound) {
+ return nil, &errors.UserNotFound
+ } else {
+ return nil, &errors.FailedToUpdateClub
+ }
+ }
+ var existingClub models.Club
+
+ err := db.First(&existingClub, id).Error
+ if err != nil {
+ if stdliberrors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, &errors.ClubNotFound
+ } else {
+ return nil, &errors.FailedToCreateClub
+ }
+ }
+
+ if err := db.Model(&existingClub).Updates(&club).Error; err != nil {
+ return nil, &errors.FailedToUpdateUser
+ }
+
+ return &existingClub, nil
+}
+
+func DeleteClub(db *gorm.DB, id uuid.UUID) *errors.Error {
+ if result := db.Delete(&models.Club{}, id); result.RowsAffected == 0 {
+ if result.Error == nil {
+ return &errors.ClubNotFound
+ } else {
+ return &errors.FailedToDeleteClub
+ }
+ }
+ return nil
+}
+
+func GetUserFollowersForClub(db *gorm.DB, club_id uuid.UUID) ([]models.User, *errors.Error) {
+ var users []models.User
+ club, err := GetClub(db, club_id)
+ if err != nil {
+ return nil, &errors.ClubNotFound
+ }
+
+ if err := db.Model(&club).Association("Follower").Find(&users); err != nil {
+ return nil, &errors.FailedToGetClubFollowers
+ }
+ return users, nil
+}
diff --git a/backend/src/transactions/club_contact.go b/backend/src/transactions/club_contact.go
index 7cb7afb87..d77f4de7d 100644
--- a/backend/src/transactions/club_contact.go
+++ b/backend/src/transactions/club_contact.go
@@ -2,7 +2,6 @@ package transactions
import (
stdliberrors "errors"
- "fmt"
"github.com/GenerateNU/sac/backend/src/errors"
"github.com/GenerateNU/sac/backend/src/models"
@@ -17,7 +16,6 @@ func PutClubContact(db *gorm.DB, contact models.Contact) (*models.Contact, *erro
DoUpdates: clause.AssignmentColumns([]string{"content"}),
}).Create(&contact).Error
if err != nil {
- fmt.Println(err)
if stdliberrors.Is(err, gorm.ErrRecordNotFound) || stdliberrors.Is(err, gorm.ErrForeignKeyViolated) {
return nil, &errors.ClubNotFound
} else {
diff --git a/backend/src/transactions/club_follower.go b/backend/src/transactions/club_follower.go
new file mode 100644
index 000000000..a2e747c80
--- /dev/null
+++ b/backend/src/transactions/club_follower.go
@@ -0,0 +1,14 @@
+package transactions
+
+import (
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/google/uuid"
+ "gorm.io/gorm"
+)
+
+func GetClubFollowers(db *gorm.DB, clubID uuid.UUID, limit int, page int) ([]models.User, *errors.Error) {
+ var users []models.User
+
+ return users, nil
+}
diff --git a/backend/src/transactions/contacts.go b/backend/src/transactions/contacts.go
index 51ac2bb00..a47405fae 100644
--- a/backend/src/transactions/contacts.go
+++ b/backend/src/transactions/contacts.go
@@ -42,42 +42,3 @@ func DeleteContact(db *gorm.DB, id uuid.UUID) *errors.Error {
}
return nil
}
-
-func UpdateClub(db *gorm.DB, id uuid.UUID, club models.Club) (*models.Club, *errors.Error) {
- result := db.Model(&models.User{}).Where("id = ?", id).Updates(club)
- if result.Error != nil {
- if stdliberrors.Is(result.Error, gorm.ErrRecordNotFound) {
- return nil, &errors.UserNotFound
- } else {
- return nil, &errors.FailedToUpdateClub
- }
- }
- var existingClub models.Club
-
- err := db.First(&existingClub, id).Error
- if err != nil {
- if stdliberrors.Is(err, gorm.ErrRecordNotFound) {
- return nil, &errors.ClubNotFound
- } else {
- return nil, &errors.FailedToCreateClub
- }
- }
-
- if err := db.Model(&existingClub).Updates(&club).Error; err != nil {
- return nil, &errors.FailedToUpdateUser
- }
-
- return &existingClub, nil
-}
-
-func DeleteClub(db *gorm.DB, id uuid.UUID) *errors.Error {
- if result := db.Delete(&models.Club{}, id); result.RowsAffected == 0 {
- if result.Error == nil {
- return &errors.ClubNotFound
- } else {
- return &errors.FailedToDeleteClub
- }
- }
-
- return nil
-}
diff --git a/backend/src/transactions/user.go b/backend/src/transactions/user.go
index 14d52e2f5..8f161681d 100644
--- a/backend/src/transactions/user.go
+++ b/backend/src/transactions/user.go
@@ -55,6 +55,19 @@ func GetUser(db *gorm.DB, id uuid.UUID) (*models.User, *errors.Error) {
return &user, nil
}
+func GetUserWithFollowers(db *gorm.DB, id uuid.UUID) (*models.User, *errors.Error) {
+ var user models.User
+ if err := db.Preload("Follower").Omit("password_hash").First(&user, id).Error; err != nil {
+ if stdliberrors.Is(err, gorm.ErrRecordNotFound) {
+ return nil, &errors.UserNotFound
+ } else {
+ return nil, &errors.FailedToGetUser
+ }
+ }
+
+ return &user, nil
+}
+
func UpdateUser(db *gorm.DB, id uuid.UUID, user models.User) (*models.User, *errors.Error) {
var existingUser models.User
diff --git a/backend/src/transactions/user_follower.go b/backend/src/transactions/user_follower.go
new file mode 100644
index 000000000..38e78ec58
--- /dev/null
+++ b/backend/src/transactions/user_follower.go
@@ -0,0 +1,57 @@
+package transactions
+
+import (
+ "github.com/GenerateNU/sac/backend/src/errors"
+ "github.com/GenerateNU/sac/backend/src/models"
+ "github.com/google/uuid"
+ "gorm.io/gorm"
+)
+
+func CreateFollowing(db *gorm.DB, userId uuid.UUID, clubId uuid.UUID) *errors.Error {
+ user, err := GetUser(db, userId)
+ if err != nil {
+ return &errors.UserNotFound
+ }
+
+ club, err := GetClub(db, clubId)
+ if err != nil {
+ return &errors.ClubNotFound
+ }
+
+ user.Follower = append(user.Follower, *club)
+
+ if err := db.Model(&user).Association("Follower").Append(&club); err != nil {
+ return &errors.FailedToUpdateUser
+ }
+
+ return nil
+}
+
+func DeleteFollowing(db *gorm.DB, userId uuid.UUID, clubId uuid.UUID) *errors.Error {
+ user, err := GetUser(db, userId)
+ if err != nil {
+ return &errors.UserNotFound
+ }
+ club, err := GetClub(db, clubId)
+ if err != nil {
+ return &errors.ClubNotFound
+ }
+ if err := db.Model(&user).Association("Follower").Delete(club); err != nil {
+ return &errors.FailedToUpdateUser
+ }
+ return nil
+}
+
+func GetClubFollowing(db *gorm.DB, userId uuid.UUID) ([]models.Club, *errors.Error) {
+ var clubs []models.Club
+
+ user, err := GetUser(db, userId)
+ if err != nil {
+ return nil, &errors.UserNotFound
+ }
+
+ if err := db.Model(&user).Association("Follower").Find(&clubs); err != nil {
+ return nil, &errors.FailedToGetUserFollowing
+ }
+ return clubs, nil
+}
diff --git a/backend/tests/api/club_follower_test.go b/backend/tests/api/club_follower_test.go
new file mode 100644
index 000000000..ca8701d29
--- /dev/null
+++ b/backend/tests/api/club_follower_test.go
@@ -0,0 +1 @@
+package tests
diff --git a/backend/tests/api/club_test.go b/backend/tests/api/club_test.go
index bea54a011..2d40958dd 100644
--- a/backend/tests/api/club_test.go
+++ b/backend/tests/api/club_test.go
@@ -152,7 +152,7 @@ func CreateSampleClub(existingAppAssert h.ExistingAppAssert) (eaa h.ExistingAppA
},
)
- return existingAppAssert, newAppAssert.App.TestUser.UUID, sampleClubUUID
+ return newAppAssert, newAppAssert.App.TestUser.UUID, sampleClubUUID
}
func TestCreateClubWorks(t *testing.T) {
diff --git a/backend/tests/api/user_follower_test.go b/backend/tests/api/user_follower_test.go
new file mode 100644
index 000000000..c1207c613
--- /dev/null
+++ b/backend/tests/api/user_follower_test.go
@@ -0,0 +1,46 @@
+package tests
+
+import (
+ "fmt"
+ "net/http"
+ "testing"
+
+ "github.com/GenerateNU/sac/backend/src/models"
+ h "github.com/GenerateNU/sac/backend/tests/api/helpers"
+ "github.com/gofiber/fiber/v2"
+)
+
+func TestCreateFollowingWorks(t *testing.T) {
+ appAssert, userUUID, clubUUID := CreateSampleClub(h.InitTest(t))
+
+ appAssert.TestOnStatusAndTester(
+ h.TestRequest{
+ Method: fiber.MethodPost,
+ Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID),
+ },
+ h.TesterWithStatus{
+ Status: fiber.StatusCreated,
+ Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
+ var user models.User
+
+ err := eaa.App.Conn.Where("id = ?", userUUID).Preload("Follower").First(&user)
+
+ eaa.Assert.NilError(err)
+
+ eaa.Assert.Equal(1, len(user.Follower))
+
+ eaa.Assert.Equal(clubUUID, user.Follower[0].ID)
+
+ var club models.Club
+
+ err = eaa.App.Conn.Where("id = ?", clubUUID).Preload("Follower").First(&club)
+
+ eaa.Assert.NilError(err)
+
+ eaa.Assert.Equal(1, len(club.Follower))
+
+ eaa.Assert.Equal(userUUID, club.Follower[0].ID)
+ },
+ },
+ ).Close()
+}
diff --git a/backend/tests/auth_test.go b/backend/tests/auth_test.go
index 3bfc1f530..bb29d577f 100644
--- a/backend/tests/auth_test.go
+++ b/backend/tests/auth_test.go
@@ -151,7 +151,7 @@ func TestSignTokenSuccess(t *testing.T) {
signedToken, authErr := auth.SignToken(token, key)
- assert.Assert(authErr == nil)
+ assert.NilError(authErr == nil)
assert.Assert(signedToken != nil)
}
diff --git a/go.work.sum b/go.work.sum
index 494c07de0..79548c649 100644
--- a/go.work.sum
+++ b/go.work.sum
@@ -46,6 +46,7 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
+golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=