From cc80986b81447cfed142bf40eac1c423e76558a6 Mon Sep 17 00:00:00 2001 From: Cyberguru1 Date: Fri, 19 Jul 2024 13:03:24 -0500 Subject: [PATCH 01/20] database initialize --- go.mod | 1 + go.sum | 2 + .../migrations/transactions_migrations.go | 9 +- internal/models/organisation.go | 19 +++++ internal/models/product.go | 8 ++ internal/models/profile.go | 11 +++ internal/models/seed/seed.go | 84 +++++++++++++++++++ internal/models/user.go | 23 +++++ main.go | 5 ++ 9 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 internal/models/organisation.go create mode 100644 internal/models/product.go create mode 100644 internal/models/profile.go create mode 100644 internal/models/seed/seed.go create mode 100644 internal/models/user.go diff --git a/go.mod b/go.mod index 27d3502f..e01d4552 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 github.com/go-playground/validator/v10 v10.22.0 github.com/gofrs/uuid v4.4.0+incompatible + github.com/google/uuid v1.4.0 github.com/jeanphorn/log4go v0.0.0-20231225120528-d93eb9001e51 github.com/nyaruka/phonenumbers v1.3.6 github.com/sirupsen/logrus v1.9.3 diff --git a/go.sum b/go.sum index f5e184cf..2ec5545b 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1 github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= diff --git a/internal/models/migrations/transactions_migrations.go b/internal/models/migrations/transactions_migrations.go index 6a8992c8..acc1f3f4 100644 --- a/internal/models/migrations/transactions_migrations.go +++ b/internal/models/migrations/transactions_migrations.go @@ -1,8 +1,15 @@ package migrations +import "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" + // _ = db.AutoMigrate(MigrationModels()...) func AuthMigrationModels() []interface{} { - return []interface{}{} // an array of db models, example: User{} + return []interface{}{ + models.User{}, + models.Organisation{}, + models.Profile{}, + models.Product{}, + } // an array of db models, example: User{} } func AlterColumnModels() []AlterColumn { diff --git a/internal/models/organisation.go b/internal/models/organisation.go new file mode 100644 index 00000000..b697ee79 --- /dev/null +++ b/internal/models/organisation.go @@ -0,0 +1,19 @@ +package models + +import "gorm.io/gorm" + +type Organisation struct { + OrgID string `json:"org_id" gorm:"primaryKey;unique;not null"` + Name string `gorm:"type:varchar(255);not null" json:"name"` + Description string `gorm:"type:text" json:"description"` + Users []User `gorm:"many2many:user_organisations;foreignKey:OrgID;joinForeignKey:org_id;References:UserID;joinReferences:user_id" json:"users"` +} + +func AddUserToOrganisation(db *gorm.DB, orgID, userID string) error { + // Add user to organisation + err := db.Exec("INSERT INTO user_organisations (org_id, user_id) VALUES (?, ?)", orgID, userID).Error + if err != nil { + return err + } + return nil +} diff --git a/internal/models/product.go b/internal/models/product.go new file mode 100644 index 00000000..0fef0996 --- /dev/null +++ b/internal/models/product.go @@ -0,0 +1,8 @@ +package models + +type Product struct { + ID string `gorm:"type:uuid;primaryKey" json:"product_id"` + Name string `gorm:"column:name; type:varchar(255); not null" json:"name"` + Description string `gorm:"column:description;type:text;" json:"description"` + UserID string `gorm:"type:uuid;" json:"user_id"` +} diff --git a/internal/models/profile.go b/internal/models/profile.go new file mode 100644 index 00000000..1dc197e6 --- /dev/null +++ b/internal/models/profile.go @@ -0,0 +1,11 @@ +package models + + +type Profile struct { + ID string `gorm:"type:uuid;primary_key" json:"profile_id"` + FirstName string `gorm:"column:first_name; type:text; not null" json:"first_name"` + LastName string `gorm:"column:last_name; type:text;not null" json:"last_name"` + Phone string `gorm:"type:varchar(255)" json:"phone"` + AvatarURL string `gorm:"type:varchar(255)" json:"avatar_url"` + UserID string `gorm:"type:uuid;" json:"user_id"` +} diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go new file mode 100644 index 00000000..db95f9c6 --- /dev/null +++ b/internal/models/seed/seed.go @@ -0,0 +1,84 @@ +package seed + +import ( + "fmt" + + "github.com/google/uuid" + "gorm.io/gorm" + + "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" +) + +func SeedDatabase(db *gorm.DB) { + // Check and seed users + userId1 := uuid.New().String() + user1 := models.User{ + UserID: userId1, + Name: "John Doe", + Email: "john@example.com", + Profile: models.Profile{ + ID: uuid.New().String(), + FirstName: "John", + LastName: "Doe", + Phone: "1234567890", + AvatarURL: "http://example.com/avatar.jpg", + }, + Products: []models.Product{ + {ID: uuid.New().String(), Name: "Product1", Description: "Description1", UserID: userId1}, + {ID: uuid.New().String(), Name: "Product2", Description: "Description2", UserID: userId1}, + }, + } + + userId2 := uuid.New().String() + user2 := models.User{ + UserID: userId2, + Name: "Jane Doe", + Email: "jane@example.com", + Profile: models.Profile{ + ID: uuid.New().String(), + FirstName: "Jane", + LastName: "Doe", + Phone: "0987654321", + AvatarURL: "http://example.com/avatar2.jpg", + }, + Products: []models.Product{ + {ID: uuid.New().String(), Name: "Product3", Description: "Description3", UserID: userId2}, + {ID: uuid.New().String(), Name: "Product4", Description: "Description4", UserID: userId2}, + }, + } + + organisations := []models.Organisation{ + {OrgID: uuid.New().String(), Name: "Org1", Description: "Description1"}, + {OrgID: uuid.New().String(), Name: "Org2", Description: "Description2"}, + {OrgID: uuid.New().String(), Name: "Org3", Description: "Description3"}, + } + + var existingUser models.User + if err := db.Preload("Profile").Preload("Products").Where("email = ?", user1.Email).First(&existingUser).Error; err != nil { + if err == gorm.ErrRecordNotFound { + db.Create(&user1) + db.Create(&user2) + for _, org := range organisations { + db.Create(&org) + } + fmt.Println("Users and organisations seeded.") + + // Add users to organisations + + // add user1 to two organization + models.AddUserToOrganisation(db, organisations[0].OrgID, user1.UserID) + models.AddUserToOrganisation(db, organisations[1].OrgID, user1.UserID) + + // Add user2 to the three organization + models.AddUserToOrganisation(db, organisations[0].OrgID, user2.UserID) + models.AddUserToOrganisation(db, organisations[1].OrgID, user2.UserID) + models.AddUserToOrganisation(db, organisations[2].OrgID, user2.UserID) + fmt.Println("Users added to organisations.") + + } else { + fmt.Println("An error occurred: ", err) + } + } else { + fmt.Println("Users already exist, skipping seeding.") + } +} diff --git a/internal/models/user.go b/internal/models/user.go new file mode 100644 index 00000000..d8a90785 --- /dev/null +++ b/internal/models/user.go @@ -0,0 +1,23 @@ +package models + +import ( + "time" +) + +type User struct { + UserID string `gorm:"type:uuid;primaryKey" json:"user_id"` + Name string `gorm:"column:name; type:varchar(255)" json:"name"` + Email string `gorm:"column:email; type:varchar(255)" json:"email"` + Profile Profile `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"profile"` + Organisations []Organisation `gorm:"many2many:user_organisations;" json:"organisations" ` // many to many relationship + Products []Product `gorm:"foreignKey:UserID" json:"products"` + CreatedAt time.Time `gorm:"column:created_at; not null; autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at; null; autoUpdateTime" json:"updated_at"` +} + +// type CreateUserRequestModel struct { +// FirstName string `json:"first_name" validate:"required"` +// LastName string `json:"last_name" validate:"required"` +// Email string `json:"email" validate:"required"` +// Password string `json:"password" validate:"required"` +// } diff --git a/main.go b/main.go index 881c5a0f..8521a918 100644 --- a/main.go +++ b/main.go @@ -5,8 +5,10 @@ import ( "log" "github.com/go-playground/validator/v10" + "github.com/hngprojects/hng_boilerplate_golang_web/internal/config" "github.com/hngprojects/hng_boilerplate_golang_web/internal/models/migrations" + "github.com/hngprojects/hng_boilerplate_golang_web/internal/models/seed" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage/postgresql" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/router" @@ -27,6 +29,9 @@ func main() { migrations.RunAllMigrations(db) } + // call the seed function + seed.SeedDatabase(db.Postgresql) + r := router.Setup(logger, validatorRef, db, &configuration.App) utility.LogAndPrint(logger, fmt.Sprintf("Server is starting at 127.0.0.1:%s", configuration.Server.Port)) From 8f8d5e6f6dfaf68673a594f59c340821b36020f2 Mon Sep 17 00:00:00 2001 From: Micah Date: Fri, 19 Jul 2024 19:27:40 +0100 Subject: [PATCH 02/20] setup database tables, database seeding, and automigration --- go.mod | 1 + go.sum | 2 + internal/config/env.go | 1 + internal/models/organisation.go | 19 ++++++++ internal/models/product.go | 8 ++++ internal/models/profile.go | 11 +++++ internal/models/seed/seed.go | 84 +++++++++++++++++++++++++++++++++ internal/models/user.go | 23 +++++++++ main.go | 4 ++ 9 files changed, 153 insertions(+) create mode 100644 internal/models/organisation.go create mode 100644 internal/models/product.go create mode 100644 internal/models/profile.go create mode 100644 internal/models/seed/seed.go create mode 100644 internal/models/user.go diff --git a/go.mod b/go.mod index 27d3502f..e01d4552 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 github.com/go-playground/validator/v10 v10.22.0 github.com/gofrs/uuid v4.4.0+incompatible + github.com/google/uuid v1.4.0 github.com/jeanphorn/log4go v0.0.0-20231225120528-d93eb9001e51 github.com/nyaruka/phonenumbers v1.3.6 github.com/sirupsen/logrus v1.9.3 diff --git a/go.sum b/go.sum index f5e184cf..2ec5545b 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1 github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= diff --git a/internal/config/env.go b/internal/config/env.go index fabc60c5..332260bf 100644 --- a/internal/config/env.go +++ b/internal/config/env.go @@ -54,6 +54,7 @@ func (config *BaseConfig) SetupConfigurationn() *Configuration { exemptFromThrottle := []string{} json.Unmarshal([]byte(config.TRUSTED_PROXIES), &trustedProxies) json.Unmarshal([]byte(config.EXEMPT_FROM_THROTTLE), &exemptFromThrottle) + if config.SERVER_PORT == "" { config.SERVER_PORT = os.Getenv("PORT") } diff --git a/internal/models/organisation.go b/internal/models/organisation.go new file mode 100644 index 00000000..16c07281 --- /dev/null +++ b/internal/models/organisation.go @@ -0,0 +1,19 @@ +package models + +import "gorm.io/gorm" + +type Organisation struct { + OrgID string `json:"org_id" gorm:"primaryKey;unique;not null"` + Name string `gorm:"type:varchar(255);not null" json:"name"` + Description string `gorm:"type:text" json:"description"` + Users []User `gorm:"many2many:user_organisations;foreignKey:OrgID;joinForeignKey:org_id;References:UserID;joinReferences:user_id" json:"users"` +} + +func AddUserToOrganisation(db *gorm.DB, orgID, userID string) error { + // Add user to organisation + err := db.Exec("INSERT INTO user_organisations (org_id, user_id) VALUES (?, ?)", orgID, userID).Error + if err != nil { + return err + } + return nil +} \ No newline at end of file diff --git a/internal/models/product.go b/internal/models/product.go new file mode 100644 index 00000000..a62eedee --- /dev/null +++ b/internal/models/product.go @@ -0,0 +1,8 @@ +package models + +type Product struct { + ID string `gorm:"type:uuid;primaryKey" json:"product_id"` + Name string `gorm:"column:name; type:varchar(255); not null" json:"name"` + Description string `gorm:"column:description;type:text;" json:"description"` + UserID string `gorm:"type:uuid;" json:"user_id"` +} \ No newline at end of file diff --git a/internal/models/profile.go b/internal/models/profile.go new file mode 100644 index 00000000..ea6d09f5 --- /dev/null +++ b/internal/models/profile.go @@ -0,0 +1,11 @@ +package models + + +type Profile struct { + ID string `gorm:"type:uuid;primary_key" json:"profile_id"` + FirstName string `gorm:"column:first_name; type:text; not null" json:"first_name"` + LastName string `gorm:"column:last_name; type:text;not null" json:"last_name"` + Phone string `gorm:"type:varchar(255)" json:"phone"` + AvatarURL string `gorm:"type:varchar(255)" json:"avatar_url"` + UserID string `gorm:"type:uuid;" json:"user_id"` +} \ No newline at end of file diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go new file mode 100644 index 00000000..c501fd31 --- /dev/null +++ b/internal/models/seed/seed.go @@ -0,0 +1,84 @@ +package seed + +import ( + "fmt" + + "github.com/google/uuid" + "gorm.io/gorm" + + "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" +) + +func SeedDatabase(db *gorm.DB) { + // Check and seed users + userId1 := uuid.New().String() + user1 := models.User{ + UserID: userId1, + Name: "John Doe", + Email: "john@example.com", + Profile: models.Profile{ + ID: uuid.New().String(), + FirstName: "John", + LastName: "Doe", + Phone: "1234567890", + AvatarURL: "http://example.com/avatar.jpg", + }, + Products: []models.Product{ + {ID: uuid.New().String(), Name: "Product1", Description: "Description1", UserID: userId1}, + {ID: uuid.New().String(), Name: "Product2", Description: "Description2", UserID: userId1}, + }, + } + + userId2 := uuid.New().String() + user2 := models.User{ + UserID: userId2, + Name: "Jane Doe", + Email: "jane@example.com", + Profile: models.Profile{ + ID: uuid.New().String(), + FirstName: "Jane", + LastName: "Doe", + Phone: "0987654321", + AvatarURL: "http://example.com/avatar2.jpg", + }, + Products: []models.Product{ + {ID: uuid.New().String(), Name: "Product3", Description: "Description3", UserID: userId2}, + {ID: uuid.New().String(), Name: "Product4", Description: "Description4", UserID: userId2}, + }, + } + + organisations := []models.Organisation{ + {OrgID: uuid.New().String(), Name: "Org1", Description: "Description1"}, + {OrgID: uuid.New().String(), Name: "Org2", Description: "Description2"}, + {OrgID: uuid.New().String(), Name: "Org3", Description: "Description3"}, + } + + var existingUser models.User + if err := db.Preload("Profile").Preload("Products").Where("email = ?", user1.Email).First(&existingUser).Error; err != nil { + if err == gorm.ErrRecordNotFound { + db.Create(&user1) + db.Create(&user2) + for _, org := range organisations { + db.Create(&org) + } + fmt.Println("Users and organisations seeded.") + + // Add users to organisations + + // add user1 to two organization + models.AddUserToOrganisation(db, organisations[0].OrgID, user1.UserID) + models.AddUserToOrganisation(db, organisations[1].OrgID, user1.UserID) + + // Add user2 to the three organization + models.AddUserToOrganisation(db, organisations[0].OrgID, user2.UserID) + models.AddUserToOrganisation(db, organisations[1].OrgID, user2.UserID) + models.AddUserToOrganisation(db, organisations[2].OrgID, user2.UserID) + fmt.Println("Users added to organisations.") + + } else { + fmt.Println("An error occurred: ", err) + } + } else { + fmt.Println("Users already exist, skipping seeding.") + } +} \ No newline at end of file diff --git a/internal/models/user.go b/internal/models/user.go new file mode 100644 index 00000000..8cb7c5b5 --- /dev/null +++ b/internal/models/user.go @@ -0,0 +1,23 @@ +package models + +import ( + "time" +) + +type User struct { + UserID string `gorm:"type:uuid;primaryKey" json:"user_id"` + Name string `gorm:"column:name; type:varchar(255)" json:"name"` + Email string `gorm:"column:email; type:varchar(255)" json:"email"` + Profile Profile `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"profile"` + Organisations []Organisation `gorm:"many2many:user_organisations;" json:"organisations" ` // many to many relationship + Products []Product `gorm:"foreignKey:UserID" json:"products"` + CreatedAt time.Time `gorm:"column:created_at; not null; autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at; null; autoUpdateTime" json:"updated_at"` +} + +// type CreateUserRequestModel struct { +// FirstName string `json:"first_name" validate:"required"` +// LastName string `json:"last_name" validate:"required"` +// Email string `json:"email" validate:"required"` +// Password string `json:"password" validate:"required"` +// } \ No newline at end of file diff --git a/main.go b/main.go index 881c5a0f..0396fe5d 100644 --- a/main.go +++ b/main.go @@ -7,6 +7,7 @@ import ( "github.com/go-playground/validator/v10" "github.com/hngprojects/hng_boilerplate_golang_web/internal/config" "github.com/hngprojects/hng_boilerplate_golang_web/internal/models/migrations" + "github.com/hngprojects/hng_boilerplate_golang_web/internal/models/seed" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage/postgresql" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/router" @@ -27,6 +28,9 @@ func main() { migrations.RunAllMigrations(db) } + // call the seed function + seed.SeedDatabase(db.Postgresql) + r := router.Setup(logger, validatorRef, db, &configuration.App) utility.LogAndPrint(logger, fmt.Sprintf("Server is starting at 127.0.0.1:%s", configuration.Server.Port)) From c6c03253f37690823ad7a4eeee3a7ce312604168 Mon Sep 17 00:00:00 2001 From: Cyberguru1 Date: Fri, 19 Jul 2024 14:06:27 -0500 Subject: [PATCH 03/20] database initialize --- internal/models/organisation.go | 11 ------- internal/models/seed/seed.go | 19 ++++++----- pkg/controller/seed/seed.go | 36 +++++++++++++++++++++ pkg/repository/storage/postgresql/create.go | 9 ++++++ pkg/router/router.go | 1 + pkg/router/seed.go | 25 ++++++++++++++ 6 files changed, 82 insertions(+), 19 deletions(-) create mode 100644 pkg/controller/seed/seed.go create mode 100644 pkg/router/seed.go diff --git a/internal/models/organisation.go b/internal/models/organisation.go index b697ee79..88641101 100644 --- a/internal/models/organisation.go +++ b/internal/models/organisation.go @@ -1,19 +1,8 @@ package models -import "gorm.io/gorm" - type Organisation struct { OrgID string `json:"org_id" gorm:"primaryKey;unique;not null"` Name string `gorm:"type:varchar(255);not null" json:"name"` Description string `gorm:"type:text" json:"description"` Users []User `gorm:"many2many:user_organisations;foreignKey:OrgID;joinForeignKey:org_id;References:UserID;joinReferences:user_id" json:"users"` } - -func AddUserToOrganisation(db *gorm.DB, orgID, userID string) error { - // Add user to organisation - err := db.Exec("INSERT INTO user_organisations (org_id, user_id) VALUES (?, ?)", orgID, userID).Error - if err != nil { - return err - } - return nil -} diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go index db95f9c6..69713447 100644 --- a/internal/models/seed/seed.go +++ b/internal/models/seed/seed.go @@ -7,6 +7,7 @@ import ( "gorm.io/gorm" "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage/postgresql" ) func SeedDatabase(db *gorm.DB) { @@ -56,23 +57,23 @@ func SeedDatabase(db *gorm.DB) { var existingUser models.User if err := db.Preload("Profile").Preload("Products").Where("email = ?", user1.Email).First(&existingUser).Error; err != nil { if err == gorm.ErrRecordNotFound { - db.Create(&user1) - db.Create(&user2) + postgresql.CreateOneRecord(db, &user1) + postgresql.CreateOneRecord(db, &user2) for _, org := range organisations { - db.Create(&org) + postgresql.CreateOneRecord(db, &org) } fmt.Println("Users and organisations seeded.") // Add users to organisations // add user1 to two organization - models.AddUserToOrganisation(db, organisations[0].OrgID, user1.UserID) - models.AddUserToOrganisation(db, organisations[1].OrgID, user1.UserID) + postgresql.AddUserToOrganisation(db, organisations[0].OrgID, user1.UserID) + postgresql.AddUserToOrganisation(db, organisations[1].OrgID, user1.UserID) // Add user2 to the three organization - models.AddUserToOrganisation(db, organisations[0].OrgID, user2.UserID) - models.AddUserToOrganisation(db, organisations[1].OrgID, user2.UserID) - models.AddUserToOrganisation(db, organisations[2].OrgID, user2.UserID) + postgresql.AddUserToOrganisation(db, organisations[0].OrgID, user2.UserID) + postgresql.AddUserToOrganisation(db, organisations[1].OrgID, user2.UserID) + postgresql.AddUserToOrganisation(db, organisations[2].OrgID, user2.UserID) fmt.Println("Users added to organisations.") } else { @@ -81,4 +82,6 @@ func SeedDatabase(db *gorm.DB) { } else { fmt.Println("Users already exist, skipping seeding.") } + + fmt.Println(existingUser) } diff --git a/pkg/controller/seed/seed.go b/pkg/controller/seed/seed.go new file mode 100644 index 00000000..955780d3 --- /dev/null +++ b/pkg/controller/seed/seed.go @@ -0,0 +1,36 @@ +package seed + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + + "github.com/hngprojects/hng_boilerplate_golang_web/external/request" + // "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" + "github.com/hngprojects/hng_boilerplate_golang_web/services/ping" + "github.com/hngprojects/hng_boilerplate_golang_web/utility" +) + +type Controller struct { + Db *storage.Database + Validator *validator.Validate + Logger *utility.Logger + ExtReq request.ExternalRequest +} + + +func (base *Controller) Get(c *gin.Context) { + if !ping.ReturnTrue() { + rd := utility.BuildErrorResponse(http.StatusInternalServerError, "error", "ping failed", fmt.Errorf("ping failed"), nil) + c.JSON(http.StatusBadRequest, rd) + return + } + base.Logger.Info("ping successfull") + rd := utility.BuildSuccessResponse(http.StatusOK, "ping successful", gin.H{"transactions": "transactions object"}) + + c.JSON(http.StatusOK, rd) + +} diff --git a/pkg/repository/storage/postgresql/create.go b/pkg/repository/storage/postgresql/create.go index ec7d3197..293b4955 100644 --- a/pkg/repository/storage/postgresql/create.go +++ b/pkg/repository/storage/postgresql/create.go @@ -28,3 +28,12 @@ func CreateMultipleRecords(db *gorm.DB, model interface{}, length int) error { } return nil } + +func AddUserToOrganisation(db *gorm.DB, orgID, userID string) error { + // Add user to organisation + err := db.Exec("INSERT INTO user_organisations (org_id, user_id) VALUES (?, ?)", orgID, userID).Error + if err != nil { + return err + } + return nil +} diff --git a/pkg/router/router.go b/pkg/router/router.go index 581ee55a..492d927d 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -6,6 +6,7 @@ import ( "github.com/gin-contrib/gzip" "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" + "github.com/hngprojects/hng_boilerplate_golang_web/internal/config" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/middleware" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" diff --git a/pkg/router/seed.go b/pkg/router/seed.go new file mode 100644 index 00000000..202dd651 --- /dev/null +++ b/pkg/router/seed.go @@ -0,0 +1,25 @@ +package router + +import ( + "fmt" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + + "github.com/hngprojects/hng_boilerplate_golang_web/external/request" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/controller/seed" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" + "github.com/hngprojects/hng_boilerplate_golang_web/utility" +) + +func Seed(r *gin.Engine, ApiVersion string, validator *validator.Validate, db *storage.Database, logger *utility.Logger) *gin.Engine { + extReq := request.ExternalRequest{Logger: logger, Test: false} + seed := seed.Controller{Db: db, Validator: validator, Logger: logger, ExtReq: extReq} + + seedUrl := r.Group(fmt.Sprintf("%v", ApiVersion)) + { + seedUrl.GET("/users/user_one_id", seed.Get) + seedUrl.GET("/users/user_two_id", seed.Get) + } + return r +} From b8d4755de7ec8a74741f5d7adef54495f1b9fadc Mon Sep 17 00:00:00 2001 From: Micah Date: Fri, 19 Jul 2024 21:56:12 +0100 Subject: [PATCH 04/20] setup user controller --- internal/config/configuration.go | 2 +- .../migrations/transactions_migrations.go | 9 ++- internal/models/organisation.go | 11 ---- internal/models/seed/seed.go | 18 +++--- pkg/controller/seed/seed.go | 60 +++++++++++++++++++ pkg/repository/storage/postgresql/create.go | 12 +++- pkg/router/router.go | 4 +- pkg/router/seed.go | 25 ++++++++ 8 files changed, 118 insertions(+), 23 deletions(-) create mode 100644 pkg/controller/seed/seed.go create mode 100644 pkg/router/seed.go diff --git a/internal/config/configuration.go b/internal/config/configuration.go index b33c8a38..407e0f1b 100644 --- a/internal/config/configuration.go +++ b/internal/config/configuration.go @@ -25,7 +25,7 @@ func Setup(logger *utility.Logger, name string) *Configuration { log.Fatalf("Error reading config file, %s", err) } - viper.AutomaticEnv() + // viper.AutomaticEnv() err := viper.Unmarshal(&baseConfiguration) if err != nil { diff --git a/internal/models/migrations/transactions_migrations.go b/internal/models/migrations/transactions_migrations.go index 6a8992c8..8a7a1014 100644 --- a/internal/models/migrations/transactions_migrations.go +++ b/internal/models/migrations/transactions_migrations.go @@ -1,8 +1,15 @@ package migrations +import "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" + // _ = db.AutoMigrate(MigrationModels()...) func AuthMigrationModels() []interface{} { - return []interface{}{} // an array of db models, example: User{} + return []interface{}{ + models.Organisation{}, + models.User{}, + models.Profile{}, + models.Product{}, + } // an array of db models, example: User{} } func AlterColumnModels() []AlterColumn { diff --git a/internal/models/organisation.go b/internal/models/organisation.go index 16c07281..88641101 100644 --- a/internal/models/organisation.go +++ b/internal/models/organisation.go @@ -1,19 +1,8 @@ package models -import "gorm.io/gorm" - type Organisation struct { OrgID string `json:"org_id" gorm:"primaryKey;unique;not null"` Name string `gorm:"type:varchar(255);not null" json:"name"` Description string `gorm:"type:text" json:"description"` Users []User `gorm:"many2many:user_organisations;foreignKey:OrgID;joinForeignKey:org_id;References:UserID;joinReferences:user_id" json:"users"` } - -func AddUserToOrganisation(db *gorm.DB, orgID, userID string) error { - // Add user to organisation - err := db.Exec("INSERT INTO user_organisations (org_id, user_id) VALUES (?, ?)", orgID, userID).Error - if err != nil { - return err - } - return nil -} \ No newline at end of file diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go index c501fd31..b6e5fb8a 100644 --- a/internal/models/seed/seed.go +++ b/internal/models/seed/seed.go @@ -7,6 +7,7 @@ import ( "gorm.io/gorm" "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage/postgresql" ) func SeedDatabase(db *gorm.DB) { @@ -56,23 +57,23 @@ func SeedDatabase(db *gorm.DB) { var existingUser models.User if err := db.Preload("Profile").Preload("Products").Where("email = ?", user1.Email).First(&existingUser).Error; err != nil { if err == gorm.ErrRecordNotFound { - db.Create(&user1) - db.Create(&user2) + postgresql.CreateOneRecord(db, &user1) + postgresql.CreateOneRecord(db, &user2) for _, org := range organisations { - db.Create(&org) + postgresql.CreateOneRecord(db, &org) } fmt.Println("Users and organisations seeded.") // Add users to organisations // add user1 to two organization - models.AddUserToOrganisation(db, organisations[0].OrgID, user1.UserID) - models.AddUserToOrganisation(db, organisations[1].OrgID, user1.UserID) + postgresql.AddUserToOrganisation(db, organisations[0].OrgID, user1.UserID) + postgresql.AddUserToOrganisation(db, organisations[1].OrgID, user1.UserID) // Add user2 to the three organization - models.AddUserToOrganisation(db, organisations[0].OrgID, user2.UserID) - models.AddUserToOrganisation(db, organisations[1].OrgID, user2.UserID) - models.AddUserToOrganisation(db, organisations[2].OrgID, user2.UserID) + postgresql.AddUserToOrganisation(db, organisations[0].OrgID, user2.UserID) + postgresql.AddUserToOrganisation(db, organisations[1].OrgID, user2.UserID) + postgresql.AddUserToOrganisation(db, organisations[2].OrgID, user2.UserID) fmt.Println("Users added to organisations.") } else { @@ -81,4 +82,5 @@ func SeedDatabase(db *gorm.DB) { } else { fmt.Println("Users already exist, skipping seeding.") } + } \ No newline at end of file diff --git a/pkg/controller/seed/seed.go b/pkg/controller/seed/seed.go new file mode 100644 index 00000000..7b73c147 --- /dev/null +++ b/pkg/controller/seed/seed.go @@ -0,0 +1,60 @@ +package seed + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + + "github.com/hngprojects/hng_boilerplate_golang_web/external/request" + "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" + "github.com/hngprojects/hng_boilerplate_golang_web/services/ping" + "github.com/hngprojects/hng_boilerplate_golang_web/utility" +) + +type Controller struct { + Db *storage.Database + Validator *validator.Validate + Logger *utility.Logger + ExtReq request.ExternalRequest +} + +func (base *Controller) Get1(c *gin.Context) { + if !ping.ReturnTrue() { + rd := utility.BuildErrorResponse(http.StatusInternalServerError, "error", "ping failed", fmt.Errorf("ping failed"), nil) + c.JSON(http.StatusBadRequest, rd) + return + } + + var user models.User + if err := base.Db.Postgresql.Preload("Profile").Preload("Products").Preload("Organisations").Where("email = ?", "john@example.com").First(&user).Error; err != nil { + fmt.Println(err) + return + } + + base.Logger.Info("user1 fetched successfully") + rd := utility.BuildSuccessResponse(http.StatusOK, "", user) + + c.JSON(http.StatusOK, rd) +} + +func (base *Controller) Get2(c *gin.Context) { + if !ping.ReturnTrue() { + rd := utility.BuildErrorResponse(http.StatusInternalServerError, "error", "ping failed", fmt.Errorf("ping failed"), nil) + c.JSON(http.StatusBadRequest, rd) + return + } + + var user models.User + if err := base.Db.Postgresql.Preload("Profile").Preload("Products").Preload("Organisations").Where("email = ?", "jane@example.com").First(&user).Error; err != nil { + fmt.Println(err) + return + } + + base.Logger.Info("user2 fetched successfully") + rd := utility.BuildSuccessResponse(http.StatusOK, "", user) + + c.JSON(http.StatusOK, rd) +} diff --git a/pkg/repository/storage/postgresql/create.go b/pkg/repository/storage/postgresql/create.go index ec7d3197..4a124349 100644 --- a/pkg/repository/storage/postgresql/create.go +++ b/pkg/repository/storage/postgresql/create.go @@ -2,7 +2,6 @@ package postgresql import ( "fmt" - "gorm.io/gorm" ) @@ -28,3 +27,14 @@ func CreateMultipleRecords(db *gorm.DB, model interface{}, length int) error { } return nil } + +func AddUserToOrganisation(db *gorm.DB, orgID, userID string) error { + // Add user to organisation + err := db.Exec("INSERT INTO user_organisations (org_id, user_id) VALUES (?, ?)", orgID, userID).Error + if err != nil { + return err + } + return nil +} + + diff --git a/pkg/router/router.go b/pkg/router/router.go index 581ee55a..cec4b163 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -30,8 +30,10 @@ func Setup(logger *utility.Logger, validator *validator.Validate, db *storage.Da r.MaxMultipartMemory = 1 << 20 // 1MB // routers - ApiVersion := "v2" + ApiVersion := "api/v1" + Health(r, ApiVersion, validator, db, logger) + Seed(r, ApiVersion, validator, db, logger) r.GET("/", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ diff --git a/pkg/router/seed.go b/pkg/router/seed.go new file mode 100644 index 00000000..de7aaa74 --- /dev/null +++ b/pkg/router/seed.go @@ -0,0 +1,25 @@ +package router + +import ( + "fmt" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + + "github.com/hngprojects/hng_boilerplate_golang_web/external/request" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/controller/seed" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" + "github.com/hngprojects/hng_boilerplate_golang_web/utility" +) + +func Seed(r *gin.Engine, ApiVersion string, validator *validator.Validate, db *storage.Database, logger *utility.Logger) *gin.Engine { + extReq := request.ExternalRequest{Logger: logger, Test: false} + seed := seed.Controller{Db: db, Validator: validator, Logger: logger, ExtReq: extReq} + + seedUrl := r.Group(fmt.Sprintf("%v", ApiVersion)) + { + seedUrl.GET("/users/user_one_id", seed.Get1) + seedUrl.GET("/users/user_two_id", seed.Get2) + } + return r +} \ No newline at end of file From 26f528c10624a753a734fce51b85760d335d0d1f Mon Sep 17 00:00:00 2001 From: Cyberguru1 Date: Fri, 19 Jul 2024 15:58:40 -0500 Subject: [PATCH 05/20] database initialize --- internal/models/seed/seed.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go index 69713447..7150ee5c 100644 --- a/internal/models/seed/seed.go +++ b/internal/models/seed/seed.go @@ -82,6 +82,4 @@ func SeedDatabase(db *gorm.DB) { } else { fmt.Println("Users already exist, skipping seeding.") } - - fmt.Println(existingUser) } From d8e3675540665ee28b67b3b4b0e366ce337e89a3 Mon Sep 17 00:00:00 2001 From: Cyberguru1 Date: Fri, 19 Jul 2024 17:34:52 -0500 Subject: [PATCH 06/20] fix seed data read --- internal/config/configuration.go | 5 ++-- internal/models/organisation.go | 4 +-- internal/models/product.go | 2 +- internal/models/seed/seed.go | 32 ++++++++++----------- internal/models/user.go | 6 ++-- pkg/repository/storage/postgresql/create.go | 2 +- 6 files changed, 26 insertions(+), 25 deletions(-) diff --git a/internal/config/configuration.go b/internal/config/configuration.go index 407e0f1b..8bdbbbf2 100644 --- a/internal/config/configuration.go +++ b/internal/config/configuration.go @@ -3,8 +3,9 @@ package config import ( "log" - "github.com/hngprojects/hng_boilerplate_golang_web/utility" "github.com/spf13/viper" + + "github.com/hngprojects/hng_boilerplate_golang_web/utility" ) // Setup initialize configuration @@ -25,7 +26,7 @@ func Setup(logger *utility.Logger, name string) *Configuration { log.Fatalf("Error reading config file, %s", err) } - // viper.AutomaticEnv() + viper.AutomaticEnv() err := viper.Unmarshal(&baseConfiguration) if err != nil { diff --git a/internal/models/organisation.go b/internal/models/organisation.go index 88641101..ad2c0e51 100644 --- a/internal/models/organisation.go +++ b/internal/models/organisation.go @@ -1,8 +1,8 @@ package models type Organisation struct { - OrgID string `json:"org_id" gorm:"primaryKey;unique;not null"` + Orgid string `gorm:"type:uuid;primaryKey;unique;not null" json:"org_id"` Name string `gorm:"type:varchar(255);not null" json:"name"` Description string `gorm:"type:text" json:"description"` - Users []User `gorm:"many2many:user_organisations;foreignKey:OrgID;joinForeignKey:org_id;References:UserID;joinReferences:user_id" json:"users"` + Users []User `gorm:"many2many:user_organisations;foreignKey:Orgid;joinForeignKey:org_id;References:Userid;joinReferences:user_id" json:"users"` } diff --git a/internal/models/product.go b/internal/models/product.go index a62eedee..46cee840 100644 --- a/internal/models/product.go +++ b/internal/models/product.go @@ -4,5 +4,5 @@ type Product struct { ID string `gorm:"type:uuid;primaryKey" json:"product_id"` Name string `gorm:"column:name; type:varchar(255); not null" json:"name"` Description string `gorm:"column:description;type:text;" json:"description"` - UserID string `gorm:"type:uuid;" json:"user_id"` + Userid string `gorm:"type:uuid;" json:"user_id"` } \ No newline at end of file diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go index 02ccff74..37596545 100644 --- a/internal/models/seed/seed.go +++ b/internal/models/seed/seed.go @@ -12,9 +12,9 @@ import ( func SeedDatabase(db *gorm.DB) { // Check and seed users - userId1 := uuid.New().String() + Userid1 := uuid.New().String() user1 := models.User{ - UserID: userId1, + Userid: Userid1, Name: "John Doe", Email: "john@example.com", Profile: models.Profile{ @@ -25,14 +25,14 @@ func SeedDatabase(db *gorm.DB) { AvatarURL: "http://example.com/avatar.jpg", }, Products: []models.Product{ - {ID: uuid.New().String(), Name: "Product1", Description: "Description1", UserID: userId1}, - {ID: uuid.New().String(), Name: "Product2", Description: "Description2", UserID: userId1}, + {ID: uuid.New().String(), Name: "Product1", Description: "Description1", Userid: Userid1}, + {ID: uuid.New().String(), Name: "Product2", Description: "Description2", Userid: Userid1}, }, } - userId2 := uuid.New().String() + Userid2 := uuid.New().String() user2 := models.User{ - UserID: userId2, + Userid: Userid2, Name: "Jane Doe", Email: "jane@example.com", Profile: models.Profile{ @@ -43,15 +43,15 @@ func SeedDatabase(db *gorm.DB) { AvatarURL: "http://example.com/avatar2.jpg", }, Products: []models.Product{ - {ID: uuid.New().String(), Name: "Product3", Description: "Description3", UserID: userId2}, - {ID: uuid.New().String(), Name: "Product4", Description: "Description4", UserID: userId2}, + {ID: uuid.New().String(), Name: "Product3", Description: "Description3", Userid: Userid2}, + {ID: uuid.New().String(), Name: "Product4", Description: "Description4", Userid: Userid2}, }, } organisations := []models.Organisation{ - {OrgID: uuid.New().String(), Name: "Org1", Description: "Description1"}, - {OrgID: uuid.New().String(), Name: "Org2", Description: "Description2"}, - {OrgID: uuid.New().String(), Name: "Org3", Description: "Description3"}, + {Orgid: uuid.New().String(), Name: "Org1", Description: "Description1"}, + {Orgid: uuid.New().String(), Name: "Org2", Description: "Description2"}, + {Orgid: uuid.New().String(), Name: "Org3", Description: "Description3"}, } var existingUser models.User @@ -67,13 +67,13 @@ func SeedDatabase(db *gorm.DB) { // Add users to organisations // add user1 to two organization - postgresql.AddUserToOrganisation(db, organisations[0].OrgID, user1.UserID) - postgresql.AddUserToOrganisation(db, organisations[1].OrgID, user1.UserID) + postgresql.AddUserToOrganisation(db, organisations[0].Orgid, user1.Userid) + postgresql.AddUserToOrganisation(db, organisations[1].Orgid, user1.Userid) // Add user2 to the three organization - postgresql.AddUserToOrganisation(db, organisations[0].OrgID, user2.UserID) - postgresql.AddUserToOrganisation(db, organisations[1].OrgID, user2.UserID) - postgresql.AddUserToOrganisation(db, organisations[2].OrgID, user2.UserID) + postgresql.AddUserToOrganisation(db, organisations[0].Orgid, user2.Userid) + postgresql.AddUserToOrganisation(db, organisations[1].Orgid, user2.Userid) + postgresql.AddUserToOrganisation(db, organisations[2].Orgid, user2.Userid) fmt.Println("Users added to organisations.") } else { diff --git a/internal/models/user.go b/internal/models/user.go index d8a90785..735f6127 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -5,12 +5,12 @@ import ( ) type User struct { - UserID string `gorm:"type:uuid;primaryKey" json:"user_id"` + Userid string `gorm:"type:uuid;primaryKey;unique;not null" json:"id"` Name string `gorm:"column:name; type:varchar(255)" json:"name"` Email string `gorm:"column:email; type:varchar(255)" json:"email"` Profile Profile `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"profile"` - Organisations []Organisation `gorm:"many2many:user_organisations;" json:"organisations" ` // many to many relationship - Products []Product `gorm:"foreignKey:UserID" json:"products"` + Organisations []Organisation `gorm:"many2many:user_organisations;;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"organisations" ` // many to many relationship + Products []Product `gorm:"foreignKey:Userid" json:"products"` CreatedAt time.Time `gorm:"column:created_at; not null; autoCreateTime" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at; null; autoUpdateTime" json:"updated_at"` } diff --git a/pkg/repository/storage/postgresql/create.go b/pkg/repository/storage/postgresql/create.go index 293b4955..dd6572b9 100644 --- a/pkg/repository/storage/postgresql/create.go +++ b/pkg/repository/storage/postgresql/create.go @@ -31,7 +31,7 @@ func CreateMultipleRecords(db *gorm.DB, model interface{}, length int) error { func AddUserToOrganisation(db *gorm.DB, orgID, userID string) error { // Add user to organisation - err := db.Exec("INSERT INTO user_organisations (org_id, user_id) VALUES (?, ?)", orgID, userID).Error + err := db.Exec("INSERT INTO user_organisations (organisation_orgid, user_userid) VALUES (?, ?)", orgID, userID).Error if err != nil { return err } From 41dfb79104f5a62bd6ba45a1f0b4ee5b1338d8fa Mon Sep 17 00:00:00 2001 From: Micah Date: Fri, 19 Jul 2024 23:41:57 +0100 Subject: [PATCH 07/20] changes --- .../migrations/transactions_migrations.go | 2 +- internal/models/organisation.go | 4 ++-- internal/models/product.go | 4 ++-- internal/models/profile.go | 5 ++--- internal/models/seed/seed.go | 20 +++++++++---------- internal/models/user.go | 8 ++++---- 6 files changed, 21 insertions(+), 22 deletions(-) diff --git a/internal/models/migrations/transactions_migrations.go b/internal/models/migrations/transactions_migrations.go index 8a7a1014..dcc16d6f 100644 --- a/internal/models/migrations/transactions_migrations.go +++ b/internal/models/migrations/transactions_migrations.go @@ -6,9 +6,9 @@ import "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" func AuthMigrationModels() []interface{} { return []interface{}{ models.Organisation{}, - models.User{}, models.Profile{}, models.Product{}, + models.User{}, } // an array of db models, example: User{} } diff --git a/internal/models/organisation.go b/internal/models/organisation.go index 88641101..7cbc2ea6 100644 --- a/internal/models/organisation.go +++ b/internal/models/organisation.go @@ -1,8 +1,8 @@ package models type Organisation struct { - OrgID string `json:"org_id" gorm:"primaryKey;unique;not null"` + Orgid string `json:"org_id" gorm:"primaryKey;unique;not null"` Name string `gorm:"type:varchar(255);not null" json:"name"` Description string `gorm:"type:text" json:"description"` - Users []User `gorm:"many2many:user_organisations;foreignKey:OrgID;joinForeignKey:org_id;References:UserID;joinReferences:user_id" json:"users"` + Users []User `gorm:"many2many:user_organisations;foreignKey:Orgid;joinForeignKey:org_id;References:Userid;joinReferences:user_id" json:"users"` } diff --git a/internal/models/product.go b/internal/models/product.go index a62eedee..e18e27c7 100644 --- a/internal/models/product.go +++ b/internal/models/product.go @@ -4,5 +4,5 @@ type Product struct { ID string `gorm:"type:uuid;primaryKey" json:"product_id"` Name string `gorm:"column:name; type:varchar(255); not null" json:"name"` Description string `gorm:"column:description;type:text;" json:"description"` - UserID string `gorm:"type:uuid;" json:"user_id"` -} \ No newline at end of file + Userid string `gorm:"type:uuid;" json:"user_id"` +} diff --git a/internal/models/profile.go b/internal/models/profile.go index ea6d09f5..92d39869 100644 --- a/internal/models/profile.go +++ b/internal/models/profile.go @@ -1,11 +1,10 @@ package models - type Profile struct { ID string `gorm:"type:uuid;primary_key" json:"profile_id"` FirstName string `gorm:"column:first_name; type:text; not null" json:"first_name"` LastName string `gorm:"column:last_name; type:text;not null" json:"last_name"` Phone string `gorm:"type:varchar(255)" json:"phone"` AvatarURL string `gorm:"type:varchar(255)" json:"avatar_url"` - UserID string `gorm:"type:uuid;" json:"user_id"` -} \ No newline at end of file + Userid string `gorm:"type:uuid;" json:"user_id"` +} diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go index b6e5fb8a..ae2c1f81 100644 --- a/internal/models/seed/seed.go +++ b/internal/models/seed/seed.go @@ -14,7 +14,7 @@ func SeedDatabase(db *gorm.DB) { // Check and seed users userId1 := uuid.New().String() user1 := models.User{ - UserID: userId1, + Userid:userId1, Name: "John Doe", Email: "john@example.com", Profile: models.Profile{ @@ -32,7 +32,7 @@ func SeedDatabase(db *gorm.DB) { userId2 := uuid.New().String() user2 := models.User{ - UserID: userId2, + Userid: userId2, Name: "Jane Doe", Email: "jane@example.com", Profile: models.Profile{ @@ -49,9 +49,9 @@ func SeedDatabase(db *gorm.DB) { } organisations := []models.Organisation{ - {OrgID: uuid.New().String(), Name: "Org1", Description: "Description1"}, - {OrgID: uuid.New().String(), Name: "Org2", Description: "Description2"}, - {OrgID: uuid.New().String(), Name: "Org3", Description: "Description3"}, + {Orgid: uuid.New().String(), Name: "Org1", Description: "Description1"}, + {Orgid: uuid.New().String(), Name: "Org2", Description: "Description2"}, + {Orgid: uuid.New().String(), Name: "Org3", Description: "Description3"}, } var existingUser models.User @@ -67,13 +67,13 @@ func SeedDatabase(db *gorm.DB) { // Add users to organisations // add user1 to two organization - postgresql.AddUserToOrganisation(db, organisations[0].OrgID, user1.UserID) - postgresql.AddUserToOrganisation(db, organisations[1].OrgID, user1.UserID) + postgresql.AddUserToOrganisation(db, organisations[0].Orgid, user1.Userid) + postgresql.AddUserToOrganisation(db, organisations[1].Orgid, user1.Userid) // Add user2 to the three organization - postgresql.AddUserToOrganisation(db, organisations[0].OrgID, user2.UserID) - postgresql.AddUserToOrganisation(db, organisations[1].OrgID, user2.UserID) - postgresql.AddUserToOrganisation(db, organisations[2].OrgID, user2.UserID) + postgresql.AddUserToOrganisation(db, organisations[0].Orgid, user2.Userid) + postgresql.AddUserToOrganisation(db, organisations[1].Orgid, user2.Userid) + postgresql.AddUserToOrganisation(db, organisations[2].Orgid, user2.Userid) fmt.Println("Users added to organisations.") } else { diff --git a/internal/models/user.go b/internal/models/user.go index 8cb7c5b5..5e0e8863 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -5,12 +5,12 @@ import ( ) type User struct { - UserID string `gorm:"type:uuid;primaryKey" json:"user_id"` + Userid string `gorm:"type:uuid;primaryKey" json:"user_id"` Name string `gorm:"column:name; type:varchar(255)" json:"name"` Email string `gorm:"column:email; type:varchar(255)" json:"email"` - Profile Profile `gorm:"foreignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"profile"` + Profile Profile `gorm:"foreignKey:Userid;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"profile"` Organisations []Organisation `gorm:"many2many:user_organisations;" json:"organisations" ` // many to many relationship - Products []Product `gorm:"foreignKey:UserID" json:"products"` + Products []Product `gorm:"foreignKey:Userid" json:"products"` CreatedAt time.Time `gorm:"column:created_at; not null; autoCreateTime" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at; null; autoUpdateTime" json:"updated_at"` } @@ -20,4 +20,4 @@ type User struct { // LastName string `json:"last_name" validate:"required"` // Email string `json:"email" validate:"required"` // Password string `json:"password" validate:"required"` -// } \ No newline at end of file +// } From 8f2a98a41c63dd6813e580c9f4df05f69ac00dbd Mon Sep 17 00:00:00 2001 From: Micah Date: Sat, 20 Jul 2024 00:21:18 +0100 Subject: [PATCH 08/20] change uuid v7 --- go.mod | 6 ++++-- go.sum | 12 ++++++++++-- internal/models/seed/seed.go | 9 +++++---- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index e01d4552..aa687696 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/hngprojects/hng_boilerplate_golang_web -go 1.19 +go 1.21.3 + +toolchain go1.22.2 require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc @@ -12,9 +14,9 @@ require ( github.com/go-playground/universal-translator v0.18.1 github.com/go-playground/validator/v10 v10.22.0 github.com/gofrs/uuid v4.4.0+incompatible - github.com/google/uuid v1.4.0 github.com/jeanphorn/log4go v0.0.0-20231225120528-d93eb9001e51 github.com/nyaruka/phonenumbers v1.3.6 + github.com/samborkent/uuidv7 v0.0.0-20231110121620-f2e19d87e48b github.com/sirupsen/logrus v1.9.3 github.com/spf13/viper v1.19.0 gorm.io/driver/postgres v1.5.9 diff --git a/go.sum b/go.sum index 2ec5545b..1bca7a35 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,7 @@ github.com/didip/tollbooth v4.0.2+incompatible/go.mod h1:A9b0665CE6l1KmzpDws2++e github.com/elliotchance/phpserialize v1.4.0 h1:cAp/9+KSnEbUC8oYCE32n2n84BeW8HOY3HMDI8hG2OY= github.com/elliotchance/phpserialize v1.4.0/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs= 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/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= @@ -26,6 +27,7 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= @@ -37,9 +39,8 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= @@ -63,7 +64,9 @@ github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuV github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 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/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -85,11 +88,15 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6 github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= 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.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= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/samborkent/uuidv7 v0.0.0-20231110121620-f2e19d87e48b h1:39v+thWy220bPAl5iP0p0b1s5DXmrtidMFRZqYsmEfI= +github.com/samborkent/uuidv7 v0.0.0-20231110121620-f2e19d87e48b/go.mod h1:Z46aLAe76cDDo+W1m5zVg+KeB+4P2+xWENVEFFzbBuQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -150,6 +157,7 @@ google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFW google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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= diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go index 37596545..e69b11c1 100644 --- a/internal/models/seed/seed.go +++ b/internal/models/seed/seed.go @@ -3,18 +3,19 @@ package seed import ( "fmt" - "github.com/google/uuid" "gorm.io/gorm" + "github.com/gofrs/uuid" "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage/postgresql" ) func SeedDatabase(db *gorm.DB) { // Check and seed users - Userid1 := uuid.New().String() + + Userid1, _ := uuid.NewV7() user1 := models.User{ - Userid: Userid1, + Userid: Userid1.String(), Name: "John Doe", Email: "john@example.com", Profile: models.Profile{ @@ -30,7 +31,7 @@ func SeedDatabase(db *gorm.DB) { }, } - Userid2 := uuid.New().String() + Userid2, _ := uuid.NewV7() user2 := models.User{ Userid: Userid2, Name: "Jane Doe", From f346af1ab0f95c0b327912cf24447a12c3c0a09a Mon Sep 17 00:00:00 2001 From: Micah Date: Sat, 20 Jul 2024 00:32:58 +0100 Subject: [PATCH 09/20] changed uuid to version 7 format --- internal/models/seed/seed.go | 28 ++++++++++++++-------------- utility/uuid.go | 8 ++++++++ 2 files changed, 22 insertions(+), 14 deletions(-) create mode 100644 utility/uuid.go diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go index e69b11c1..bfccc84b 100644 --- a/internal/models/seed/seed.go +++ b/internal/models/seed/seed.go @@ -5,54 +5,54 @@ import ( "gorm.io/gorm" - "github.com/gofrs/uuid" "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage/postgresql" + "github.com/hngprojects/hng_boilerplate_golang_web/utility" ) func SeedDatabase(db *gorm.DB) { - // Check and seed users + // instantiate uuid - Userid1, _ := uuid.NewV7() + Userid1 := utility.GenerateUUID() user1 := models.User{ - Userid: Userid1.String(), + Userid: Userid1, Name: "John Doe", Email: "john@example.com", Profile: models.Profile{ - ID: uuid.New().String(), + ID: utility.GenerateUUID(), FirstName: "John", LastName: "Doe", Phone: "1234567890", AvatarURL: "http://example.com/avatar.jpg", }, Products: []models.Product{ - {ID: uuid.New().String(), Name: "Product1", Description: "Description1", Userid: Userid1}, - {ID: uuid.New().String(), Name: "Product2", Description: "Description2", Userid: Userid1}, + {ID: utility.GenerateUUID(), Name: "Product1", Description: "Description1", Userid: Userid1}, + {ID: utility.GenerateUUID(), Name: "Product2", Description: "Description2", Userid: Userid1}, }, } - Userid2, _ := uuid.NewV7() + Userid2 := utility.GenerateUUID() user2 := models.User{ Userid: Userid2, Name: "Jane Doe", Email: "jane@example.com", Profile: models.Profile{ - ID: uuid.New().String(), + ID: utility.GenerateUUID(), FirstName: "Jane", LastName: "Doe", Phone: "0987654321", AvatarURL: "http://example.com/avatar2.jpg", }, Products: []models.Product{ - {ID: uuid.New().String(), Name: "Product3", Description: "Description3", Userid: Userid2}, - {ID: uuid.New().String(), Name: "Product4", Description: "Description4", Userid: Userid2}, + {ID: utility.GenerateUUID(), Name: "Product3", Description: "Description3", Userid: Userid2}, + {ID: utility.GenerateUUID(), Name: "Product4", Description: "Description4", Userid: Userid2}, }, } organisations := []models.Organisation{ - {Orgid: uuid.New().String(), Name: "Org1", Description: "Description1"}, - {Orgid: uuid.New().String(), Name: "Org2", Description: "Description2"}, - {Orgid: uuid.New().String(), Name: "Org3", Description: "Description3"}, + {Orgid: utility.GenerateUUID(), Name: "Org1", Description: "Description1"}, + {Orgid: utility.GenerateUUID(), Name: "Org2", Description: "Description2"}, + {Orgid: utility.GenerateUUID(), Name: "Org3", Description: "Description3"}, } var existingUser models.User diff --git a/utility/uuid.go b/utility/uuid.go new file mode 100644 index 00000000..dc6d7546 --- /dev/null +++ b/utility/uuid.go @@ -0,0 +1,8 @@ +package utility + +import "github.com/gofrs/uuid" + +func GenerateUUID() string { + id, _ := uuid.NewV7() + return id.String() +} From 9ba6ac3221b22709616948f3645fa83d2689247d Mon Sep 17 00:00:00 2001 From: ekedonald Date: Sat, 20 Jul 2024 02:34:36 +0100 Subject: [PATCH 10/20] "Updated environment variables and PostgreSQL service configuration in development and staging workflow" --- .github/workflows/development.yml | 26 ++++++++++++-------------- .github/workflows/staging.yml | 26 ++++++++++++-------------- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 04926253..2ac132af 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -22,29 +22,27 @@ jobs: runs-on: ubuntu-latest needs: build env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: password - POSTGRES_DB: db_name + TEST_USERNAME: postgres + TEST_PASSWORD: password + TEST_DB_NAME: db_name + TEST_DB_HOST: localhost + TEST_DB_PORT: 5432 + TEST_DB_CONNECTION: pgsql + TEST_TIMEZONE: Africa/Lagos + TEST_SSLMODE: disable + TEST_MIGRATE: true services: postgres: image: postgres:latest env: - POSTGRES_USER: ${{ env.POSTGRES_USER }} - POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }} - POSTGRES_DB: ${{ env.POSTGRES_DB }} + POSTGRES_USER: ${{ env.TEST_USERNAME }} + POSTGRES_PASSWORD: ${{ env.TEST_PASSWORD }} + POSTGRES_DB: ${{ env.TEST_DB_NAME }} ports: - 5432:5432 steps: - name: Checkout Code uses: actions/checkout@v4 - - name: Create the app config file - run: cp app-sample.env app.env - - name: Run The Project - run: nohup go run main.go > /dev/null 2>&1 & - - name: Wait for application to start - run: sleep 30s - - name: Test for reachability - run: curl http://localhost:8019 - name: Run All Tests run: go test ./... -timeout 99999s diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index 36056249..ec762097 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -22,29 +22,27 @@ jobs: runs-on: ubuntu-latest needs: build env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: password - POSTGRES_DB: db_name + TEST_USERNAME: postgres + TEST_PASSWORD: password + TEST_DB_NAME: db_name + TEST_DB_HOST: localhost + TEST_DB_PORT: 5432 + TEST_DB_CONNECTION: pgsql + TEST_TIMEZONE: Africa/Lagos + TEST_SSLMODE: disable + TEST_MIGRATE: true services: postgres: image: postgres:latest env: - POSTGRES_USER: ${{ env.POSTGRES_USER }} - POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }} - POSTGRES_DB: ${{ env.POSTGRES_DB }} + POSTGRES_USER: ${{ env.TEST_USERNAME }} + POSTGRES_PASSWORD: ${{ env.TEST_PASSWORD }} + POSTGRES_DB: ${{ env.TEST_DB_NAME }} ports: - 5432:5432 steps: - name: Checkout Code uses: actions/checkout@v4 - - name: Create the app config file - run: cp app-sample.env app.env - - name: Run The Project - run: nohup go run main.go > /dev/null 2>&1 & - - name: Wait for application to start - run: sleep 30s - - name: Test for reachability - run: curl http://localhost:8019 - name: Run All Tests run: go test ./... -timeout 99999s From f9075e6bb09e79db47068b0c452743d8bd72ad95 Mon Sep 17 00:00:00 2001 From: Cyberguru1 Date: Sat, 20 Jul 2024 00:55:30 -0500 Subject: [PATCH 11/20] database setup fix --- go.mod | 3 +- go.sum | 2 - ...ansactions_migrations.go => migrations.go} | 0 internal/models/organisation.go | 28 +++++++++++-- internal/models/product.go | 12 ++++-- internal/models/profile.go | 16 ++++--- internal/models/seed/seed.go | 29 +++++-------- internal/models/user.go | 11 +---- main.go | 7 ++-- pkg/controller/seed/seed.go | 38 ++++------------- pkg/repository/storage/postgresql/create.go | 9 ---- pkg/router/seed.go | 3 +- services/seed/seed.go | 42 +++++++++++++++++++ 13 files changed, 110 insertions(+), 90 deletions(-) rename internal/models/migrations/{transactions_migrations.go => migrations.go} (100%) create mode 100644 services/seed/seed.go diff --git a/go.mod b/go.mod index aa687696..11fea3f0 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/hngprojects/hng_boilerplate_golang_web -go 1.21.3 +go 1.19 toolchain go1.22.2 @@ -16,7 +16,6 @@ require ( github.com/gofrs/uuid v4.4.0+incompatible github.com/jeanphorn/log4go v0.0.0-20231225120528-d93eb9001e51 github.com/nyaruka/phonenumbers v1.3.6 - github.com/samborkent/uuidv7 v0.0.0-20231110121620-f2e19d87e48b github.com/sirupsen/logrus v1.9.3 github.com/spf13/viper v1.19.0 gorm.io/driver/postgres v1.5.9 diff --git a/go.sum b/go.sum index 1bca7a35..d4d7bb2d 100644 --- a/go.sum +++ b/go.sum @@ -95,8 +95,6 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke 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/samborkent/uuidv7 v0.0.0-20231110121620-f2e19d87e48b h1:39v+thWy220bPAl5iP0p0b1s5DXmrtidMFRZqYsmEfI= -github.com/samborkent/uuidv7 v0.0.0-20231110121620-f2e19d87e48b/go.mod h1:Z46aLAe76cDDo+W1m5zVg+KeB+4P2+xWENVEFFzbBuQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= diff --git a/internal/models/migrations/transactions_migrations.go b/internal/models/migrations/migrations.go similarity index 100% rename from internal/models/migrations/transactions_migrations.go rename to internal/models/migrations/migrations.go diff --git a/internal/models/organisation.go b/internal/models/organisation.go index ad2c0e51..003befc0 100644 --- a/internal/models/organisation.go +++ b/internal/models/organisation.go @@ -1,8 +1,28 @@ package models +import ( + "time" + + "gorm.io/gorm" +) + type Organisation struct { - Orgid string `gorm:"type:uuid;primaryKey;unique;not null" json:"org_id"` - Name string `gorm:"type:varchar(255);not null" json:"name"` - Description string `gorm:"type:text" json:"description"` - Users []User `gorm:"many2many:user_organisations;foreignKey:Orgid;joinForeignKey:org_id;References:Userid;joinReferences:user_id" json:"users"` + ID string `gorm:"type:uuid;primaryKey;unique;not null" json:"id"` + Name string `gorm:"type:varchar(255);not null" json:"name"` + Description string `gorm:"type:text" json:"description"` + Users []User `gorm:"many2many:user_organisations;foreignKey:ID;joinForeignKey:org_id;References:ID;joinReferences:user_id" json:"users"` + CreatedAt time.Time `gorm:"column:created_at; not null; autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at; null; autoUpdateTime" json:"updated_at"` +} + +func AddUserToOrganisation(db *gorm.DB, user interface{}, orgs []interface{}) error { + + // Add user to organisation + + err := db.Model(user).Association("Organisations").Append(orgs...) + if err != nil { + return err + } + + return nil } diff --git a/internal/models/product.go b/internal/models/product.go index e18e27c7..d2903611 100644 --- a/internal/models/product.go +++ b/internal/models/product.go @@ -1,8 +1,12 @@ package models +import "time" + type Product struct { - ID string `gorm:"type:uuid;primaryKey" json:"product_id"` - Name string `gorm:"column:name; type:varchar(255); not null" json:"name"` - Description string `gorm:"column:description;type:text;" json:"description"` - Userid string `gorm:"type:uuid;" json:"user_id"` + ID string `gorm:"type:uuid;primaryKey" json:"product_id"` + Name string `gorm:"column:name; type:varchar(255); not null" json:"name"` + Description string `gorm:"column:description;type:text;" json:"description"` + OwnerID string `gorm:"type:uuid;" json:"owner_id"` + CreatedAt time.Time `gorm:"column:created_at; not null; autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at; null; autoUpdateTime" json:"updated_at"` } diff --git a/internal/models/profile.go b/internal/models/profile.go index 92d39869..821b0054 100644 --- a/internal/models/profile.go +++ b/internal/models/profile.go @@ -1,10 +1,14 @@ package models +import "time" + type Profile struct { - ID string `gorm:"type:uuid;primary_key" json:"profile_id"` - FirstName string `gorm:"column:first_name; type:text; not null" json:"first_name"` - LastName string `gorm:"column:last_name; type:text;not null" json:"last_name"` - Phone string `gorm:"type:varchar(255)" json:"phone"` - AvatarURL string `gorm:"type:varchar(255)" json:"avatar_url"` - Userid string `gorm:"type:uuid;" json:"user_id"` + ID string `gorm:"type:uuid;primary_key" json:"profile_id"` + FirstName string `gorm:"column:first_name; type:text; not null" json:"first_name"` + LastName string `gorm:"column:last_name; type:text;not null" json:"last_name"` + Phone string `gorm:"type:varchar(255)" json:"phone"` + AvatarURL string `gorm:"type:varchar(255)" json:"avatar_url"` + Userid string `gorm:"type:uuid;" json:"user_id"` + CreatedAt time.Time `gorm:"column:created_at; not null; autoCreateTime" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at; null; autoUpdateTime" json:"updated_at"` } diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go index bfccc84b..3332075a 100644 --- a/internal/models/seed/seed.go +++ b/internal/models/seed/seed.go @@ -15,7 +15,7 @@ func SeedDatabase(db *gorm.DB) { Userid1 := utility.GenerateUUID() user1 := models.User{ - Userid: Userid1, + ID: Userid1, Name: "John Doe", Email: "john@example.com", Profile: models.Profile{ @@ -26,14 +26,14 @@ func SeedDatabase(db *gorm.DB) { AvatarURL: "http://example.com/avatar.jpg", }, Products: []models.Product{ - {ID: utility.GenerateUUID(), Name: "Product1", Description: "Description1", Userid: Userid1}, - {ID: utility.GenerateUUID(), Name: "Product2", Description: "Description2", Userid: Userid1}, + {ID: utility.GenerateUUID(), Name: "Product1", Description: "Description1", OwnerID: Userid1}, + {ID: utility.GenerateUUID(), Name: "Product2", Description: "Description2", OwnerID: Userid1}, }, } Userid2 := utility.GenerateUUID() user2 := models.User{ - Userid: Userid2, + ID: Userid2, Name: "Jane Doe", Email: "jane@example.com", Profile: models.Profile{ @@ -44,15 +44,15 @@ func SeedDatabase(db *gorm.DB) { AvatarURL: "http://example.com/avatar2.jpg", }, Products: []models.Product{ - {ID: utility.GenerateUUID(), Name: "Product3", Description: "Description3", Userid: Userid2}, - {ID: utility.GenerateUUID(), Name: "Product4", Description: "Description4", Userid: Userid2}, + {ID: utility.GenerateUUID(), Name: "Product3", Description: "Description3", OwnerID: Userid2}, + {ID: utility.GenerateUUID(), Name: "Product4", Description: "Description4", OwnerID: Userid2}, }, } organisations := []models.Organisation{ - {Orgid: utility.GenerateUUID(), Name: "Org1", Description: "Description1"}, - {Orgid: utility.GenerateUUID(), Name: "Org2", Description: "Description2"}, - {Orgid: utility.GenerateUUID(), Name: "Org3", Description: "Description3"}, + {ID: utility.GenerateUUID(), Name: "Org1", Description: "Description1"}, + {ID: utility.GenerateUUID(), Name: "Org2", Description: "Description2"}, + {ID: utility.GenerateUUID(), Name: "Org3", Description: "Description3"}, } var existingUser models.User @@ -66,15 +66,8 @@ func SeedDatabase(db *gorm.DB) { fmt.Println("Users and organisations seeded.") // Add users to organisations - - // add user1 to two organization - postgresql.AddUserToOrganisation(db, organisations[0].Orgid, user1.Userid) - postgresql.AddUserToOrganisation(db, organisations[1].Orgid, user1.Userid) - - // Add user2 to the three organization - postgresql.AddUserToOrganisation(db, organisations[0].Orgid, user2.Userid) - postgresql.AddUserToOrganisation(db, organisations[1].Orgid, user2.Userid) - postgresql.AddUserToOrganisation(db, organisations[2].Orgid, user2.Userid) + models.AddUserToOrganisation(db, &user1, []interface{}{&organisations[0], &organisations[1]}) + models.AddUserToOrganisation(db, &user2, []interface{}{&organisations[0], &organisations[1], &organisations[2]}) fmt.Println("Users added to organisations.") } else { diff --git a/internal/models/user.go b/internal/models/user.go index 70e76d35..7f2aa4a7 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -5,19 +5,12 @@ import ( ) type User struct { - Userid string `gorm:"type:uuid;primaryKey;unique;not null" json:"id"` + ID string `gorm:"type:uuid;primaryKey;unique;not null" json:"id"` Name string `gorm:"column:name; type:varchar(255)" json:"name"` Email string `gorm:"column:email; type:varchar(255)" json:"email"` Profile Profile `gorm:"foreignKey:Userid;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"profile"` Organisations []Organisation `gorm:"many2many:user_organisations;;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"organisations" ` // many to many relationship - Products []Product `gorm:"foreignKey:Userid" json:"products"` + Products []Product `gorm:"foreignKey:OwnerID" json:"products"` CreatedAt time.Time `gorm:"column:created_at; not null; autoCreateTime" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at; null; autoUpdateTime" json:"updated_at"` } - -// type CreateUserRequestModel struct { -// FirstName string `json:"first_name" validate:"required"` -// LastName string `json:"last_name" validate:"required"` -// Email string `json:"email" validate:"required"` -// Password string `json:"password" validate:"required"` -// } diff --git a/main.go b/main.go index 8521a918..fb66f968 100644 --- a/main.go +++ b/main.go @@ -21,16 +21,17 @@ func main() { configuration := config.Setup(logger, "./app") postgresql.ConnectToDatabase(logger, configuration.Database) + validatorRef := validator.New() db := storage.Connection() if configuration.Database.Migrate { migrations.RunAllMigrations(db) - } - // call the seed function - seed.SeedDatabase(db.Postgresql) + // call the seed function + seed.SeedDatabase(db.Postgresql) + } r := router.Setup(logger, validatorRef, db, &configuration.App) diff --git a/pkg/controller/seed/seed.go b/pkg/controller/seed/seed.go index 7b73c147..1d4f03cb 100644 --- a/pkg/controller/seed/seed.go +++ b/pkg/controller/seed/seed.go @@ -1,16 +1,14 @@ package seed import ( - "fmt" "net/http" "github.com/gin-gonic/gin" "github.com/go-playground/validator/v10" "github.com/hngprojects/hng_boilerplate_golang_web/external/request" - "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" - "github.com/hngprojects/hng_boilerplate_golang_web/services/ping" + "github.com/hngprojects/hng_boilerplate_golang_web/services/seed" "github.com/hngprojects/hng_boilerplate_golang_web/utility" ) @@ -21,39 +19,17 @@ type Controller struct { ExtReq request.ExternalRequest } -func (base *Controller) Get1(c *gin.Context) { - if !ping.ReturnTrue() { - rd := utility.BuildErrorResponse(http.StatusInternalServerError, "error", "ping failed", fmt.Errorf("ping failed"), nil) - c.JSON(http.StatusBadRequest, rd) - return - } - - var user models.User - if err := base.Db.Postgresql.Preload("Profile").Preload("Products").Preload("Organisations").Where("email = ?", "john@example.com").First(&user).Error; err != nil { - fmt.Println(err) - return - } - - base.Logger.Info("user1 fetched successfully") - rd := utility.BuildSuccessResponse(http.StatusOK, "", user) - - c.JSON(http.StatusOK, rd) -} +func (base *Controller) GetUser(c *gin.Context) { -func (base *Controller) Get2(c *gin.Context) { - if !ping.ReturnTrue() { - rd := utility.BuildErrorResponse(http.StatusInternalServerError, "error", "ping failed", fmt.Errorf("ping failed"), nil) - c.JSON(http.StatusBadRequest, rd) - return - } + user, err := seed.GetUser(c, base.Db.Postgresql) - var user models.User - if err := base.Db.Postgresql.Preload("Profile").Preload("Products").Preload("Organisations").Where("email = ?", "jane@example.com").First(&user).Error; err != nil { - fmt.Println(err) + if err != nil { + rd := utility.BuildErrorResponse(http.StatusNotFound, "error", err.Error(), err, nil) + c.JSON(http.StatusInternalServerError, rd) return } - base.Logger.Info("user2 fetched successfully") + base.Logger.Info("user fetched successfully") rd := utility.BuildSuccessResponse(http.StatusOK, "", user) c.JSON(http.StatusOK, rd) diff --git a/pkg/repository/storage/postgresql/create.go b/pkg/repository/storage/postgresql/create.go index dd6572b9..ec7d3197 100644 --- a/pkg/repository/storage/postgresql/create.go +++ b/pkg/repository/storage/postgresql/create.go @@ -28,12 +28,3 @@ func CreateMultipleRecords(db *gorm.DB, model interface{}, length int) error { } return nil } - -func AddUserToOrganisation(db *gorm.DB, orgID, userID string) error { - // Add user to organisation - err := db.Exec("INSERT INTO user_organisations (organisation_orgid, user_userid) VALUES (?, ?)", orgID, userID).Error - if err != nil { - return err - } - return nil -} diff --git a/pkg/router/seed.go b/pkg/router/seed.go index bd62a1cb..f200d07f 100644 --- a/pkg/router/seed.go +++ b/pkg/router/seed.go @@ -18,8 +18,7 @@ func Seed(r *gin.Engine, ApiVersion string, validator *validator.Validate, db *s seedUrl := r.Group(fmt.Sprintf("%v", ApiVersion)) { - seedUrl.GET("/users/user_one_id", seed.Get1) - seedUrl.GET("/users/user_two_id", seed.Get2) + seedUrl.GET("/users/:user_id", seed.GetUser) } return r } diff --git a/services/seed/seed.go b/services/seed/seed.go new file mode 100644 index 00000000..f8c3bc55 --- /dev/null +++ b/services/seed/seed.go @@ -0,0 +1,42 @@ +package seed + +import ( + "errors" + "strconv" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" + + "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" +) + +func GetUser(c *gin.Context, db *gorm.DB) (models.User, error) { + + var userResp models.User + + // Get the user_id from the URL + userIDStr := c.Param("user_id") + + // convert the string id to integer + userID, err := strconv.Atoi(userIDStr) + if err != nil { + return userResp, err + } + + userEmail := "" + + if userID == 1 { + userEmail = "john@example.com" + } else if userID == 2 { + userEmail = "jane@example.com" + } else { + return userResp, errors.New("user id does not exist") + } + + if err := db.Preload("Profile").Preload("Products").Preload("Organisations").Where("email = ?", userEmail).First(&userResp).Error; err != nil { + return userResp, err + } + + return userResp, nil + +} From ac770c5d6c31d1f1e51b368e83f13e3954dbc39a Mon Sep 17 00:00:00 2001 From: ekedonald Date: Sat, 20 Jul 2024 11:26:34 +0100 Subject: [PATCH 12/20] "Created and added 'install_rabbitmq.sh' to the list of scripts in install_app_dependencies.sh" --- scripts/install_app_dependencies.sh | 1 + scripts/install_rabbitmq.sh | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 scripts/install_rabbitmq.sh diff --git a/scripts/install_app_dependencies.sh b/scripts/install_app_dependencies.sh index 3d661c0e..7ee4f337 100644 --- a/scripts/install_app_dependencies.sh +++ b/scripts/install_app_dependencies.sh @@ -21,6 +21,7 @@ SCRIPTS=( "install_postgres_and_go.sh" "install_pm2.sh" "setup_postgres.sh" + "install_rabbitmq.sh" ) # Make all scripts executable and execute them diff --git a/scripts/install_rabbitmq.sh b/scripts/install_rabbitmq.sh new file mode 100644 index 00000000..23028f69 --- /dev/null +++ b/scripts/install_rabbitmq.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +# Update package lists +sudo apt-get update + +# Install RabbitMQ server +sudo apt-get install -y rabbitmq-server + +# Start & Enable RabbitMQ +sudo systemctl start rabbitmq-server +sudo systemctl enable rabbitmq-server \ No newline at end of file From f06ac0f1f79a79ff3bf0df484413a4a815773b65 Mon Sep 17 00:00:00 2001 From: Micah Date: Sat, 20 Jul 2024 12:24:14 +0100 Subject: [PATCH 13/20] implemented receivers for data query --- internal/models/migrations/migrations.go | 1 - internal/models/organisation.go | 16 +------ internal/models/seed/seed.go | 4 +- internal/models/user.go | 26 ++++++++++- pkg/controller/invite/invite.go | 55 ++++++++++++++++++++++++ pkg/controller/seed/seed.go | 4 +- pkg/router/invite.go | 24 +++++++++++ pkg/router/router.go | 1 + services/seed/seed.go | 28 +----------- 9 files changed, 113 insertions(+), 46 deletions(-) create mode 100644 pkg/controller/invite/invite.go create mode 100644 pkg/router/invite.go diff --git a/internal/models/migrations/migrations.go b/internal/models/migrations/migrations.go index dcc16d6f..8840baef 100644 --- a/internal/models/migrations/migrations.go +++ b/internal/models/migrations/migrations.go @@ -2,7 +2,6 @@ package migrations import "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" -// _ = db.AutoMigrate(MigrationModels()...) func AuthMigrationModels() []interface{} { return []interface{}{ models.Organisation{}, diff --git a/internal/models/organisation.go b/internal/models/organisation.go index 003befc0..1c13552f 100644 --- a/internal/models/organisation.go +++ b/internal/models/organisation.go @@ -2,27 +2,13 @@ package models import ( "time" - - "gorm.io/gorm" ) type Organisation struct { - ID string `gorm:"type:uuid;primaryKey;unique;not null" json:"id"` + ID string `gorm:"type:uuid;primaryKey;unique;not null" json:"id"` Name string `gorm:"type:varchar(255);not null" json:"name"` Description string `gorm:"type:text" json:"description"` Users []User `gorm:"many2many:user_organisations;foreignKey:ID;joinForeignKey:org_id;References:ID;joinReferences:user_id" json:"users"` CreatedAt time.Time `gorm:"column:created_at; not null; autoCreateTime" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at; null; autoUpdateTime" json:"updated_at"` } - -func AddUserToOrganisation(db *gorm.DB, user interface{}, orgs []interface{}) error { - - // Add user to organisation - - err := db.Model(user).Association("Organisations").Append(orgs...) - if err != nil { - return err - } - - return nil -} diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go index 3332075a..7185b509 100644 --- a/internal/models/seed/seed.go +++ b/internal/models/seed/seed.go @@ -66,8 +66,8 @@ func SeedDatabase(db *gorm.DB) { fmt.Println("Users and organisations seeded.") // Add users to organisations - models.AddUserToOrganisation(db, &user1, []interface{}{&organisations[0], &organisations[1]}) - models.AddUserToOrganisation(db, &user2, []interface{}{&organisations[0], &organisations[1], &organisations[2]}) + existingUser.AddUserToOrganisation(db, &user1, []interface{}{&organisations[0], &organisations[1]}) + existingUser.AddUserToOrganisation(db, &user2, []interface{}{&organisations[0], &organisations[1], &organisations[2]}) fmt.Println("Users added to organisations.") } else { diff --git a/internal/models/user.go b/internal/models/user.go index 7f2aa4a7..f3c5b801 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -2,10 +2,12 @@ package models import ( "time" + + "gorm.io/gorm" ) type User struct { - ID string `gorm:"type:uuid;primaryKey;unique;not null" json:"id"` + ID string `gorm:"type:uuid;primaryKey;unique;not null" json:"id"` Name string `gorm:"column:name; type:varchar(255)" json:"name"` Email string `gorm:"column:email; type:varchar(255)" json:"email"` Profile Profile `gorm:"foreignKey:Userid;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"profile"` @@ -14,3 +16,25 @@ type User struct { CreatedAt time.Time `gorm:"column:created_at; not null; autoCreateTime" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at; null; autoUpdateTime" json:"updated_at"` } + +func (u *User) AddUserToOrganisation(db *gorm.DB, user interface{}, orgs []interface{}) error { + + // Add user to organisation + err := db.Model(user).Association("Organisations").Append(orgs...) + if err != nil { + return err + } + + return nil +} + + +func (u *User) GetUserByID(db *gorm.DB, userID string) (User, error) { + var user User + + if err := db.Preload("Profile").Preload("Products").Preload("Organisations").Where("id = ?", userID).First(&user).Error; err != nil { + return user, err + } + + return user, nil +} \ No newline at end of file diff --git a/pkg/controller/invite/invite.go b/pkg/controller/invite/invite.go new file mode 100644 index 00000000..0666b0fd --- /dev/null +++ b/pkg/controller/invite/invite.go @@ -0,0 +1,55 @@ +package invite + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + + "github.com/hngprojects/hng_boilerplate_golang_web/external/request" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" + "github.com/hngprojects/hng_boilerplate_golang_web/utility" +) + +type Controller struct { + Db *storage.Database + Validator *validator.Validate + Logger *utility.Logger + ExtReq request.ExternalRequest +} + +type InvitationRequest struct { + Emails []string `json:"emails" validate:"required"` + OrgID string `json:"org_id" binding:"uuid"` +} + + + +func (base *Controller) PostInvite(c *gin.Context) { + + var inviteReq InvitationRequest + + if err := c.ShouldBindJSON(&inviteReq); err != nil { + rd := utility.BuildErrorResponse(http.StatusBadRequest, "error", "Failed to parse request body", err, nil) + c.JSON(http.StatusBadRequest, rd) + return + } + + err := base.Validator.Struct(&inviteReq) + if err != nil { + rd := utility.BuildErrorResponse(http.StatusBadRequest,"error", "Validation failed", utility.ValidationResponse(err, base.Validator), nil) + c.JSON(http.StatusBadRequest, rd) + return + } + + // if err != nil { + // rd := utility.BuildErrorResponse(http.StatusNotFound, "error", err.Error(), err, nil) + // c.JSON(http.StatusInternalServerError, rd) + // return + // } + + base.Logger.Info("invite posted successfully") + rd := utility.BuildSuccessResponse(http.StatusOK, "", "invite url") + + c.JSON(http.StatusOK, rd) +} diff --git a/pkg/controller/seed/seed.go b/pkg/controller/seed/seed.go index 1d4f03cb..0376953a 100644 --- a/pkg/controller/seed/seed.go +++ b/pkg/controller/seed/seed.go @@ -20,8 +20,10 @@ type Controller struct { } func (base *Controller) GetUser(c *gin.Context) { + //get the user_id from the URL + userIDStr := c.Param("user_id") - user, err := seed.GetUser(c, base.Db.Postgresql) + user, err := seed.GetUser(userIDStr, base.Db.Postgresql) if err != nil { rd := utility.BuildErrorResponse(http.StatusNotFound, "error", err.Error(), err, nil) diff --git a/pkg/router/invite.go b/pkg/router/invite.go new file mode 100644 index 00000000..90615f7e --- /dev/null +++ b/pkg/router/invite.go @@ -0,0 +1,24 @@ +package router + +import ( + "fmt" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + + "github.com/hngprojects/hng_boilerplate_golang_web/external/request" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/controller/invite" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" + "github.com/hngprojects/hng_boilerplate_golang_web/utility" +) + +func Invite(r *gin.Engine, ApiVersion string, validator *validator.Validate, db *storage.Database, logger *utility.Logger) *gin.Engine { + extReq := request.ExternalRequest{Logger: logger, Test: false} + invite := invite.Controller{Db: db, Validator: validator, Logger: logger, ExtReq: extReq} + + inviteUrl := r.Group(fmt.Sprintf("%v", ApiVersion)) + { + inviteUrl.POST("/organisation/send-invite", invite.PostInvite) + } + return r +} diff --git a/pkg/router/router.go b/pkg/router/router.go index e59e2f17..14f2f60e 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -35,6 +35,7 @@ func Setup(logger *utility.Logger, validator *validator.Validate, db *storage.Da Health(r, ApiVersion, validator, db, logger) Seed(r, ApiVersion, validator, db, logger) + Invite(r, ApiVersion, validator, db, logger) r.GET("/", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ diff --git a/services/seed/seed.go b/services/seed/seed.go index f8c3bc55..3571f771 100644 --- a/services/seed/seed.go +++ b/services/seed/seed.go @@ -1,42 +1,18 @@ package seed import ( - "errors" - "strconv" - - "github.com/gin-gonic/gin" "gorm.io/gorm" "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" ) -func GetUser(c *gin.Context, db *gorm.DB) (models.User, error) { - +func GetUser(userIDStr string, db *gorm.DB) (models.User, error) { var userResp models.User - // Get the user_id from the URL - userIDStr := c.Param("user_id") - - // convert the string id to integer - userID, err := strconv.Atoi(userIDStr) + userResp, err := userResp.GetUserByID(db, userIDStr) if err != nil { return userResp, err } - userEmail := "" - - if userID == 1 { - userEmail = "john@example.com" - } else if userID == 2 { - userEmail = "jane@example.com" - } else { - return userResp, errors.New("user id does not exist") - } - - if err := db.Preload("Profile").Preload("Products").Preload("Organisations").Where("email = ?", userEmail).First(&userResp).Error; err != nil { - return userResp, err - } - return userResp, nil - } From 4130c9b4bee55f181da23b362f345713e867c8e3 Mon Sep 17 00:00:00 2001 From: Cyberguru1 Date: Sat, 20 Jul 2024 06:25:18 -0500 Subject: [PATCH 14/20] api for organization creation --- go.sum | 8 --- internal/models/organisation.go | 36 +++++++++++- internal/models/seed/seed.go | 6 +- internal/models/user.go | 17 +++++- pkg/controller/organisation/organisation.go | 63 +++++++++++++++++++++ pkg/router/organisation.go | 25 ++++++++ pkg/router/router.go | 1 + services/organisation/organisation.go | 61 ++++++++++++++++++++ 8 files changed, 203 insertions(+), 14 deletions(-) create mode 100644 pkg/controller/organisation/organisation.go create mode 100644 pkg/router/organisation.go create mode 100644 services/organisation/organisation.go diff --git a/go.sum b/go.sum index d4d7bb2d..f5e184cf 100644 --- a/go.sum +++ b/go.sum @@ -15,7 +15,6 @@ github.com/didip/tollbooth v4.0.2+incompatible/go.mod h1:A9b0665CE6l1KmzpDws2++e github.com/elliotchance/phpserialize v1.4.0 h1:cAp/9+KSnEbUC8oYCE32n2n84BeW8HOY3HMDI8hG2OY= github.com/elliotchance/phpserialize v1.4.0/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs= 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/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= @@ -27,7 +26,6 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= @@ -39,7 +37,6 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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= @@ -64,9 +61,7 @@ github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuV github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 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/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -88,9 +83,7 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6 github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= 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.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= @@ -155,7 +148,6 @@ google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFW google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 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= diff --git a/internal/models/organisation.go b/internal/models/organisation.go index 003befc0..6fc0399f 100644 --- a/internal/models/organisation.go +++ b/internal/models/organisation.go @@ -4,17 +4,38 @@ import ( "time" "gorm.io/gorm" + + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage/postgresql" ) type Organisation struct { - ID string `gorm:"type:uuid;primaryKey;unique;not null" json:"id"` + ID string `gorm:"type:uuid;primaryKey;unique;not null" json:"id"` Name string `gorm:"type:varchar(255);not null" json:"name"` Description string `gorm:"type:text" json:"description"` - Users []User `gorm:"many2many:user_organisations;foreignKey:ID;joinForeignKey:org_id;References:ID;joinReferences:user_id" json:"users"` + Email string `gorm:"type:varchar(255);unique" json:"email"` + State string `gorm:"type:varchar(255)" json:"state"` + Industry string `gorm:"type:varchar(255)" json:"industry"` + Type string `gorm:"type:varchar(255)" json:"type"` + Address string `gorm:"type:varchar(255)" json:"address"` + Country string `gorm:"type:varchar(255)" json:"country"` + Slug string `gorm:"type:varchar(255)" json:"slug"` + OwnerID string `gorm:"type:uuid;" json:"owner_id"` + Users []User `gorm:"many2many:user_organisations;foreignKey:ID;joinForeignKey:org_id;References:ID;joinReferences:user_id"` CreatedAt time.Time `gorm:"column:created_at; not null; autoCreateTime" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at; null; autoUpdateTime" json:"updated_at"` } +type CreateOrgRequestModel struct { + Name string `json:"name" validate:"required,min=2,max=255"` + Description string `json:"description" ` + Email string `json:"email" validate:"required,email"` + State string `json:"state" validate:"required"` + Industry string `json:"industry" validate:"required"` + Type string `json:"type" validate:"required"` + Address string `json:"address" validate:"required"` + Country string `json:"country" validate:"required"` +} + func AddUserToOrganisation(db *gorm.DB, user interface{}, orgs []interface{}) error { // Add user to organisation @@ -26,3 +47,14 @@ func AddUserToOrganisation(db *gorm.DB, user interface{}, orgs []interface{}) er return nil } + +func (c *Organisation) CreateOrganisation(db *gorm.DB) error { + + err := postgresql.CreateOneRecord(db, &c) + + if err != nil { + return err + } + + return nil +} diff --git a/internal/models/seed/seed.go b/internal/models/seed/seed.go index 3332075a..aeb809f1 100644 --- a/internal/models/seed/seed.go +++ b/internal/models/seed/seed.go @@ -50,9 +50,9 @@ func SeedDatabase(db *gorm.DB) { } organisations := []models.Organisation{ - {ID: utility.GenerateUUID(), Name: "Org1", Description: "Description1"}, - {ID: utility.GenerateUUID(), Name: "Org2", Description: "Description2"}, - {ID: utility.GenerateUUID(), Name: "Org3", Description: "Description3"}, + {ID: utility.GenerateUUID(), Name: "Org1", Email: fmt.Sprintf(utility.RandomString(4)+"@gmail.com"),Description: "Description1", OwnerID: Userid1}, + {ID: utility.GenerateUUID(), Name: "Org2", Email: fmt.Sprintf(utility.RandomString(4)+"@gmail.com"),Description: "Description2", OwnerID: Userid1}, + {ID: utility.GenerateUUID(), Name: "Org3", Email: fmt.Sprintf(utility.RandomString(4)+"@gmail.com"),Description: "Description3", OwnerID: Userid2}, } var existingUser models.User diff --git a/internal/models/user.go b/internal/models/user.go index 7f2aa4a7..770002d1 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -1,11 +1,14 @@ package models import ( + "errors" "time" + + "gorm.io/gorm" ) type User struct { - ID string `gorm:"type:uuid;primaryKey;unique;not null" json:"id"` + ID string `gorm:"type:uuid;primaryKey;unique;not null" json:"id"` Name string `gorm:"column:name; type:varchar(255)" json:"name"` Email string `gorm:"column:email; type:varchar(255)" json:"email"` Profile Profile `gorm:"foreignKey:Userid;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"profile"` @@ -14,3 +17,15 @@ type User struct { CreatedAt time.Time `gorm:"column:created_at; not null; autoCreateTime" json:"created_at"` UpdatedAt time.Time `gorm:"column:updated_at; null; autoUpdateTime" json:"updated_at"` } + +func GetUserByID(db *gorm.DB, userID string) (User, error) { + var user User + result := db.First(&user, "id = ?", userID) + if result.Error != nil { + if errors.Is(result.Error, gorm.ErrRecordNotFound) { + return user, errors.New("user not found") + } + return user, result.Error + } + return user, nil +} diff --git a/pkg/controller/organisation/organisation.go b/pkg/controller/organisation/organisation.go new file mode 100644 index 00000000..618ffe7e --- /dev/null +++ b/pkg/controller/organisation/organisation.go @@ -0,0 +1,63 @@ +package organisation + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + + "github.com/hngprojects/hng_boilerplate_golang_web/external/request" + "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" + service "github.com/hngprojects/hng_boilerplate_golang_web/services/organisation" + "github.com/hngprojects/hng_boilerplate_golang_web/utility" +) + +type Controller struct { + Db *storage.Database + Validator *validator.Validate + Logger *utility.Logger + ExtReq request.ExternalRequest +} + +func (base *Controller) CreateOrganisation(c *gin.Context) { + + var ( + req = models.CreateOrgRequestModel{} + ) + + err := c.ShouldBind(&req) + if err != nil { + rd := utility.BuildErrorResponse(http.StatusBadRequest, "error", "Failed to parse request body", err, nil) + c.JSON(http.StatusBadRequest, rd) + return + } + + err = base.Validator.Struct(&req) + if err != nil { + rd := utility.BuildErrorResponse(http.StatusBadRequest, "error", "Validation failed", utility.ValidationResponse(err, base.Validator), nil) + c.JSON(http.StatusBadRequest, rd) + return + } + + reqData, err := service.ValidateCreateOrgRequest(req, base.Db.Postgresql) + if err != nil { + rd := utility.BuildErrorResponse(http.StatusBadRequest, "error", err.Error(), err, nil) + c.JSON(http.StatusBadRequest, rd) + return + } + + userId := "0190ceb3-d6c4-7491-90db-3408a5b05b30" + + respData, err := service.CreateOrganisation(reqData, base.Db.Postgresql, userId) + if err != nil { + rd := utility.BuildErrorResponse(http.StatusBadRequest, "error", err.Error(), err, nil) + c.JSON(http.StatusBadRequest, rd) + return + } + + base.Logger.Info("organisation created successfully") + rd := utility.BuildSuccessResponse(http.StatusOK, "organisation created successfully", respData) + + c.JSON(http.StatusOK, rd) +} diff --git a/pkg/router/organisation.go b/pkg/router/organisation.go new file mode 100644 index 00000000..cdea7e6e --- /dev/null +++ b/pkg/router/organisation.go @@ -0,0 +1,25 @@ +package router + +import ( + "fmt" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + + "github.com/hngprojects/hng_boilerplate_golang_web/external/request" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/controller/organisation" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" + "github.com/hngprojects/hng_boilerplate_golang_web/utility" +) + +func Organisation(r *gin.Engine, ApiVersion string, validator *validator.Validate, db *storage.Database, logger *utility.Logger) *gin.Engine { + extReq := request.ExternalRequest{Logger: logger, Test: false} + organisation := organisation.Controller{Db: db, Validator: validator, Logger: logger, ExtReq: extReq} + + organisationUrl := r.Group(fmt.Sprintf("%v", ApiVersion)) + { + organisationUrl.POST("/organisations", organisation.CreateOrganisation) + + } + return r +} diff --git a/pkg/router/router.go b/pkg/router/router.go index e59e2f17..7c6f75bc 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -35,6 +35,7 @@ func Setup(logger *utility.Logger, validator *validator.Validate, db *storage.Da Health(r, ApiVersion, validator, db, logger) Seed(r, ApiVersion, validator, db, logger) + Organisation(r, ApiVersion, validator, db, logger) r.GET("/", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ diff --git a/services/organisation/organisation.go b/services/organisation/organisation.go new file mode 100644 index 00000000..fa301620 --- /dev/null +++ b/services/organisation/organisation.go @@ -0,0 +1,61 @@ +package service + +import ( + "errors" + "strings" + + "gorm.io/gorm" + + "github.com/hngprojects/hng_boilerplate_golang_web/internal/models" + "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage/postgresql" + "github.com/hngprojects/hng_boilerplate_golang_web/utility" +) + +func ValidateCreateOrgRequest(req models.CreateOrgRequestModel, db *gorm.DB) (models.CreateOrgRequestModel, error) { + + org := models.Organisation{} + + // Check if the organization already exists + exists := postgresql.CheckExists(db, &org, "email = ?", req.Email) + if exists { + return req, errors.New("organization already exists with the given email") + } + + return req, nil +} + +func CreateOrganisation(req models.CreateOrgRequestModel, db *gorm.DB, userId string) (*models.Organisation, error) { + + org := models.Organisation{ + ID: utility.GenerateUUID(), + Name: strings.ToLower(req.Name), + Description: strings.ToLower(req.Description), + Email: strings.ToLower(req.Email), + State: strings.ToLower(req.State), + Industry: strings.ToLower(req.Industry), + Type: strings.ToLower(req.Type), + OwnerID: userId, + Address: strings.ToLower(req.Address), + Country: strings.ToLower(req.Country), + } + + err := org.CreateOrganisation(db) + + if err != nil { + return nil, err + } + + user, err := models.GetUserByID(db, userId) + + if err != nil { + return nil, err + } + + err = models.AddUserToOrganisation(db, &user, []interface{}{&org}) + + if err != nil { + return nil, err + } + + return &org, nil +} From 368a7b9a464133ac7b5e296fb0e8f054f3e4ece7 Mon Sep 17 00:00:00 2001 From: Cyberguru1 Date: Sun, 21 Jul 2024 02:16:50 -0500 Subject: [PATCH 15/20] fix log file path --- tests/log.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/log.json b/tests/log.json index 272b6f91..9f6c54f3 100644 --- a/tests/log.json +++ b/tests/log.json @@ -7,7 +7,7 @@ { "enable": true, "level": "FINE", - "filename": "../logs/app.log", + "filename": "./logs/app.log", "category": "fileLogs", "pattern": "[%D %T] [%C] [%L] (%S) %M", "rotate": true, From 0b69927051a5a8dbbbc3e1292ca2ceccaa5787b2 Mon Sep 17 00:00:00 2001 From: Cyberguru1 Date: Sun, 21 Jul 2024 02:46:17 -0500 Subject: [PATCH 16/20] fix env read --- internal/config/configuration.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/config/configuration.go b/internal/config/configuration.go index 8bdbbbf2..487dc406 100644 --- a/internal/config/configuration.go +++ b/internal/config/configuration.go @@ -23,7 +23,7 @@ func Setup(logger *utility.Logger, name string) *Configuration { viper.AddConfigPath(".") if err := viper.ReadInConfig(); err != nil { - log.Fatalf("Error reading config file, %s", err) + log.Printf("Error reading config file, %s", err) } viper.AutomaticEnv() From 5f50f79f8b23da2a9d730d83890dc48dc2dfd8f8 Mon Sep 17 00:00:00 2001 From: Cyberguru1 Date: Sun, 21 Jul 2024 03:05:10 -0500 Subject: [PATCH 17/20] check port env --- internal/config/configuration.go | 1 + pkg/repository/storage/postgresql/connection.go | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/internal/config/configuration.go b/internal/config/configuration.go index 487dc406..ecc5c763 100644 --- a/internal/config/configuration.go +++ b/internal/config/configuration.go @@ -23,6 +23,7 @@ func Setup(logger *utility.Logger, name string) *Configuration { viper.AddConfigPath(".") if err := viper.ReadInConfig(); err != nil { + // remove from fatal to Printf to check environment next log.Printf("Error reading config file, %s", err) } diff --git a/pkg/repository/storage/postgresql/connection.go b/pkg/repository/storage/postgresql/connection.go index b7ac5bba..a5445385 100644 --- a/pkg/repository/storage/postgresql/connection.go +++ b/pkg/repository/storage/postgresql/connection.go @@ -2,18 +2,18 @@ package postgresql import ( "fmt" + "log" "net/url" "os" "strconv" - "log" + "gorm.io/driver/postgres" + "gorm.io/gorm" + lg "gorm.io/gorm/logger" "github.com/hngprojects/hng_boilerplate_golang_web/internal/config" "github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage" "github.com/hngprojects/hng_boilerplate_golang_web/utility" - "gorm.io/driver/postgres" - "gorm.io/gorm" - lg "gorm.io/gorm/logger" ) func ConnectToDatabase(logger *utility.Logger, configDatabases config.Database) *gorm.DB { @@ -29,6 +29,7 @@ func ConnectToDatabase(logger *utility.Logger, configDatabases config.Database) } func connectToDb(host, user, password, dbname, port, sslmode, timezone string, logger *utility.Logger) *gorm.DB { + fmt.Printf("%v, %T",port,port) if _, err := strconv.Atoi(port); err != nil { u, err := url.Parse(port) if err != nil { @@ -43,6 +44,7 @@ func connectToDb(host, user, password, dbname, port, sslmode, timezone string, l } port = detectedPort } + dsn := fmt.Sprintf("host=%v user=%v password=%v dbname=%v port=%v sslmode=%v TimeZone=%v", host, user, password, dbname, port, sslmode, timezone) newLogger := lg.New( From 93fe5688ecdcdd1c3686e0e73b7be55107f5b641 Mon Sep 17 00:00:00 2001 From: Cyberguru1 Date: Sun, 21 Jul 2024 04:48:39 -0500 Subject: [PATCH 18/20] fix viper env bind err --- dapp.env | 38 ++++++++++++++++++++++++++++++++ internal/config/configuration.go | 31 +++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 dapp.env diff --git a/dapp.env b/dapp.env new file mode 100644 index 00000000..cd211ec0 --- /dev/null +++ b/dapp.env @@ -0,0 +1,38 @@ +# server # +SERVER_PORT=8019 +SERVER_SECRET="mySecretKey" +SERVER_ACCESSTOKENEXPIREDURATION=2 +REQUEST_PER_SECOND=6 +TRUSTED_PROXIES=["192.168.0.1", "192.168.0.2"] +EXEMPT_FROM_THROTTLE=["127.0.0.1", "192.168.0.2", "::1"] + +# App # +APP_NAME = local +APP_URL=http://localhost:8019 + +# Databases # +DB_HOST=localhost +DB_PORT=5432 +DB_CONNECTION=pgsql +TIMEZONE=Africa/Lagos +SSLMODE=disable +USERNAME=postgres +PASSWORD=guru +DB_NAME=hashiradb +MIGRATE=true + +# Test # +export TEST_DB_HOST=localhost +export TEST_DB_PORT=5432 +export TEST_DB_CONNECTION=pgsql +export TEST_TIMEZONE=Africa/Lagos +export TEST_SSLMODE=disable +export TEST_USERNAME=postgres +export TEST_PASSWORD=guru +export TEST_DB_NAME=hashiradbtest +export TEST_MIGRATE=true + + +# IPSTACK +IPSTACK_KEY=key +IPSTACK_BASE_URL=http://api.ipstack.com diff --git a/internal/config/configuration.go b/internal/config/configuration.go index ecc5c763..8e2a7935 100644 --- a/internal/config/configuration.go +++ b/internal/config/configuration.go @@ -3,6 +3,7 @@ package config import ( "log" + "github.com/mitchellh/mapstructure" "github.com/spf13/viper" "github.com/hngprojects/hng_boilerplate_golang_web/utility" @@ -23,11 +24,20 @@ func Setup(logger *utility.Logger, name string) *Configuration { viper.AddConfigPath(".") if err := viper.ReadInConfig(); err != nil { - // remove from fatal to Printf to check environment next + // remove from fatal to Printf to check env log.Printf("Error reading config file, %s", err) - } + log.Printf("Reading from environment variable") + + viper.AutomaticEnv() - viper.AutomaticEnv() + var config BaseConfig + + // bind config keys to viper + err := BindKeys(viper.GetViper(), config) + if err != nil { + log.Fatalf("Unable to bindkeys in struct, %v", err) + } + } err := viper.Unmarshal(&baseConfiguration) if err != nil { @@ -46,3 +56,18 @@ func Setup(logger *utility.Logger, name string) *Configuration { func GetConfig() *Configuration { return Config } + +func BindKeys(v *viper.Viper, input interface{}) error { + + envKeysMap := &map[string]interface{}{} + if err := mapstructure.Decode(input, &envKeysMap); err != nil { + return err + } + for k := range *envKeysMap { + if bindErr := viper.BindEnv(k); bindErr != nil { + return bindErr + } + } + + return nil +} From 36407bb8d4ca2982620bc7d3546d5c31fc509403 Mon Sep 17 00:00:00 2001 From: Cyberguru1 Date: Sun, 21 Jul 2024 05:03:02 -0500 Subject: [PATCH 19/20] fix viper env bind err --- dapp.env | 38 ------------------- .../storage/postgresql/connection.go | 1 - 2 files changed, 39 deletions(-) delete mode 100644 dapp.env diff --git a/dapp.env b/dapp.env deleted file mode 100644 index cd211ec0..00000000 --- a/dapp.env +++ /dev/null @@ -1,38 +0,0 @@ -# server # -SERVER_PORT=8019 -SERVER_SECRET="mySecretKey" -SERVER_ACCESSTOKENEXPIREDURATION=2 -REQUEST_PER_SECOND=6 -TRUSTED_PROXIES=["192.168.0.1", "192.168.0.2"] -EXEMPT_FROM_THROTTLE=["127.0.0.1", "192.168.0.2", "::1"] - -# App # -APP_NAME = local -APP_URL=http://localhost:8019 - -# Databases # -DB_HOST=localhost -DB_PORT=5432 -DB_CONNECTION=pgsql -TIMEZONE=Africa/Lagos -SSLMODE=disable -USERNAME=postgres -PASSWORD=guru -DB_NAME=hashiradb -MIGRATE=true - -# Test # -export TEST_DB_HOST=localhost -export TEST_DB_PORT=5432 -export TEST_DB_CONNECTION=pgsql -export TEST_TIMEZONE=Africa/Lagos -export TEST_SSLMODE=disable -export TEST_USERNAME=postgres -export TEST_PASSWORD=guru -export TEST_DB_NAME=hashiradbtest -export TEST_MIGRATE=true - - -# IPSTACK -IPSTACK_KEY=key -IPSTACK_BASE_URL=http://api.ipstack.com diff --git a/pkg/repository/storage/postgresql/connection.go b/pkg/repository/storage/postgresql/connection.go index a5445385..feeb5f5e 100644 --- a/pkg/repository/storage/postgresql/connection.go +++ b/pkg/repository/storage/postgresql/connection.go @@ -29,7 +29,6 @@ func ConnectToDatabase(logger *utility.Logger, configDatabases config.Database) } func connectToDb(host, user, password, dbname, port, sslmode, timezone string, logger *utility.Logger) *gorm.DB { - fmt.Printf("%v, %T",port,port) if _, err := strconv.Atoi(port); err != nil { u, err := url.Parse(port) if err != nil { From e52ee7c2f9c9ae12e38cec1caace7b8a7b895c79 Mon Sep 17 00:00:00 2001 From: Cyberguru1 Date: Sun, 21 Jul 2024 05:08:04 -0500 Subject: [PATCH 20/20] fix viper env bind err --- go.mod | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/go.mod b/go.mod index e6ddaa42..4772dc2c 100644 --- a/go.mod +++ b/go.mod @@ -2,8 +2,6 @@ module github.com/hngprojects/hng_boilerplate_golang_web go 1.19 -toolchain go1.22.2 - require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc github.com/didip/tollbooth v4.0.2+incompatible @@ -16,6 +14,7 @@ require ( github.com/gofrs/uuid v4.4.0+incompatible github.com/golang-jwt/jwt v3.2.2+incompatible github.com/jeanphorn/log4go v0.0.0-20231225120528-d93eb9001e51 + github.com/mitchellh/mapstructure v1.5.0 github.com/nyaruka/phonenumbers v1.3.6 github.com/sirupsen/logrus v1.9.3 github.com/spf13/viper v1.19.0 @@ -45,7 +44,6 @@ require ( github.com/leodido/go-urn v1.4.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect