Skip to content

Commit

Permalink
add update staff user acddress and contacts
Browse files Browse the repository at this point in the history
Signed-off-by: Kathurima Kimathi <kathurimakimathi415@gmail.com>
  • Loading branch information
KathurimaKimathi committed Oct 27, 2021
1 parent 912aaf2 commit 2ecf518
Show file tree
Hide file tree
Showing 22 changed files with 974 additions and 112 deletions.
1 change: 1 addition & 0 deletions pkg/onboarding/application/dto/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ type UserInput struct {
// for the preferred language list, order matters
Languages []enumutils.Language `json:"languages"`
Flavour feedlib.Flavour `json:"flavour"`
Address []*AddressesInput `json:"addressInput"`
}

// ContactInput contains input required to register a user
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/google/uuid"
"github.com/savannahghi/enumutils"
"github.com/savannahghi/feedlib"
"github.com/savannahghi/onboarding-service/pkg/onboarding/application/enums"
"github.com/savannahghi/onboarding-service/pkg/onboarding/infrastructure/database/postgres/gorm"
"github.com/segmentio/ksuid"
Expand All @@ -25,17 +26,19 @@ type GormMock struct {
CollectMetricsFn func(ctx context.Context, metrics *gorm.Metric) (*gorm.Metric, error)
SetUserPINFn func(ctx context.Context, pinData *gorm.PINData) (bool, error)
GetUserPINByUserIDFn func(ctx context.Context, userID string) (*gorm.PINData, error)
GetUserProfileByUserIDFn func(ctx context.Context, userID string, flavour string) (*gorm.User, error)
GetUserProfileByUserIDFn func(ctx context.Context, userID string, flavour feedlib.Flavour) (*gorm.User, error)
RegisterStaffUserFn func(ctx context.Context, user *gorm.User, staff *gorm.StaffProfile) (*gorm.StaffUserProfile, error)
RegisterClientFn func(ctx context.Context, userInput *gorm.User, clientInput *gorm.ClientProfile) (*gorm.ClientUserProfile, error)
AddIdentifierFn func(ctx context.Context, identifier *gorm.Identifier) (*gorm.Identifier, error)
GetClientProfileByClientIDFn func(ctx context.Context, clientID string) (*gorm.ClientProfile, error)
GetStaffProfileFn func(ctx context.Context, staffNumber string) (*gorm.StaffProfile, error)

//Updates
UpdateUserLastSuccessfulLoginFn func(ctx context.Context, userID string, lastLoginTime time.Time, flavour string) error
UpdateUserLastFailedLoginFn func(ctx context.Context, userID string, lastFailedLoginTime time.Time, flavour string) error
UpdateUserFailedLoginCountFn func(ctx context.Context, userID string, failedLoginCount string, flavour string) error
UpdateUserNextAllowedLoginFn func(ctx context.Context, userID string, nextAllowedLoginTime time.Time, flavour string) error
UpdateUserLastSuccessfulLoginFn func(ctx context.Context, userID string, lastLoginTime time.Time, flavour feedlib.Flavour) error
UpdateUserLastFailedLoginFn func(ctx context.Context, userID string, lastFailedLoginTime time.Time, flavour feedlib.Flavour) error
UpdateUserFailedLoginCountFn func(ctx context.Context, userID string, failedLoginCount string, flavour feedlib.Flavour) error
UpdateUserNextAllowedLoginFn func(ctx context.Context, userID string, nextAllowedLoginTime time.Time, flavour feedlib.Flavour) error
UpdateStaffUserFn func(ctx context.Context, userID string, user *gorm.User, staff *gorm.StaffProfile) (bool, error)
}

// NewGormMock initializes a new instance of `GormMock` then mocking the case of success.
Expand Down Expand Up @@ -160,7 +163,7 @@ func NewGormMock() *GormMock {
return true, nil
},

