Skip to content

Commit

Permalink
Merge pull request #57 from Cyberguru1/main
Browse files Browse the repository at this point in the history
[FEAT] API Endpoint For Organisation Creation by users
  • Loading branch information
NwokoyeChigozie authored Jul 21, 2024
2 parents aa7ef5f + 9d94a56 commit 78c59dc
Show file tree
Hide file tree
Showing 20 changed files with 1,157 additions and 20 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ 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/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 Down Expand Up @@ -61,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
10 changes: 2 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand All @@ -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=
Expand All @@ -38,8 +36,9 @@ 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/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 @@ -64,9 +63,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=
Expand All @@ -88,9 +85,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=
Expand Down Expand Up @@ -155,7 +150,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=
Expand Down
35 changes: 34 additions & 1 deletion internal/models/organisation.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,46 @@ 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"`
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"`
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
}
8 changes: 5 additions & 3 deletions internal/models/seed/seed.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func SeedDatabase(db *gorm.DB) {
ID: Userid1,
Name: "John Doe",
Email: "[email protected]",
Password: utility.RandomString(20),
Profile: models.Profile{
ID: utility.GenerateUUID(),
FirstName: "John",
Expand All @@ -35,6 +36,7 @@ func SeedDatabase(db *gorm.DB) {
user2 := models.User{
ID: Userid2,
Name: "Jane Doe",
Password: utility.RandomString(20),
Email: "[email protected]",
Profile: models.Profile{
ID: utility.GenerateUUID(),
Expand All @@ -50,9 +52,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)+"@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
Expand Down
31 changes: 29 additions & 2 deletions internal/models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,36 @@ 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
Expand All @@ -28,7 +45,6 @@ func (u *User) AddUserToOrganisation(db *gorm.DB, user interface{}, orgs []inter
return nil
}


func (u *User) GetUserByID(db *gorm.DB, userID string) (User, error) {
var user User

Expand All @@ -37,4 +53,15 @@ func (u *User) GetUserByID(db *gorm.DB, userID string) (User, error) {
}

return user, nil
}
}

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

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

if err != nil {
return err
}

return nil
}
73 changes: 73 additions & 0 deletions pkg/controller/organisation/organisation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package organisation

import (
"net/http"

"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/golang-jwt/jwt"

"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.StatusUnprocessableEntity, "error", "Validation failed", utility.ValidationResponse(err, base.Validator), nil)
c.JSON(http.StatusUnprocessableEntity, rd)
return
}

reqData, code, err := service.ValidateCreateOrgRequest(req, base.Db.Postgresql)
if err != nil {
rd := utility.BuildErrorResponse(code, "error", err.Error(), err, nil)
c.JSON(code, rd)
return
}

claims, exists := c.Get("userClaims")
if !exists {
rd := utility.BuildErrorResponse(http.StatusBadRequest, "error", "unable to get user claims", err, nil)
c.JSON(http.StatusBadRequest, rd)
return
}

userClaims := claims.(jwt.MapClaims)

userId := userClaims["user_id"].(string)

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.StatusCreated, "organisation created successfully", respData)

c.JSON(http.StatusCreated, rd)
}
94 changes: 94 additions & 0 deletions pkg/controller/user/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package user

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"
"github.com/hngprojects/hng_boilerplate_golang_web/services/user"
"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) CreateUser(c *gin.Context) {

var (
req = models.CreateUserRequestModel{}
)

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.StatusUnprocessableEntity, "error", "Validation failed", utility.ValidationResponse(err, base.Validator), nil)
c.JSON(http.StatusUnprocessableEntity, rd)
return
}

reqData, err := user.ValidateCreateUserRequest(req, base.Db.Postgresql)
if err != nil {
rd := utility.BuildErrorResponse(http.StatusBadRequest, "error", err.Error(), err, nil)
c.JSON(http.StatusBadRequest, rd)
return
}

respData, code, err := user.CreateUser(reqData, base.Db.Postgresql)
if err != nil {
rd := utility.BuildErrorResponse(http.StatusBadRequest, "error", err.Error(), err, nil)
c.JSON(http.StatusBadRequest, rd)
return
}

base.Logger.Info("user created successfully")
rd := utility.BuildSuccessResponse(http.StatusCreated, "user created successfully", respData)

c.JSON(code, rd)
}

func (base *Controller) LoginUser(c *gin.Context) {

var (
req = models.LoginRequestModel{}
)

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
}

respData, code, err := user.LoginUser(req, base.Db.Postgresql)
if err != nil {
rd := utility.BuildErrorResponse(code, "error", err.Error(), err, nil)
c.JSON(http.StatusBadRequest, rd)
return
}

base.Logger.Info("user login successfully")

rd := utility.BuildSuccessResponse(http.StatusOK, "user login successfully", respData)
c.JSON(http.StatusOK, rd)
}
Loading

0 comments on commit 78c59dc

Please sign in to comment.