Skip to content

Commit

Permalink
chore: implement register staff user
Browse files Browse the repository at this point in the history
Signed-off-by: maxwellgithinji <maxwellgithinji@gmail.com>
  • Loading branch information
maxwellgithinji committed Oct 21, 2021
1 parent dd47f3a commit 568940f
Show file tree
Hide file tree
Showing 25 changed files with 2,185 additions and 147 deletions.
37 changes: 37 additions & 0 deletions pkg/onboarding/application/dto/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,40 @@ type LoginInput struct {
PIN string `json:"pin"`
Flavour feedlib.Flavour `json:"flavour"`
}

// UserInput contains the input for the User
type UserInput struct {
Username string // @handle, also globally unique; nickname

DisplayName string // user's preferred display name

// TODO Consider making the names optional in DB; validation in frontends
FirstName string // given name
MiddleName string
LastName string

// UserType string // TODO enum; e.g client, health care worker

// Gender string // TODO enum; genders; keep it simple

// Contacts []*domain.Contact // TODO: validate, ensure

// // for the preferred language list, order matters
// Languages []string // TODO: turn this into a slice of enums, start small (en, sw)
}

// StaffProfileInput contains input required to register a staff
type StaffProfileInput struct {
StaffNumber string

// Facilities []*domain.Facility // TODO: needs at least one

// A UI switcher optionally toggles the default
// TODO: the list of facilities to switch between is strictly those that the user is assigned to
DefaultFacilityID *string // TODO: required, FK to facility

// // there is nothing special about super-admin; just the set of roles they have
// Roles []domain.RoleType `json:"roles"` // TODO: roles are an enum (controlled list), known to both FE and BE

// Addresses []*domain.UserAddress
}
25 changes: 15 additions & 10 deletions pkg/onboarding/domain/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,18 @@ type User struct {
MiddleName string
LastName string

Flavour feedlib.Flavour
// UserType string // TODO enum; e.g client, health care worker

UserType string // TODO enum; e.g client, health care worker

Gender string // TODO enum; genders; keep it simple
// Gender string // TODO enum; genders; keep it simple

Active bool

Contacts []*Contact // TODO: validate, ensure
// Contacts []*Contact // TODO: validate, ensure

// for the preferred language list, order matters
Languages []string // TODO: turn this into a slice of enums, start small (en, sw)
// // for the preferred language list, order matters
// Languages []string // TODO: turn this into a slice of enums, start small (en, sw)

PushTokens []string
// PushTokens []string

// when a user logs in successfully, set this
LastSuccessfulLogin *time.Time
Expand All @@ -85,6 +83,7 @@ type User struct {

TermsAccepted bool
AcceptedTermsID string // foreign key to version of terms they accepted
Flavour feedlib.Flavour
}

// AuthCredentials is the authentication credentials for a given user
Expand Down Expand Up @@ -256,15 +255,15 @@ type Metric struct {
type StaffProfile struct {
ID *string

UserID uuid.UUID // foreign key to user
UserID *string // foreign key to user

StaffNumber string

Facilities []*Facility // TODO: needs at least one

// A UI switcher optionally toggles the default
// TODO: the list of facilities to switch between is strictly those that the user is assigned to
DefaultFacilityID string // TODO: required, FK to facility
DefaultFacilityID *string // TODO: required, FK to facility

// there is nothing special about super-admin; just the set of roles they have
Roles []string // TODO: roles are an enum (controlled list), known to both FE and BE
Expand All @@ -279,3 +278,9 @@ type PIN struct {
ConfirmedPin string
Flavour feedlib.Flavour
}

// StaffUserProfile combines user and staff profile
type StaffUserProfile struct {
User *User
Staff *StaffProfile
}
42 changes: 41 additions & 1 deletion pkg/onboarding/infrastructure/database/postgres/gorm/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import (
type Create interface {
GetOrCreateFacility(ctx context.Context, facility *Facility) (*Facility, error)
CollectMetrics(ctx context.Context, metrics *Metric) (*Metric, error)
// RegisterStaffUser(user User, profile StaffProfile) (*User, *StaffProfile, error)
SetUserPIN(ctx context.Context, pinData *PINData) (bool, error)
RegisterStaffUser(ctx context.Context, user *User, staff *StaffProfile) (*StaffUserProfile, error)
}

// GetOrCreateFacility ...
Expand Down Expand Up @@ -45,3 +45,43 @@ func (db *PGInstance) CollectMetrics(ctx context.Context, metrics *Metric) (*Met

return metrics, nil
}

// RegisterStaffUser creates both the user profile and the staff profile.
func (db *PGInstance) RegisterStaffUser(ctx context.Context, user *User, staff *StaffProfile) (*StaffUserProfile, error) {
// Initialize a database transaction
tx := db.DB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Error; err != nil {
return nil, fmt.Errorf("failied initialize database transaction %v", err)
}

// create a user profile, then rollback the transaction if it is unsuccessful
if err := tx.Create(user).Error; err != nil {
tx.Rollback()
return nil, fmt.Errorf("failed to create a user %v", err)
}

// assign userID in staff a value due to foreign keys constraint
staff.UserID = user.UserID

// create a staff profile, then rollback the transaction if it is unsuccessful
if err := tx.Create(staff).Error; err != nil {
tx.Rollback()
return nil, fmt.Errorf("failed to create a staff profile %v", err)
}

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

return &StaffUserProfile{
User: user,
Staff: staff,
}, nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type GormMock struct {
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)
RegisterStaffUserFn func(ctx context.Context, user *gorm.User, staff *gorm.StaffProfile) (*gorm.StaffUserProfile, error)

//Updates
UpdateUserLastSuccessfulLoginFn func(ctx context.Context, userID string, lastLoginTime time.Time, flavour string) error
Expand Down Expand Up @@ -182,6 +183,33 @@ func NewGormMock() *GormMock {
UpdateUserNextAllowedLoginFn: func(ctx context.Context, userID string, nextAllowedLoginTime time.Time, flavour string) error {
return nil
},
RegisterStaffUserFn: func(ctx context.Context, user *gorm.User, staff *gorm.StaffProfile) (*gorm.StaffUserProfile, error) {
ID := uuid.New().String()
testTime := time.Now()
return &gorm.StaffUserProfile{
User: &gorm.User{
UserID: &ID,
Username: "test",
DisplayName: "test",
FirstName: "test",
MiddleName: "test",
LastName: "test",
Active: true,
LastSuccessfulLogin: &testTime,
LastFailedLogin: &testTime,
NextAllowedLogin: &testTime,
FailedLoginCount: "0",
TermsAccepted: true,
AcceptedTermsID: ID,
},
Staff: &gorm.StaffProfile{
StaffProfileID: &ID,
UserID: &ID,
StaffNumber: "s123",
DefaultFacilityID: &ID,
},
}, nil
},
}
}

Expand Down Expand Up @@ -249,3 +277,8 @@ func (gm *GormMock) UpdateUserFailedLoginCount(ctx context.Context, userID strin
func (gm *GormMock) UpdateUserNextAllowedLogin(ctx context.Context, userID string, nextAllowedLoginTime time.Time, flavour string) error {
return gm.UpdateUserNextAllowedLoginFn(ctx, userID, nextAllowedLoginTime, flavour)
}

// RegisterStaffUser mocks the implementation of RegisterStaffUser method.
func (gm *GormMock) RegisterStaffUser(ctx context.Context, user *gorm.User, staff *gorm.StaffProfile) (*gorm.StaffUserProfile, error) {
return gm.RegisterStaffUserFn(ctx, user, staff)
}
29 changes: 17 additions & 12 deletions pkg/onboarding/infrastructure/database/postgres/gorm/tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,6 @@ type User struct {
MiddleName string `gorm:"column:middle_name"`
LastName string `gorm:"column:last_name"`

Flavour feedlib.Flavour `gorm:"column:flavour"`

UserType string `gorm:"column:user_type"` // TODO enum; e.g client, health care worker

Gender string `gorm:"column:gender"` // TODO enum; genders; keep it simple
Expand All @@ -104,7 +102,7 @@ type User struct {

Contacts []Contact `gorm:"many2many:user_contact;"` // TODO: validate, ensure

// for the preferred language list, order matters
// // for the preferred language list, order matters
Languages []string `gorm:"type:text[];column:languages"` // TODO: turn this into a slice of enums, start small (en, sw)

PushTokens []string `gorm:"type:text[];column:push_tokens"`
Expand All @@ -123,8 +121,9 @@ type User struct {
// calculated each time there is a failed login
NextAllowedLogin *time.Time `gorm:"type:time;column:next_allowed_login"`

TermsAccepted bool `gorm:"type:bool;column:terms_accepted"`
AcceptedTermsID string `gorm:"column:accepted_terms_id"` // foreign key to version of terms they accepted
TermsAccepted bool `gorm:"type:bool;column:terms_accepted"`
AcceptedTermsID string `gorm:"column:accepted_terms_id"` // foreign key to version of terms they accepted
Flavour feedlib.Flavour `gorm:"column:flavour"`
}

// BeforeCreate is a hook run before creating a new user
Expand Down Expand Up @@ -173,21 +172,21 @@ type StaffProfile struct {
StaffProfileID *string `gorm:"primaryKey;unique;column:staff_profile_id"`

UserID *string `gorm:"unique;column:user_id"` // foreign key to user
User User `gorm:"foreignKey:user_id;references:user_id;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
User User `gorm:"foreignKey:user_id;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`

StaffNumber string `gorm:"column:staff_number"`

Facilities []*Facility `gorm:"many2many:staffprofile_facility;not null;"` // TODO: needs at least one
// Facilities []*Facility `gorm:"many2many:staffprofile_facility;not null;"` // TODO: needs at least one

// A UI switcher optionally toggles the default
// TODO: the list of facilities to switch between is strictly those that the user is assigned to
DefaultFacilityID uuid.UUID `gorm:"column:default_facility_id"` // TODO: required, FK to facility
Facility Facility `gorm:"foreignKey:default_facility_id;references:facility_id;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
DefaultFacilityID *string `gorm:"column:default_facility_id"` // TODO: required, FK to facility
Facility Facility `gorm:"foreignKey:default_facility_id;references:facility_id;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`

// there is nothing special about super-admin; just the set of roles they have
Roles []string `gorm:"type:text[];column:roles"` // TODO: roles are an enum (controlled list), known to both FE and BE
// // there is nothing special about super-admin; just the set of roles they have
// Roles []string `gorm:"type:text[];column:roles"` // TODO: roles are an enum (controlled list), known to both FE and BE

Addresses []*UserAddress `gorm:"many2many:staffprofile_useraddress;"`
// Addresses []*UserAddress `gorm:"many2many:staffprofile_useraddress;"`
}

// BeforeCreate is a hook run before creating a new staff profile
Expand Down Expand Up @@ -226,6 +225,12 @@ func (UserAddress) TableName() string {
return "useraddress"
}

// StaffUserProfile combines user and staff profile
type StaffUserProfile struct {
User *User
Staff *StaffProfile
}

func allTables() []interface{} {
tables := []interface{}{
&Facility{},
Expand Down
62 changes: 50 additions & 12 deletions pkg/onboarding/infrastructure/database/postgres/mappers.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,18 @@ func (d *OnboardingDb) mapProfileObjectToDomain(profileObject *gorm.User) *domai
}

return &domain.User{
ID: profileObject.UserID,
Username: profileObject.Username,
DisplayName: profileObject.DisplayName,
FirstName: profileObject.FirstName,
MiddleName: profileObject.MiddleName,
LastName: profileObject.LastName,
Flavour: profileObject.Flavour,
UserType: profileObject.UserType,
Gender: profileObject.Gender,
Active: profileObject.Active,
Languages: profileObject.Languages,
PushTokens: profileObject.PushTokens,
ID: profileObject.UserID,
Username: profileObject.Username,
DisplayName: profileObject.DisplayName,
FirstName: profileObject.FirstName,
MiddleName: profileObject.MiddleName,
LastName: profileObject.LastName,
Flavour: profileObject.Flavour,
// UserType: profileObject.UserType,
// Gender: profileObject.Gender,
Active: profileObject.Active,
// Languages: profileObject.Languages,
// PushTokens: profileObject.PushTokens,
LastSuccessfulLogin: profileObject.LastSuccessfulLogin,
LastFailedLogin: profileObject.LastFailedLogin,
FailedLoginCount: profileObject.FailedLoginCount,
Expand All @@ -91,3 +91,41 @@ func (d *OnboardingDb) mapPINObjectToDomain(pinObj *gorm.PINData) *domain.UserPI
Salt: pinObj.Salt,
}
}

// mapUserObjectToDomain maps the db user to a domain model.
// It searches the database to fetch items specific to the user
func (d *OnboardingDb) mapRegisterStaffObjectToDomain(userStaffObject *gorm.StaffUserProfile) *domain.StaffUserProfile {

userObject := userStaffObject.User
staffObject := userStaffObject.Staff

user := &domain.User{
ID: userObject.UserID,
Username: userObject.Username,
DisplayName: userObject.DisplayName,
FirstName: userObject.FirstName,
MiddleName: userObject.MiddleName,
LastName: userObject.LastName,
Active: userObject.Active,
LastSuccessfulLogin: userObject.LastSuccessfulLogin,
LastFailedLogin: userObject.LastFailedLogin,
FailedLoginCount: userObject.FailedLoginCount,
NextAllowedLogin: userObject.NextAllowedLogin,
TermsAccepted: userObject.TermsAccepted,
AcceptedTermsID: userObject.AcceptedTermsID,
}

staffProfile := &domain.StaffProfile{
ID: staffObject.StaffProfileID,
UserID: userObject.UserID,
StaffNumber: staffObject.StaffNumber,
// Facilities: staffObject.Facilities,
DefaultFacilityID: staffObject.DefaultFacilityID,
// Roles: staffObject.Roles,
// Addresses: staffObject.Addresses,
}
return &domain.StaffUserProfile{
User: user,
Staff: staffProfile,
}
}

0 comments on commit 568940f

Please sign in to comment.