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 01/30] 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 @@
- - Go Workflow Status + + Backend Workflow Status
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 02/30] 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 03/30] 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 04/30] 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 05/30] 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 @@ Backend Workflow Status + + + Backend CodeQL Workflow Status + + +
+ + + CLI Workflow Status + + + + CLI CodeQL Workflow Status + + +
+ + + Mobile CodeQL Workflow Status + + +
+ + + Web CodeQL Workflow Status + ## 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 06/30] 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 07/30] 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 08/30] 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 09/30] 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 10/30] 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 + +![Repo Activity](https://repobeats.axiom.co/api/embed/0c57b86b156d377fcc75a6e482bf921acc8c550b.svg "Repobeats analytics image") + ## 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 11/30] 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 12/30] 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 13/30] 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 14/30] 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 15/30] 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 16/30] 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 17/30] 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 -![Repo Activity](https://repobeats.axiom.co/api/embed/0c57b86b156d377fcc75a6e482bf921acc8c550b.svg "Repobeats analytics image") +![Repo Activity](https://repobeats.axiom.co/api/embed/0c57b86b156d377fcc75a6e482bf921acc8c550b.svg "Repobeats Analytics Image") ## 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 18/30] 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 19/30] 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 20/30] 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 @@
- + + Backend Go Report + Backend Workflow Status @@ -16,6 +19,10 @@
+
+ CLI Go Report + CLI Workflow Status 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 21/30] 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 22/30] 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 23/30] 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 24/30] 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 25/30] 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 26/30] 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 27/30] 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 28/30] 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 29/30] 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 30/30] 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()))