Skip to content

Commit

Permalink
Merge pull request #43 from Micah-Shallom/main
Browse files Browse the repository at this point in the history
[FEAT] Database Setup - Models, and Seeding
  • Loading branch information
NwokoyeChigozie authored Jul 20, 2024
2 parents ec84712 + f06ac0f commit aa7ef5f
Show file tree
Hide file tree
Showing 19 changed files with 367 additions and 12 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ 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
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand All @@ -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=
Expand All @@ -37,6 +39,7 @@ 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=
Expand All @@ -61,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=
Expand All @@ -83,7 +88,9 @@ 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=
Expand Down Expand Up @@ -148,6 +155,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=
Expand Down
3 changes: 2 additions & 1 deletion internal/config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions internal/config/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
Expand Down
16 changes: 16 additions & 0 deletions internal/models/migrations/migrations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package migrations

import "github.com/hngprojects/hng_boilerplate_golang_web/internal/models"

func AuthMigrationModels() []interface{} {
return []interface{}{
models.Organisation{},
models.Profile{},
models.Product{},
models.User{},
} // an array of db models, example: User{}
}

func AlterColumnModels() []AlterColumn {
return []AlterColumn{}
}
10 changes: 0 additions & 10 deletions internal/models/migrations/transactions_migrations.go

This file was deleted.

14 changes: 14 additions & 0 deletions internal/models/organisation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package models

import (
"time"
)

type Organisation struct {
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"`
}
12 changes: 12 additions & 0 deletions internal/models/product.go
Original file line number Diff line number Diff line change
@@ -0,0 +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"`
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"`
}
14 changes: 14 additions & 0 deletions internal/models/profile.go
Original file line number Diff line number Diff line change
@@ -0,0 +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"`
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"`
}
80 changes: 80 additions & 0 deletions internal/models/seed/seed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package seed

import (
"fmt"

"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 SeedDatabase(db *gorm.DB) {
// instantiate uuid

Userid1 := utility.GenerateUUID()
user1 := models.User{
ID: Userid1,
Name: "John Doe",
Email: "[email protected]",
Profile: models.Profile{
ID: utility.GenerateUUID(),
FirstName: "John",
LastName: "Doe",
Phone: "1234567890",
AvatarURL: "http://example.com/avatar.jpg",
},
Products: []models.Product{
{ID: utility.GenerateUUID(), Name: "Product1", Description: "Description1", OwnerID: Userid1},
{ID: utility.GenerateUUID(), Name: "Product2", Description: "Description2", OwnerID: Userid1},
},
}

Userid2 := utility.GenerateUUID()
user2 := models.User{
ID: Userid2,
Name: "Jane Doe",
Email: "[email protected]",
Profile: models.Profile{
ID: utility.GenerateUUID(),
FirstName: "Jane",
LastName: "Doe",
Phone: "0987654321",
AvatarURL: "http://example.com/avatar2.jpg",
},
Products: []models.Product{
{ID: utility.GenerateUUID(), Name: "Product3", Description: "Description3", OwnerID: Userid2},
{ID: utility.GenerateUUID(), Name: "Product4", Description: "Description4", OwnerID: Userid2},
},
}

organisations := []models.Organisation{
{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
if err := db.Preload("Profile").Preload("Products").Where("email = ?", user1.Email).First(&existingUser).Error; err != nil {
if err == gorm.ErrRecordNotFound {
postgresql.CreateOneRecord(db, &user1)
postgresql.CreateOneRecord(db, &user2)
for _, org := range organisations {
postgresql.CreateOneRecord(db, &org)
}
fmt.Println("Users and organisations seeded.")

// Add users to organisations
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 {
fmt.Println("An error occurred: ", err)
}
} else {
fmt.Println("Users already exist, skipping seeding.")
}

}
40 changes: 40 additions & 0 deletions internal/models/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package models

import (
"time"

"gorm.io/gorm"
)

type User struct {
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: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"`
}

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
}
6 changes: 6 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -19,12 +21,16 @@ 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)
}

r := router.Setup(logger, validatorRef, db, &configuration.App)
Expand Down
55 changes: 55 additions & 0 deletions pkg/controller/invite/invite.go
Original file line number Diff line number Diff line change
@@ -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)
}
38 changes: 38 additions & 0 deletions pkg/controller/seed/seed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package seed

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/services/seed"
"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) GetUser(c *gin.Context) {
//get the user_id from the URL
userIDStr := c.Param("user_id")

user, err := seed.GetUser(userIDStr, base.Db.Postgresql)

if err != nil {
rd := utility.BuildErrorResponse(http.StatusNotFound, "error", err.Error(), err, nil)
c.JSON(http.StatusInternalServerError, rd)
return
}

base.Logger.Info("user fetched successfully")
rd := utility.BuildSuccessResponse(http.StatusOK, "", user)

c.JSON(http.StatusOK, rd)
}
Loading

0 comments on commit aa7ef5f

Please sign in to comment.