GetUserProfileByUserIDFn: func(ctx context.Context, userID, flavour string) (*gorm.User, error) {
GetUserProfileByUserIDFn: func(ctx context.Context, userID string, flavour feedlib.Flavour) (*gorm.User, error) {
id := uuid.New().String()
usercontact := &gorm.Contact{
ContactID: &id,
Expand Down Expand Up @@ -206,21 +209,47 @@ func NewGormMock() *GormMock {
}, nil
},

UpdateUserLastSuccessfulLoginFn: func(ctx context.Context, userID string, lastLoginTime time.Time, flavour string) error {
GetStaffProfileFn: func(ctx context.Context, staffNumber string) (*gorm.StaffProfile, error) {
testUID := ksuid.New().String()
address := &gorm.Addresses{
Type: "test",
Text: "test",
Country: "test",
PostalCode: "test",
County: "test",
Active: false,
StaffProfileID: new(string),
}
return &gorm.StaffProfile{
StaffProfileID: &testUID,
UserID: &testUID,
User: gorm.User{},
StaffNumber: "s100",
DefaultFacilityID: &testUID,
Addresses: []*gorm.Addresses{address},
}, nil
},

UpdateUserLastSuccessfulLoginFn: func(ctx context.Context, userID string, lastLoginTime time.Time, flavour feedlib.Flavour) error {
return nil
},

UpdateUserLastFailedLoginFn: func(ctx context.Context, userID string, lastFailedLoginTime time.Time, flavour string) error {
UpdateUserLastFailedLoginFn: func(ctx context.Context, userID string, lastFailedLoginTime time.Time, flavour feedlib.Flavour) error {
return nil
},

UpdateUserFailedLoginCountFn: func(ctx context.Context, userID, failedLoginCount, flavour string) error {
UpdateUserFailedLoginCountFn: func(ctx context.Context, userID, failedLoginCount string, flavour feedlib.Flavour) error {
return nil
},

UpdateUserNextAllowedLoginFn: func(ctx context.Context, userID string, nextAllowedLoginTime time.Time, flavour string) error {
UpdateUserNextAllowedLoginFn: func(ctx context.Context, userID string, nextAllowedLoginTime time.Time, flavour feedlib.Flavour) error {
return nil
},

UpdateStaffUserFn: func(ctx context.Context, userID string, user *gorm.User, staff *gorm.StaffProfile) (bool, error) {
return true, nil
},

RegisterStaffUserFn: func(ctx context.Context, user *gorm.User, staff *gorm.StaffProfile) (*gorm.StaffUserProfile, error) {
ID := uuid.New().String()
testTime := time.Now()
Expand Down Expand Up @@ -307,27 +336,27 @@ func (gm *GormMock) GetUserPINByUserID(ctx context.Context, userID string) (*gor
}

// GetUserProfileByUserID gets user profile by user ID
func (gm *GormMock) GetUserProfileByUserID(ctx context.Context, userID string, flavour string) (*gorm.User, error) {
func (gm *GormMock) GetUserProfileByUserID(ctx context.Context, userID string, flavour feedlib.Flavour) (*gorm.User, error) {
return gm.GetUserProfileByUserIDFn(ctx, userID, flavour)
}

//UpdateUserLastSuccessfulLogin ...
func (gm *GormMock) UpdateUserLastSuccessfulLogin(ctx context.Context, userID string, lastLoginTime time.Time, flavour string) error {
//UpdateUserLastSuccessfulLogin updates the user's last successful login time
func (gm *GormMock) UpdateUserLastSuccessfulLogin(ctx context.Context, userID string, lastLoginTime time.Time, flavour feedlib.Flavour) error {
return gm.UpdateUserLastSuccessfulLoginFn(ctx, userID, lastLoginTime, flavour)
}

// UpdateUserLastFailedLogin ...
func (gm *GormMock) UpdateUserLastFailedLogin(ctx context.Context, userID string, lastFailedLoginTime time.Time, flavour string) error {
// UpdateUserLastFailedLogin updates the user's last failed login
func (gm *GormMock) UpdateUserLastFailedLogin(ctx context.Context, userID string, lastFailedLoginTime time.Time, flavour feedlib.Flavour) error {
return gm.UpdateUserLastFailedLoginFn(ctx, userID, lastFailedLoginTime, flavour)
}

// UpdateUserFailedLoginCount ...
func (gm *GormMock) UpdateUserFailedLoginCount(ctx context.Context, userID string, failedLoginCount string, flavour string) error {
// UpdateUserFailedLoginCount updates the users failed login count
func (gm *GormMock) UpdateUserFailedLoginCount(ctx context.Context, userID string, failedLoginCount string, flavour feedlib.Flavour) error {
return gm.UpdateUserFailedLoginCountFn(ctx, userID, failedLoginCount, flavour)
}

// UpdateUserNextAllowedLogin ...
func (gm *GormMock) UpdateUserNextAllowedLogin(ctx context.Context, userID string, nextAllowedLoginTime time.Time, flavour string) error {
// UpdateUserNextAllowedLogin updates the user's next allowed login time
func (gm *GormMock) UpdateUserNextAllowedLogin(ctx context.Context, userID string, nextAllowedLoginTime time.Time, flavour feedlib.Flavour) error {
return gm.UpdateUserNextAllowedLoginFn(ctx, userID, nextAllowedLoginTime, flavour)
}

Expand All @@ -336,6 +365,16 @@ func (gm *GormMock) RegisterStaffUser(ctx context.Context, user *gorm.User, staf
return gm.RegisterStaffUserFn(ctx, user, staff)
}

// UpdateStaffUserProfile mocks the implementation of UpdateStaffUserProfile method.
func (gm *GormMock) UpdateStaffUserProfile(ctx context.Context, userID string, user *gorm.User, staff *gorm.StaffProfile) (bool, error) {
return gm.UpdateStaffUserFn(ctx, userID, user, staff)
}

// GetStaffProfile mocks the implementation of GetStaffProfile method.
func (gm *GormMock) GetStaffProfile(ctx context.Context, staffNumber string) (*gorm.StaffProfile, error) {
return gm.GetStaffProfileFn(ctx, staffNumber)
}

// RegisterClient mocks the implementation of RegisterClient method
func (gm *GormMock) RegisterClient(
ctx context.Context,
Expand Down
17 changes: 14 additions & 3 deletions pkg/onboarding/infrastructure/database/postgres/gorm/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ type Query interface {
RetrieveFacility(ctx context.Context, id *string, isActive bool) (*Facility, error)
RetrieveFacilityByMFLCode(ctx context.Context, MFLCode string, isActive bool) (*Facility, error)
GetFacilities(ctx context.Context) ([]Facility, error)
GetUserProfileByUserID(ctx context.Context, userID string, flavour string) (*User, error)
GetUserProfileByUserID(ctx context.Context, userID string, flavour feedlib.Flavour) (*User, error)
GetUserPINByUserID(ctx context.Context, userID string) (*PINData, error)
GetClientProfileByClientID(ctx context.Context, clientID string) (*ClientProfile, error)
GetStaffProfile(ctx context.Context, staffNumber string) (*StaffProfile, error)
}

// RetrieveFacility fetches a single facility
Expand All @@ -41,9 +42,9 @@ func (db *PGInstance) RetrieveFacilityByMFLCode(ctx context.Context, MFLCode str
}

// GetUserProfileByUserID fetches a user profile facility using the user ID
func (db *PGInstance) GetUserProfileByUserID(ctx context.Context, userID string, flavour string) (*User, error) {
func (db *PGInstance) GetUserProfileByUserID(ctx context.Context, userID string, flavour feedlib.Flavour) (*User, error) {
var user User
if err := db.DB.Where(&User{UserID: &userID, Flavour: feedlib.Flavour(flavour)}).First(&user).Error; err != nil {
if err := db.DB.Preload("Contacts").Where(&User{UserID: &userID, Flavour: flavour}).First(&user).Error; err != nil {
return nil, fmt.Errorf("failed to get user by userID %v: %v", userID, err)
}
return &user, nil
Expand Down Expand Up @@ -79,3 +80,13 @@ func (db *PGInstance) GetClientProfileByClientID(ctx context.Context, clientID s

return &client, nil
}

// GetStaffProfile retrieves a client profile by ID
func (db *PGInstance) GetStaffProfile(ctx context.Context, staffNumber string) (*StaffProfile, error) {
var staff StaffProfile
if err := db.DB.Where(&StaffProfile{StaffNumber: staffNumber}).First(&staff).Error; err != nil {
return nil, err
}

return &staff, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ type User struct {

Active bool `gorm:"column:active"`

Contacts []Contact `gorm:"ForeignKey:UserID"` // TODO: validate, ensure
Contacts []Contact `gorm:"ForeignKey:UserID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"` // TODO: validate, ensure

// // for the preferred language list, order matters
Languages pq.StringArray `gorm:"type:text[];column:languages"` // TODO: turn this into a slice of enums, start small (en, sw)
Expand Down Expand Up @@ -190,7 +190,7 @@ type StaffProfile struct {
// there is nothing special about super-admin; just the set of roles they have
Roles pq.StringArray `gorm:"type:text[];column:roles"` // TODO: roles are an enum (controlled list), known to both FE and BE

Addresses []*Addresses `gorm:"ForeignKey:StaffProfileID"`
Addresses []*Addresses `gorm:"ForeignKey:StaffProfileID;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
}

// BeforeCreate is a hook run before creating a new staff profile
Expand Down
100 changes: 88 additions & 12 deletions pkg/onboarding/infrastructure/database/postgres/gorm/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,21 @@ import (
"context"
"fmt"
"time"

"github.com/savannahghi/feedlib"
)

// Update represents all `update` ops to the database
type Update interface {
UpdateUserLastSuccessfulLogin(ctx context.Context, userID string, lastLoginTime time.Time, flavour string) error
UpdateUserLastFailedLogin(ctx context.Context, userID string, lastFailedLoginTime time.Time, flavour string) error
UpdateUserFailedLoginCount(ctx context.Context, userID string, failedLoginCount string, flavour string) error
UpdateUserNextAllowedLogin(ctx context.Context, userID string, nextAllowedLoginTime time.Time, flavour string) error
UpdateUserLastSuccessfulLogin(ctx context.Context, userID string, lastLoginTime time.Time, flavour feedlib.Flavour) error
UpdateUserLastFailedLogin(ctx context.Context, userID string, lastFailedLoginTime time.Time, flavour feedlib.Flavour) error
UpdateUserFailedLoginCount(ctx context.Context, userID string, failedLoginCount string, flavour feedlib.Flavour) error
UpdateUserNextAllowedLogin(ctx context.Context, userID string, nextAllowedLoginTime time.Time, flavour feedlib.Flavour) error
UpdateStaffUserProfile(ctx context.Context, userID string, user *User, staff *StaffProfile) (bool, error)
}

// UpdateUserLastSuccessfulLogin ...
func (db *PGInstance) UpdateUserLastSuccessfulLogin(ctx context.Context, userID string, lastLoginTime time.Time, flavour string) error {
// UpdateUserLastSuccessfulLogin updates users last successful login time
func (db *PGInstance) UpdateUserLastSuccessfulLogin(ctx context.Context, userID string, lastLoginTime time.Time, flavour feedlib.Flavour) error {
userProfile, err := db.GetUserProfileByUserID(ctx, userID, flavour)
if err != nil {
return fmt.Errorf("unable to get user profile by userID when updating: %v", err)
Expand All @@ -24,8 +27,8 @@ func (db *PGInstance) UpdateUserLastSuccessfulLogin(ctx context.Context, userID
return db.DB.Model(&User{}).Where(&User{UserID: userProfile.UserID, Flavour: userProfile.Flavour}).Updates(&User{LastSuccessfulLogin: &lastLoginTime}).Error
}

// UpdateUserLastFailedLogin ...
func (db *PGInstance) UpdateUserLastFailedLogin(ctx context.Context, userID string, lastFailedLoginTime time.Time, flavour string) error {
// UpdateUserLastFailedLogin updates user's last failed login time
func (db *PGInstance) UpdateUserLastFailedLogin(ctx context.Context, userID string, lastFailedLoginTime time.Time, flavour feedlib.Flavour) error {
userProfile, err := db.GetUserProfileByUserID(ctx, userID, flavour)
if err != nil {
return fmt.Errorf("unable to get user profile by userID when updating: %v", err)
Expand All @@ -34,8 +37,8 @@ func (db *PGInstance) UpdateUserLastFailedLogin(ctx context.Context, userID stri
return db.DB.Model(&User{}).Where(&User{UserID: userProfile.UserID, Flavour: userProfile.Flavour}).Updates(&User{LastFailedLogin: &lastFailedLoginTime}).Error
}

// UpdateUserFailedLoginCount ...
func (db *PGInstance) UpdateUserFailedLoginCount(ctx context.Context, userID string, failedLoginCount string, flavour string) error {
// UpdateUserFailedLoginCount updates users failed login count
func (db *PGInstance) UpdateUserFailedLoginCount(ctx context.Context, userID string, failedLoginCount string, flavour feedlib.Flavour) error {
userProfile, err := db.GetUserProfileByUserID(ctx, userID, flavour)
if err != nil {
return fmt.Errorf("unable to get user profile by userID when updating: %v", err)
Expand All @@ -45,12 +48,85 @@ func (db *PGInstance) UpdateUserFailedLoginCount(ctx context.Context, userID str
Updates(&User{FailedLoginCount: failedLoginCount}).Error
}

// UpdateUserNextAllowedLogin ...
func (db *PGInstance) UpdateUserNextAllowedLogin(ctx context.Context, userID string, nextAllowedLoginTime time.Time, flavour string) error {
// UpdateUserNextAllowedLogin updates the users next allowed login time
func (db *PGInstance) UpdateUserNextAllowedLogin(ctx context.Context, userID string, nextAllowedLoginTime time.Time, flavour feedlib.Flavour) error {
userProfile, err := db.GetUserProfileByUserID(ctx, userID, flavour)
if err != nil {
return fmt.Errorf("unable to get user profile by userID when updating: %v", err)
}

return db.DB.Model(&User{}).Where(&User{UserID: userProfile.UserID, Flavour: userProfile.Flavour}).Updates(&User{NextAllowedLogin: &nextAllowedLoginTime}).Error
}

// UpdateStaffUserProfile updates the staff user
func (db *PGInstance) UpdateStaffUserProfile(ctx context.Context, userID string, user *User, staff *StaffProfile) (bool, error) {
userProfile, err := db.GetUserProfileByUserID(ctx, userID, user.Flavour)
if err != nil {
return false, fmt.Errorf("unable to get user profile by userID: %v", err)
}

staffProfile, err := db.GetStaffProfile(ctx, staff.StaffNumber)
if err != nil {
return false, fmt.Errorf("unable to get staff profile by staff number: %v", err)
}

tx := db.DB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Error; err != nil {
return false, fmt.Errorf("failied initialize database transaction %v", err)
}

for _, c := range user.Contacts {
if err := tx.Model(&Contact{}).Where(&Contact{UserID: &userID}).Updates(&Contact{
Type: c.Type,
Contact: c.Contact,
Active: c.Active,
OptedIn: c.OptedIn,
}).Error; err != nil {
tx.Rollback()
return false, fmt.Errorf("failed to update a user data: %v", err)
}
}

for _, a := range staff.Addresses {
if err := tx.Model(&Addresses{}).Where(&Addresses{StaffProfileID: staffProfile.StaffProfileID}).Updates(&Addresses{
Type: a.Type,
Text: a.Text,
Country: a.Country,
PostalCode: a.PostalCode,
County: a.County,
Active: a.Active,
}).Error; err != nil {
tx.Rollback()
return false, fmt.Errorf("failed to update a staff profile: %v", err)
}
}

// Update a user profile, then rollback the transaction if it is unsuccessful
if err := tx.Model(&User{}).Where(&User{UserID: userProfile.UserID, Flavour: userProfile.Flavour}).Updates(&User{
Languages: user.Languages,
}).Error; err != nil {
tx.Rollback()
return false, fmt.Errorf("failed to update a user data: %v", err)
}

// update a staff profile, then rollback the transaction if it is unsuccessful
if err := tx.Model(&StaffProfile{}).Where(&StaffProfile{UserID: userProfile.UserID}).Updates(&StaffProfile{
DefaultFacilityID: staff.DefaultFacilityID,
}).Error; err != nil {
tx.Rollback()
return false, fmt.Errorf("failed to update a staff data: %v", err)
}

// try to commit the transactions
if err := tx.Commit().Error; err != nil {
tx.Rollback()
return false, fmt.Errorf("transaction commit to update a staff profile failed: %v", err)
}

return true, nil
}
31 changes: 31 additions & 0 deletions pkg/onboarding/infrastructure/database/postgres/mappers.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,3 +241,34 @@ func (d *OnboardingDb) mapClientObjectToDomain(client *gorm.ClientProfile) *doma
ClientCounselled: client.ClientCounselled,
}
}

//mapStaffObjectToDomain maps the staff object to the domain defined type
func (d *OnboardingDb) mapStaffObjectToDomain(staff *gorm.StaffProfile) *domain.StaffProfile {
if staff == nil {
return nil
}

addresses := []*domain.Addresses{}
if len(staff.Addresses) > 0 {
for _, a := range staff.Addresses {
address := &domain.Addresses{
ID: *a.AddressesID,
Type: a.Type,
Text: a.Text,
Country: a.Country,
PostalCode: a.PostalCode,
County: a.County,
Active: a.Active,
}
addresses = append(addresses, address)
}
}

return &domain.StaffProfile{
ID: staff.StaffProfileID,
UserID: staff.UserID,
StaffNumber: staff.StaffNumber,
DefaultFacilityID: staff.DefaultFacilityID,
Addresses: addresses,
}
}
Loading

0 comments on commit 2ecf518

Please sign in to comment.