Skip to content

Commit

Permalink
feat: implement contacts, languages, user input for staff user (#42)
Browse files Browse the repository at this point in the history
Signed-off-by: maxwellgithinji <maxwellgithinji@gmail.com>
  • Loading branch information
maxwellgithinji authored Oct 22, 2021
1 parent 17dc65f commit dd76b3f
Show file tree
Hide file tree
Showing 22 changed files with 1,290 additions and 253 deletions.
2 changes: 1 addition & 1 deletion documentation/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ type UserProfile struct {
// unique user name. Synonymous to a handle
// e.g @juliusowino
// this will be auto-generated on first login, meaning a user must have a username
UserName string `json:"userName" firestore:"userName"`
Username string `json:"userName" firestore:"userName"`

// VerifiedIdentifiers represent various ways the user has been able to login
// and these providers point to the same user
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ require (
firebase.google.com/go v3.13.0+incompatible
github.com/99designs/gqlgen v0.13.0
github.com/GoogleCloudPlatform/cloudsql-proxy v1.26.0
github.com/Pallinder/go-randomdata v1.2.0 // indirect
github.com/brianvoe/gofakeit v3.18.0+incompatible
github.com/casbin/casbin/v2 v2.31.3
github.com/google/uuid v1.3.0
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/imroc/req v0.3.0
github.com/lib/pq v1.10.3
github.com/savannahghi/converterandformatter v0.0.9
github.com/savannahghi/enumutils v0.0.3
github.com/savannahghi/feedlib v0.0.4
Expand All @@ -28,6 +31,7 @@ require (
github.com/savannahghi/serverutils v0.0.6
github.com/segmentio/ksuid v1.0.4
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.7.0
github.com/tj/assert v0.0.3
github.com/vektah/gqlparser/v2 v2.1.0
go.opencensus.io v0.23.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Pallinder/go-randomdata v1.2.0 h1:DZ41wBchNRb/0GfsePLiSwb0PHZmT67XY00lCDlaYPg=
github.com/Pallinder/go-randomdata v1.2.0/go.mod h1:yHmJgulpD2Nfrm0cR9tI/+oAgRqCQQixsA8HyRZfV9Y=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
Expand Down
37 changes: 26 additions & 11 deletions pkg/onboarding/application/dto/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,17 +122,32 @@ type ClientProfileInput struct {

// UserInput is used to supply user input for registration
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 `json:"firstName"`
MiddleName string `json:"middleName"`
LastName string `json:"lastName"`
UserName string `json:"userName"`
DisplayName string `json:"displayName"`
Gender enumutils.Gender `json:"gender"`
Flavour feedlib.Flavour `json:"flavour"`

// UserType string // TODO enum; e.g client, health care worker
// Contacts []*domain.Contact // TODO: validate, ensure
FirstName string // given name
MiddleName string
LastName string

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

Gender enumutils.Gender // TODO enum; genders; keep it simple

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

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

// ContactInput contains input required to register a user
type ContactInput struct {
Type enums.ContactType
Contact string //TODO Validate: phones are E164, emails are valid
Active bool
// a user may opt not to be contacted via this contact
// e.g if it's a shared phone owned by a teenager
OptedIn bool
}
58 changes: 58 additions & 0 deletions pkg/onboarding/application/enums/contact_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package enums

import (
"fmt"
"io"
"log"
"strconv"
)

// ContactType is a list of all the contact types.
type ContactType string

// contacts type constants
const (
PhoneContact ContactType = "PHONE"
EmailContact ContactType = "EMAIL"
)

// AllContacts is a set of a valid and known contact types.
var AllContacts = []ContactType{
PhoneContact,
EmailContact,
}

// IsValid returns true if a contact is valid
func (m ContactType) IsValid() bool {
switch m {
case PhoneContact, EmailContact:
return true
}
return false
}

func (m ContactType) String() string {
return string(m)
}

// UnmarshalGQL converts the supplied value to a contact type.
func (m *ContactType) UnmarshalGQL(v interface{}) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}

*m = ContactType(str)
if !m.IsValid() {
return fmt.Errorf("%s is not a valid ContactType", str)
}
return nil
}

// MarshalGQL writes the contact type to the supplied
func (m ContactType) MarshalGQL(w io.Writer) {
_, err := fmt.Fprint(w, strconv.Quote(m.String()))
if err != nil {
log.Printf("%v\n", err)
}
}
47 changes: 47 additions & 0 deletions pkg/onboarding/application/enums/usertype.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package enums

import (
"fmt"
)

// UsersType is a list of all the user types.
type UsersType string

// contacts type constants
const (
HealthcareWorkerUser UsersType = "HEALTHCAREWORKER"
ClientUser UsersType = "CLIENT"
)

// AllUsers is a set of a valid and known user types.
var AllUsers = []UsersType{
HealthcareWorkerUser,
ClientUser,
}

// IsValid returns true if a user is valid
func (m UsersType) IsValid() bool {
switch m {
case HealthcareWorkerUser, ClientUser:
return true
}
return false
}

func (m UsersType) String() string {
return string(m)
}

// UnmarshalGQL converts the supplied value to a user type.
func (m *UsersType) UnmarshalGQL(v interface{}) error {
str, ok := v.(string)
if !ok {
return fmt.Errorf("enums must be strings")
}

*m = UsersType(str)
if !m.IsValid() {
return fmt.Errorf("%s is not a valid UsersType", str)
}
return nil
}
8 changes: 4 additions & 4 deletions pkg/onboarding/domain/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ type User struct {
MiddleName string
LastName string

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

Gender enumutils.Gender
Gender enumutils.Gender // 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 []enumutils.Language // TODO: turn this into a slice of enums, start small (en, sw)
Expand Down Expand Up @@ -223,7 +223,7 @@ type ClientProfileRegistrationPayload struct {
type Contact struct {
ID *string

Type string // TODO enum
Type enums.ContactType // TODO enum

Contact string // TODO Validate: phones are E164, emails are valid

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func (db *PGInstance) RegisterClient(

if err := tx.Create(clientProfile).Error; err != nil {
tx.Rollback()
return nil, fmt.Errorf("failed to create a staff profile %v", err)
return nil, fmt.Errorf("failed to create a client profile %v", err)
}

if err := tx.Commit().Error; err != nil {
Expand Down
52 changes: 28 additions & 24 deletions pkg/onboarding/infrastructure/database/postgres/gorm/tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"time"

"github.com/google/uuid"
"github.com/lib/pq"
"github.com/savannahghi/enumutils"
"github.com/savannahghi/feedlib"
"github.com/savannahghi/onboarding-service/pkg/onboarding/application/enums"
Expand Down Expand Up @@ -53,6 +54,7 @@ type Metric struct {
// ensures we don't re-save the same metric; opaque; globally unique
MetricID *string `gorm:"primaryKey;autoIncrement:true;unique;column:metric_id"`

// TODO Metric types should be a controlled list i.e enum
Type enums.MetricType `gorm:"column:metric_type"`

// this will vary by context
Expand Down Expand Up @@ -94,16 +96,16 @@ type User struct {
MiddleName string `gorm:"column:middle_name"`
LastName string `gorm:"column:last_name"`

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

Gender enumutils.Gender `gorm:"column:gender"`
Gender enumutils.Gender `gorm:"column:gender"` // TODO enum; genders; keep it simple

Active bool `gorm:"column:active"`

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

// // 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)
Languages pq.StringArray `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 Down Expand Up @@ -144,15 +146,17 @@ type Contact struct {

ContactID *string `gorm:"primaryKey;unique;column:contact_id"`

Type string `gorm:"column:type"` // TODO enum
Type enums.ContactType `gorm:"column:type"` // TODO enum

Contact string `gorm:"column:contact"` // TODO Validate: phones are E164, emails are valid
Contact string `gorm:"unique;column:contact"` // TODO Validate: phones are E164, emails are valid

Active bool `gorm:"column:active"`

// a user may opt not to be contacted via this contact
// e.g if it's a shared phone owned by a teenager
OptedIn bool `gorm:"column:opted_in"`

UserID *string `gorm:"column:user_id"` // Foreign key
}

// BeforeCreate is a hook run before creating a new contact
Expand All @@ -172,9 +176,9 @@ 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;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`
User User `gorm:"foreignKey:user_id;references:user_id;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;"`

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

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

Expand Down Expand Up @@ -284,22 +288,8 @@ func (ClientProfile) TableName() string {
// ClientUserProfile holds the details of end users who are not using the system in
// a professional capacity e.g consumers, patients etc together with their user profile
type ClientUserProfile struct {
User *User `json:"user"`
Client *ClientProfile `json:"client"`
}

func allTables() []interface{} {
tables := []interface{}{
&Facility{},
&Metric{},
&User{},
&ClientProfile{},
&Contact{},
&StaffProfile{},
&UserAddress{},
&PINData{},
}
return tables
User *User
Client *ClientProfile
}

// PINData is the PIN's gorm data model.
Expand Down Expand Up @@ -327,3 +317,17 @@ func (p *PINData) BeforeCreate(tx *gorm.DB) (err error) {
func (PINData) TableName() string {
return "pindata"
}

func allTables() []interface{} {
tables := []interface{}{
&Facility{},
&Metric{},
&User{},
&Contact{},
&StaffProfile{},
&ClientProfile{},
&UserAddress{},
&PINData{},
}
return tables
}
Loading

0 comments on commit dd76b3f

Please sign in to comment.