From fb39d29ffbf40fe8a6235f1fd40ddadde8c05fe8 Mon Sep 17 00:00:00 2001 From: maxwellgithinji Date: Tue, 26 Oct 2021 09:31:52 +0300 Subject: [PATCH] feat: implement staff facilities Signed-off-by: maxwellgithinji --- pkg/onboarding/application/dto/input.go | 2 +- .../database/postgres/gorm/create.go | 32 ++++- .../database/postgres/gorm/tables.go | 17 ++- .../database/postgres/mappers.go | 26 +++- .../database/postgres/mock/pg_mock.go | 13 ++ .../database/postgres/pg_create.go | 44 +++++- .../database/postgres/pg_create_test.go | 10 ++ .../presentation/graph/generated/generated.go | 132 +++++++++++++++++- .../presentation/graph/input.graphql | 4 +- .../presentation/graph/types.graphql | 2 +- .../usecases/staff/staff_integration_test.go | 42 +++++- .../usecases/staff/staff_unit_test.go | 10 ++ 12 files changed, 314 insertions(+), 20 deletions(-) diff --git a/pkg/onboarding/application/dto/input.go b/pkg/onboarding/application/dto/input.go index 570f8455..26de5827 100644 --- a/pkg/onboarding/application/dto/input.go +++ b/pkg/onboarding/application/dto/input.go @@ -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 diff --git a/pkg/onboarding/infrastructure/database/postgres/gorm/create.go b/pkg/onboarding/infrastructure/database/postgres/gorm/create.go index bf9fede7..1c368eb3 100644 --- a/pkg/onboarding/infrastructure/database/postgres/gorm/create.go +++ b/pkg/onboarding/infrastructure/database/postgres/gorm/create.go @@ -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 { @@ -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() diff --git a/pkg/onboarding/infrastructure/database/postgres/gorm/tables.go b/pkg/onboarding/infrastructure/database/postgres/gorm/tables.go index 372243c2..750b1270 100644 --- a/pkg/onboarding/infrastructure/database/postgres/gorm/tables.go +++ b/pkg/onboarding/infrastructure/database/postgres/gorm/tables.go @@ -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 @@ -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() diff --git a/pkg/onboarding/infrastructure/database/postgres/mappers.go b/pkg/onboarding/infrastructure/database/postgres/mappers.go index b3bfdc81..65bd9612 100644 --- a/pkg/onboarding/infrastructure/database/postgres/mappers.go +++ b/pkg/onboarding/infrastructure/database/postgres/mappers.go @@ -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{} @@ -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, diff --git a/pkg/onboarding/infrastructure/database/postgres/mock/pg_mock.go b/pkg/onboarding/infrastructure/database/postgres/mock/pg_mock.go index 83483708..c47f4722 100644 --- a/pkg/onboarding/infrastructure/database/postgres/mock/pg_mock.go +++ b/pkg/onboarding/infrastructure/database/postgres/mock/pg_mock.go @@ -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, @@ -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 }, diff --git a/pkg/onboarding/infrastructure/database/postgres/pg_create.go b/pkg/onboarding/infrastructure/database/postgres/pg_create.go index bf962a2a..e1fd8b7f 100644 --- a/pkg/onboarding/infrastructure/database/postgres/pg_create.go +++ b/pkg/onboarding/infrastructure/database/postgres/pg_create.go @@ -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{} @@ -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 @@ -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 +} diff --git a/pkg/onboarding/infrastructure/database/postgres/pg_create_test.go b/pkg/onboarding/infrastructure/database/postgres/pg_create_test.go index a86a42c1..2ef26a06 100644 --- a/pkg/onboarding/infrastructure/database/postgres/pg_create_test.go +++ b/pkg/onboarding/infrastructure/database/postgres/pg_create_test.go @@ -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 } diff --git a/pkg/onboarding/presentation/graph/generated/generated.go b/pkg/onboarding/presentation/graph/generated/generated.go index c2c7495f..1526081e 100644 --- a/pkg/onboarding/presentation/graph/generated/generated.go +++ b/pkg/onboarding/presentation/graph/generated/generated.go @@ -290,6 +290,7 @@ type ComplexityRoot struct { StaffProfile struct { Addresses func(childComplexity int) int DefaultFacilityID func(childComplexity int) int + Facilities func(childComplexity int) int ID func(childComplexity int) int Roles func(childComplexity int) int StaffNumber func(childComplexity int) int @@ -1829,6 +1830,13 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.StaffProfile.DefaultFacilityID(childComplexity), true + case "StaffProfile.facilities": + if e.complexity.StaffProfile.Facilities == nil { + break + } + + return e.complexity.StaffProfile.Facilities(childComplexity), true + case "StaffProfile.id": if e.complexity.StaffProfile.ID == nil { break @@ -2461,8 +2469,8 @@ input UserInput { } input StaffProfileInput { - staffNumber: String! - # facilities: [FacilityInput!] + staffNumber: String! + facilities: [String!] defaultFacilityID: String! addresses: [AddressesInput!]! roles: [RolesType] # TODO: roles are an enum (controlled list), known to both FE and BE @@ -2555,7 +2563,7 @@ type StaffProfile { id: String! userID: String! staffNumber: String! - # facilities: [Facility!] + facilities: [Facility!] defaultFacilityID: String! addresses: [Addresses!] roles: [RolesType!] @@ -10025,6 +10033,38 @@ func (ec *executionContext) _StaffProfile_staffNumber(ctx context.Context, field return ec.marshalNString2string(ctx, field.Selections, res) } +func (ec *executionContext) _StaffProfile_facilities(ctx context.Context, field graphql.CollectedField, obj *domain1.StaffProfile) (ret graphql.Marshaler) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = graphql.Null + } + }() + fc := &graphql.FieldContext{ + Object: "StaffProfile", + Field: field, + Args: nil, + IsMethod: false, + IsResolver: false, + } + + ctx = graphql.WithFieldContext(ctx, fc) + resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { + ctx = rctx // use context from middleware stack in children + return obj.Facilities, nil + }) + if err != nil { + ec.Error(ctx, err) + return graphql.Null + } + if resTmp == nil { + return graphql.Null + } + res := resTmp.([]*domain1.Facility) + fc.Result = res + return ec.marshalOFacility2ᚕᚖgithubᚗcomᚋsavannahghiᚋonboardingᚑserviceᚋpkgᚋonboardingᚋdomainᚐFacilityᚄ(ctx, field.Selections, res) +} + func (ec *executionContext) _StaffProfile_defaultFacilityID(ctx context.Context, field graphql.CollectedField, obj *domain1.StaffProfile) (ret graphql.Marshaler) { defer func() { if r := recover(); r != nil { @@ -13503,6 +13543,14 @@ func (ec *executionContext) unmarshalInputStaffProfileInput(ctx context.Context, if err != nil { return it, err } + case "facilities": + var err error + + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("facilities")) + it.Facilities, err = ec.unmarshalOString2ᚕᚖstringᚄ(ctx, v) + if err != nil { + return it, err + } case "defaultFacilityID": var err error @@ -15140,6 +15188,8 @@ func (ec *executionContext) _StaffProfile(ctx context.Context, sel ast.Selection if out.Values[i] == graphql.Null { invalids++ } + case "facilities": + out.Values[i] = ec._StaffProfile_facilities(ctx, field, obj) case "defaultFacilityID": out.Values[i] = ec._StaffProfile_defaultFacilityID(ctx, field, obj) if out.Values[i] == graphql.Null { @@ -17243,6 +17293,46 @@ func (ec *executionContext) marshalOFacility2ᚕᚖgithubᚗcomᚋsavannahghiᚋ return ret } +func (ec *executionContext) marshalOFacility2ᚕᚖgithubᚗcomᚋsavannahghiᚋonboardingᚑserviceᚋpkgᚋonboardingᚋdomainᚐFacilityᚄ(ctx context.Context, sel ast.SelectionSet, v []*domain1.Facility) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + var wg sync.WaitGroup + isLen1 := len(v) == 1 + if !isLen1 { + wg.Add(len(v)) + } + for i := range v { + i := i + fc := &graphql.FieldContext{ + Index: &i, + Result: &v[i], + } + ctx := graphql.WithFieldContext(ctx, fc) + f := func(i int) { + defer func() { + if r := recover(); r != nil { + ec.Error(ctx, ec.Recover(ctx, r)) + ret = nil + } + }() + if !isLen1 { + defer wg.Done() + } + ret[i] = ec.marshalNFacility2ᚖgithubᚗcomᚋsavannahghiᚋonboardingᚑserviceᚋpkgᚋonboardingᚋdomainᚐFacility(ctx, sel, v[i]) + } + if isLen1 { + f(i) + } else { + go f(i) + } + + } + wg.Wait() + return ret +} + func (ec *executionContext) marshalOFacility2ᚖgithubᚗcomᚋsavannahghiᚋonboardingᚑserviceᚋpkgᚋonboardingᚋdomainᚐFacility(ctx context.Context, sel ast.SelectionSet, v *domain1.Facility) graphql.Marshaler { if v == nil { return graphql.Null @@ -18025,6 +18115,42 @@ func (ec *executionContext) marshalOString2ᚕstringᚄ(ctx context.Context, sel return ret } +func (ec *executionContext) unmarshalOString2ᚕᚖstringᚄ(ctx context.Context, v interface{}) ([]*string, error) { + if v == nil { + return nil, nil + } + var vSlice []interface{} + if v != nil { + if tmp1, ok := v.([]interface{}); ok { + vSlice = tmp1 + } else { + vSlice = []interface{}{v} + } + } + var err error + res := make([]*string, len(vSlice)) + for i := range vSlice { + ctx := graphql.WithPathContext(ctx, graphql.NewPathWithIndex(i)) + res[i], err = ec.unmarshalNString2ᚖstring(ctx, vSlice[i]) + if err != nil { + return nil, err + } + } + return res, nil +} + +func (ec *executionContext) marshalOString2ᚕᚖstringᚄ(ctx context.Context, sel ast.SelectionSet, v []*string) graphql.Marshaler { + if v == nil { + return graphql.Null + } + ret := make(graphql.Array, len(v)) + for i := range v { + ret[i] = ec.marshalNString2ᚖstring(ctx, sel, v[i]) + } + + return ret +} + func (ec *executionContext) unmarshalOString2ᚖstring(ctx context.Context, v interface{}) (*string, error) { if v == nil { return nil, nil diff --git a/pkg/onboarding/presentation/graph/input.graphql b/pkg/onboarding/presentation/graph/input.graphql index bbc49c22..c1d42d5a 100644 --- a/pkg/onboarding/presentation/graph/input.graphql +++ b/pkg/onboarding/presentation/graph/input.graphql @@ -27,8 +27,8 @@ input UserInput { } input StaffProfileInput { - staffNumber: String! - # facilities: [FacilityInput!] + staffNumber: String! + facilities: [String!] defaultFacilityID: String! addresses: [AddressesInput!]! roles: [RolesType] # TODO: roles are an enum (controlled list), known to both FE and BE diff --git a/pkg/onboarding/presentation/graph/types.graphql b/pkg/onboarding/presentation/graph/types.graphql index 7653158a..9354184a 100644 --- a/pkg/onboarding/presentation/graph/types.graphql +++ b/pkg/onboarding/presentation/graph/types.graphql @@ -50,7 +50,7 @@ type StaffProfile { id: String! userID: String! staffNumber: String! - # facilities: [Facility!] + facilities: [Facility!] defaultFacilityID: String! addresses: [Addresses!] roles: [RolesType!] diff --git a/pkg/onboarding/usecases/staff/staff_integration_test.go b/pkg/onboarding/usecases/staff/staff_integration_test.go index 321cbbc9..0539c0e6 100644 --- a/pkg/onboarding/usecases/staff/staff_integration_test.go +++ b/pkg/onboarding/usecases/staff/staff_integration_test.go @@ -21,6 +21,11 @@ func TestUseCaseStaffProfileImpl_RegisterStaffUser(t *testing.T) { testFacilityID := uuid.New().String() + // Type cast the ID values + type tempID struct { + ID string + } + code := ksuid.New().String() facilityInput := dto.FacilityInput{ Name: "test", @@ -30,11 +35,25 @@ func TestUseCaseStaffProfileImpl_RegisterStaffUser(t *testing.T) { Description: "test description", } + code2 := ksuid.New().String() + facilityInput2 := dto.FacilityInput{ + Name: "test", + Code: code2, + Active: true, + County: "test", + Description: "test description", + } + //valid: Create a facility facility, err := f.GetOrCreateFacility(ctx, facilityInput) assert.Nil(t, err) assert.NotNil(t, facility) + //valid: Create another facility + facility2, err := f.GetOrCreateFacility(ctx, facilityInput2) + assert.Nil(t, err) + assert.NotNil(t, facility2) + // First Set of Valid Input contactInput := &dto.ContactInput{ Type: enums.PhoneContact, @@ -70,10 +89,11 @@ func TestUseCaseStaffProfileImpl_RegisterStaffUser(t *testing.T) { Active: true, }, }, - Roles: []enums.RolesType{enums.RolesTypeCanInviteClient}, + Roles: []enums.RolesType{enums.RolesTypeCanInviteClient}, + Facilities: []*string{facility2.ID}, } - // Second set of valid Inputs + // Second set of valid Inputs (with duplicate facilities input) contactInput2 := &dto.ContactInput{ Type: enums.PhoneContact, Contact: randomdata.PhoneNumber(), @@ -108,7 +128,8 @@ func TestUseCaseStaffProfileImpl_RegisterStaffUser(t *testing.T) { Active: true, }, }, - Roles: []enums.RolesType{enums.RolesTypeCanInviteClient}, + Roles: []enums.RolesType{enums.RolesTypeCanInviteClient}, + Facilities: []*string{facility2.ID, facility.ID, facility2.ID}, } // Invalid facility id @@ -187,6 +208,15 @@ func TestUseCaseStaffProfileImpl_RegisterStaffUser(t *testing.T) { useStaffProfile, err = f.RegisterStaffUser(ctx, userInput, staffInput) assert.Nil(t, err) assert.NotNil(t, useStaffProfile) + // ensure the length of facilities is 2 for the first input + assert.Equal(t, len(useStaffProfile.Staff.Facilities), 2) + // ensure the default facility ID appears at index 1 + + var defaultFacilityID = tempID{} + var staffFacilityIndex0 = tempID{} + defaultFacilityID.ID = *useStaffProfile.Staff.DefaultFacilityID + staffFacilityIndex0.ID = *useStaffProfile.Staff.Facilities[0].ID + assert.Equal(t, staffFacilityIndex0.ID, defaultFacilityID.ID) //Invalid: creating a user with duplicate staff number and contact useStaffProfile, err = f.RegisterStaffUser(ctx, userInput, staffInput) @@ -207,6 +237,12 @@ func TestUseCaseStaffProfileImpl_RegisterStaffUser(t *testing.T) { useStaffProfile, err = f.RegisterStaffUser(ctx, userInput2, staffInpu2) assert.Nil(t, err) assert.NotNil(t, useStaffProfile) + // ensure the length of facilities is 2 for the second input when given duplicate facility IDs in input + assert.Equal(t, len(useStaffProfile.Staff.Facilities), 2) + // ensure the default facility ID appears at index 1 + defaultFacilityID.ID = *useStaffProfile.Staff.DefaultFacilityID + staffFacilityIndex0.ID = *useStaffProfile.Staff.Facilities[0].ID + assert.Equal(t, staffFacilityIndex0.ID, defaultFacilityID.ID) // TODO: teardown the user and replace randomdata with gofakeit diff --git a/pkg/onboarding/usecases/staff/staff_unit_test.go b/pkg/onboarding/usecases/staff/staff_unit_test.go index 142d66ef..f430b376 100644 --- a/pkg/onboarding/usecases/staff/staff_unit_test.go +++ b/pkg/onboarding/usecases/staff/staff_unit_test.go @@ -148,6 +148,16 @@ func TestOnboardingDb_RegisterStaffUser(t *testing.T) { }, }, Roles: []enums.RolesType{enums.RolesTypeCanInviteClient}, + Facilities: []*domain.Facility{ + { + ID: &testFacilityID, + Name: "test", + Code: "f1234", + Active: true, + County: "test", + Description: "test description", + }, + }, }, }, nil }