Skip to content

Commit

Permalink
Merge pull request #64 from hngprojects/dev
Browse files Browse the repository at this point in the history
Send to staging
  • Loading branch information
Cyberguru1 authored Jul 21, 2024
2 parents 26714a5 + c42f8ea commit e2e0f3c
Show file tree
Hide file tree
Showing 37 changed files with 1,559 additions and 26 deletions.
7 changes: 5 additions & 2 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 All @@ -12,10 +14,13 @@ 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/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
golang.org/x/crypto v0.23.0
gorm.io/driver/postgres v1.5.9
gorm.io/gorm v1.25.10
)
Expand All @@ -41,7 +46,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
Expand All @@ -59,7 +63,6 @@ require (
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.6.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/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/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
Expand Down
37 changes: 33 additions & 4 deletions internal/config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,12 @@ package config
import (
"log"

"github.com/hngprojects/hng_boilerplate_golang_web/utility"

"github.com/mitchellh/mapstructure"

"github.com/spf13/viper"

"github.com/hngprojects/hng_boilerplate_golang_web/utility"
)

// Setup initialize configuration
Expand All @@ -22,10 +26,20 @@ func Setup(logger *utility.Logger, name string) *Configuration {
viper.AddConfigPath(".")

if err := viper.ReadInConfig(); err != nil {
log.Fatalf("Error reading config file, %s", err)
}
// 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 {
Expand All @@ -44,3 +58,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
}
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.

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

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"`
Name string `gorm:"type:varchar(255);not null" json:"name"`
Description string `gorm:"type:text" json:"description"`
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"`
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"`
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 (c *Organisation) CreateOrganisation(db *gorm.DB) error {

err := postgresql.CreateOneRecord(db, &c)

if err != nil {
return err
}

return nil
}
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"`
}
82 changes: 82 additions & 0 deletions internal/models/seed/seed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
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]",
Password: utility.RandomString(20),
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",
Password: utility.RandomString(20),
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", Email: fmt.Sprintf(utility.RandomString(4)+"@email.com"),Description: "Description1", OwnerID: Userid1},
{ID: utility.GenerateUUID(), Name: "Org2", Email: fmt.Sprintf(utility.RandomString(4)+"@email.com"),Description: "Description2", OwnerID: Userid1},
{ID: utility.GenerateUUID(), Name: "Org3", Email: fmt.Sprintf(utility.RandomString(4)+"@email.com"),Description: "Description3", OwnerID: Userid2},
}

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.")
}

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

import (
"time"

"gorm.io/gorm"

"github.com/hngprojects/hng_boilerplate_golang_web/pkg/repository/storage/postgresql"
)

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"`
Password string `gorm:"column:password; type:text; not null" json:"-"`
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"`
}

type CreateUserRequestModel struct {
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
FirstName string `json:"first_name" validate:"required"`
LastName string `json:"last_name" validate:"required"`
UserName string `json:"username" validate:"required"`
PhoneNumber string `json:"phone_number"`
}

type LoginRequestModel struct {
Email string `json:"email" validate:"required"`
Password string `json:"password" validate:"required"`
}

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
}

func (u *User) CreateUser(db *gorm.DB) error {

err := postgresql.CreateOneRecord(db, &u)

if err != nil {
return err
}

return 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
Loading

0 comments on commit e2e0f3c

Please sign in to comment.