Skip to content

Commit

Permalink
feat: implement staff facilities
Browse files Browse the repository at this point in the history
Signed-off-by: maxwellgithinji <maxwellgithinji@gmail.com>
  • Loading branch information
maxwellgithinji committed Oct 28, 2021
1 parent a6f7e15 commit fb39d29
Show file tree
Hide file tree
Showing 12 changed files with 314 additions and 20 deletions.
2 changes: 1 addition & 1 deletion pkg/onboarding/application/dto/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ type LoginInput struct {
type StaffProfileInput struct {
StaffNumber string `json:"staffNumber"`

// Facilities []*domain.Facility `json:"facilities"` // TODO: needs at least one
Facilities []*string // 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
Expand Down
32 changes: 30 additions & 2 deletions pkg/onboarding/infrastructure/database/postgres/gorm/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ func (db *PGInstance) CollectMetrics(ctx context.Context, metrics *Metric) (*Met

// 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 {
Expand All @@ -78,12 +77,41 @@ func (db *PGInstance) RegisterStaffUser(ctx context.Context, user *User, staff *
// assign userID in staff a value due to foreign keys constraint
staff.UserID = user.UserID

// Save the staff facilities in a temporary variable to be used when appending associations
tempFacilities := staff.Facilities

// Purge the staff facilities so we wont have duplicate facilities in results (We are using tempFacilities)
staff.Facilities = []*Facility{}

// create a staff profile, then rollback the transaction if it is unsuccessful
if err := tx.Create(staff).Error; err != nil {
// Omit the creation of facilities since we don't want to create new facilities when creating staff
// Associate staff to facilities and append the temp facilities
if err := tx.Omit("Facilities").Create(staff).Association("Facilities").Append(tempFacilities); err != nil {
tx.Rollback()
return nil, fmt.Errorf("failed to create a staff profile %v", err)
}

// TODO: remove manual saving of the associations
// Appending temp facilities is not saving data in the join table
// hence this manual save to the join table
for _, f := range tempFacilities {
// Prepare the join table with the facilities and staff IDs
staffProfileFacility := StaffprofileFacility{
StaffProfileID: *staff.StaffProfileID,
FacilityID: *f.FacilityID,
}
// Search for unique together fields in the join table and if they do not exist, insert the values for staffID and facilityID
var count int64
tx.Where("staff_profile_id = ? AND facility_id = ?", staff.StaffProfileID, f.FacilityID).Find(&staffProfileFacility).Count(&count)
if count < 1 {
err := tx.Create(staffProfileFacility).Error
if err != nil {
tx.Rollback()
return nil, fmt.Errorf("failed to create a values in staffprofile - facility join table %v", err)
}
}
}

// try to commit the transactions
if err := tx.Commit().Error; err != nil {
tx.Rollback()
Expand Down
17 changes: 14 additions & 3 deletions pkg/onboarding/infrastructure/database/postgres/gorm/tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,12 @@ type StaffProfile struct {

StaffNumber string `gorm:"unique;column:staff_number"`

// Facilities []*Facility `gorm:"many2many:staffprofile_facility;not null;"` // TODO: needs at least one
Facilities []*Facility `gorm:"many2many:staffprofile_facility;foreignKey:StaffProfileID;joinForeignKey:StaffProfileID;References:FacilityID;joinReferences:FacilityID"` // 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 `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:defaultfacility_id"` // TODO: required, FK to facility
Facility Facility `gorm:"foreignKey:defaultfacility_id;references:facility_id;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`

// 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
Expand Down Expand Up @@ -218,6 +218,17 @@ type Addresses struct {
StaffProfileID *string `gorm:"column:staff_profile_id"`
}

// StaffprofileFacility is a join table for staffprofile and facility
type StaffprofileFacility struct {
StaffProfileID string `gorm:"column:staff_profile_id"`
FacilityID string `gorm:"column:facility_id"`
}

// TableName customizes how the table name is generated
func (StaffprofileFacility) TableName() string {
return "staffprofile_facility"
}

// BeforeCreate is a hook run before creating a new address
func (a *Addresses) BeforeCreate(tx *gorm.DB) (err error) {
id := uuid.New().String()
Expand Down
26 changes: 22 additions & 4 deletions pkg/onboarding/infrastructure/database/postgres/mappers.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,24 @@ func (d *OnboardingDb) mapRegisterStaffObjectToDomain(userStaffObject *gorm.Staf
userObject := userStaffObject.User
staffObject := userStaffObject.Staff

facilities := []*domain.Facility{}

for _, f := range staffObject.Facilities {
active, err := strconv.ParseBool(f.Active)
if err != nil {
return nil
}
facility := &domain.Facility{
ID: f.FacilityID,
Name: f.Name,
Code: f.Code,
Active: active,
County: f.County,
Description: f.Description,
}
facilities = append(facilities, facility)
}

user := createMapUser(userObject)

addresses := []*domain.Addresses{}
Expand All @@ -106,10 +124,10 @@ func (d *OnboardingDb) mapRegisterStaffObjectToDomain(userStaffObject *gorm.Staf
}

staffProfile := &domain.StaffProfile{
ID: staffObject.StaffProfileID,
UserID: userObject.UserID,
StaffNumber: staffObject.StaffNumber,
// Facilities: staffObject.Facilities,
ID: staffObject.StaffProfileID,
UserID: userObject.UserID,
StaffNumber: staffObject.StaffNumber,
Facilities: facilities,
DefaultFacilityID: staffObject.DefaultFacilityID,
Addresses: addresses,
Roles: roles,
Expand Down
13 changes: 13 additions & 0 deletions pkg/onboarding/infrastructure/database/postgres/mock/pg_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ func NewPostgresMock() *PostgresMock {
testTime := time.Now()
roles := []enums.RolesType{enums.RolesTypeCanInviteClient}
languages := []enumutils.Language{enumutils.LanguageEn}
name := "Kanairo One"
code := "KN001"
county := "Kanairo"
description := "This is just for mocking"
facilityID := uuid.New().String()
return &domain.StaffUserProfile{
User: &domain.User{
ID: &ID,
Expand Down Expand Up @@ -221,6 +226,14 @@ func NewPostgresMock() *PostgresMock {
},
},
Roles: roles,
Facilities: []*domain.Facility{{
ID: &facilityID,
Name: name,
Code: code,
Active: true,
County: county,
Description: description,
}},
},
}, nil
},
Expand Down
44 changes: 43 additions & 1 deletion pkg/onboarding/infrastructure/database/postgres/pg_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,31 @@ func (d *OnboardingDb) RegisterStaffUser(ctx context.Context, user *dto.UserInpu
return nil, fmt.Errorf("expected default facility ID to be provided")
}

defaultFacilityID := staff.DefaultFacilityID

facilities := []*gorm.Facility{}

facilitiesInput := []string{}

// append default facility at index 0 in the slice of facilitiesInput
facilitiesInput = append(facilitiesInput, *defaultFacilityID)

// Append other facilities in the facilitiesInput
for _, staffFacility := range staff.Facilities {
facilitiesInput = append(facilitiesInput, *staffFacility)
}

// ensure we don't assign duplicate facilities
sanitizedFacilities := uniqueSliceOfString(facilitiesInput)

for _, f := range sanitizedFacilities {
facility, err := d.query.RetrieveFacility(ctx, &f, true)
if err != nil {
return nil, fmt.Errorf("failed to retrieve facility with id %v: %v", f, err)
}
facilities = append(facilities, facility)
}

userObject := createUserObject(user)

addresses := []*gorm.Addresses{}
Expand Down Expand Up @@ -128,12 +153,13 @@ func (d *OnboardingDb) RegisterStaffUser(ctx context.Context, user *dto.UserInpu
DefaultFacilityID: staff.DefaultFacilityID,
Addresses: addresses,
Roles: roles,
Facilities: facilities,
}

userStaffProfile, err := d.create.RegisterStaffUser(ctx, userObject, staffObject)
if err != nil {

return nil, fmt.Errorf("failed to create user session %v", err)
return nil, fmt.Errorf("failed to create user session: %v", err)
}

return d.mapRegisterStaffObjectToDomain(userStaffProfile), nil
Expand Down Expand Up @@ -227,3 +253,19 @@ func createUserObject(user *dto.UserInput) *gorm.User {
}
return userObject
}

// A helper function that ensures sanitizes the facility ID inputs for staff
func uniqueSliceOfString(slice []string) []string {
var unique []string
sampleLoop:
for _, v := range slice {
for i, u := range unique {
if v == u {
unique[i] = v
continue sampleLoop
}
}
unique = append(unique, v)
}
return unique
}
10 changes: 10 additions & 0 deletions pkg/onboarding/infrastructure/database/postgres/pg_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,16 @@ func TestOnboardingDb_RegisterStaffUser(t *testing.T) {
},
},
Roles: []string{enums.RolesTypeCanInviteClient.String()},
Facilities: []*gorm.Facility{
{
FacilityID: &testFacilityID,
Name: "test",
Code: "f1234",
Active: "true",
County: "test",
Description: "test description",
},
},
},
}, nil
}
Expand Down

0 comments on commit fb39d29

Please sign in to comment.