Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: make api usecases scaffold #28

Merged
merged 1 commit into from
Oct 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ require (
go.opentelemetry.io/contrib/instrumentation/github.com/gorilla/mux/otelmux v0.21.0
go.opentelemetry.io/otel v1.0.0-RC1
go.opentelemetry.io/otel/trace v1.0.0-RC1
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
gorm.io/datatypes v1.0.2
gorm.io/driver/postgres v1.1.2
gorm.io/gorm v1.21.16
Expand Down
25 changes: 10 additions & 15 deletions pkg/onboarding/application/dto/output.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
package dto

import (
"github.com/savannahghi/firebasetools"
"github.com/savannahghi/onboarding-service/pkg/onboarding/domain"
)
// // FacilityEdge is used to serialize GraphQL Relay edges for healthcare facilities
// type FacilityEdge struct {
// Cursor *string `json:"cursor"`
// Node *domain.Facility `json:"node"`
// }

// FacilityEdge is used to serialize GraphQL Relay edges for healthcare facilities
type FacilityEdge struct {
Cursor *string `json:"cursor"`
Node *domain.Facility `json:"node"`
}

// FacilityConnection is used to serialize GraphQL Relay connections for healthcare facilities
type FacilityConnection struct {
Edges []*FacilityEdge `json:"edges"`
PageInfo *firebasetools.PageInfo `json:"pageInfo"`
}
// // FacilityConnection is used to serialize GraphQL Relay connections for healthcare facilities
// type FacilityConnection struct {
// Edges []*FacilityEdge `json:"edges"`
// PageInfo *firebasetools.PageInfo `json:"pageInfo"`
// }
196 changes: 195 additions & 1 deletion pkg/onboarding/domain/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
//
// e.g CCC clinics, Pharmacies.
type Facility struct {
// ID is the Global customer ID(GCID)
// ID is the Global facility ID(GCID)
ID uuid.UUID
// unique within this structure
Name string
Expand All @@ -38,6 +38,200 @@ type Facility struct {
// Date string // TODO: Clear spec on validation e.g dates must be ISO 8601
// }

// User holds details that both the client and staff have in common
//
// Client and Staff cannot exist without being a user
type User struct {
ID uuid.UUID // globally unique ID

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

Active bool

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)

PushTokens []string

// when a user logs in successfully, set this
LastSuccessfulLogin *time.Time

// whenever there is a failed login (e.g bad PIN), set this
// reset to null / blank when they succeed at logging in
LastFailedLogin *time.Time

// each time there is a failed login, **increment** this
// set to zero after successful login
FailedLoginCount int

// calculated each time there is a failed login
NextAllowedLogin *time.Time

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

// AuthCredentials is the authentication credentials for a given user
type AuthCredentials struct {
User *User

RefreshToken string
IDToken string
ExpiresIn time.Time
}

// UserPIN is used to store users' PINs and their entire change history.
type UserPIN struct {
UserID string // TODO: At the DB, this should be indexed

HashedPIN string
ValidFrom time.Time
ValidTo time.Time

// TODO: Compute this each time an operation involving the PIN is carried out
// in order to make routine things e.g login via PIN fast
IsValid bool // TODO: Consider a composite or partial DB index with UserID, IsValid, flavour
Flavour string
}

// Identifier are specific/unique identifiers for a user
type Identifier struct {
ID *string // globally unique identifier
ClientID string // TODO: FK to client
IdentifierType string // TODO: Enum; start with basics e.g CCC number, ID number
IdentifierUse string // TODO: Enum; e.g official, temporary, old (see FHIR Person for enum)

// TODO: Validate identifier value against type e.g format of CCC number
// TODO: Unique together: identifier value & type i.e the same identifier can't be used for more than one client
IdentifierValue string // the actual identifier e.g CCC number
Description string
ValidFrom *time.Time
ValidTo *time.Time
Active bool
IsPrimaryIdentifier bool
}

// ClientProfile holds the details of end users who are not using the system in
// a professional capacity e.g consumers, patients etc.
//It is a linkage model e.g to tie together all of a person's identifiers
// and their health record ID
type ClientProfile struct {
ID string // globally unique identifier; synthetic i.e has no encoded meaning

// every client is a user first
// biodata is linked to the user record
// the client record is for bridging to other identifiers e.g patient record IDs
UserID string // TODO: Foreign key to User

TreatmentEnrollmentDate *time.Time // use for date of treatment enrollment

ClientType string // TODO: enum; e.g PMTCT, OVC

Active bool

HealthRecordID *string // optional link to a health record e.g FHIR Patient ID

// TODO: a client can have many identifiers; an identifier belongs to a client
// (implement reverse relation lookup)
Identifiers []*Identifier

Addresses []*Address

RelatedPersons []*RelatedPerson // e.g next of kin

// client's currently assigned facility
FacilityID string // TODO: FK

TreatmentBuddyUserID string // TODO: optional, FK to User

CHVUserID string // TODO: optional, FK to User

ClientCounselled bool
}

// Address are value objects for user address e.g postal code
type Address struct {
ID string

Type string // TODO: enum; postal, physical or both
Text string // actual address, can be multi-line
Country string // TODO: enum
PostalCode string
County string // TODO: counties belong to a country
Active bool
}

// RelatedPerson holds the details for person we consider relates to a Client
//
// It servers as Next of Kin details
type RelatedPerson struct {
ID string

Active bool
RelatedTo string // TODO: FK to client
RelationshipType string // TODO: enum
FirstName string
LastName string
OtherName string // TODO: optional
Gender string // TODO: enum

DateOfBirth *time.Time // TODO: optional
Addresses []*Address // TODO: optional
Contacts []*Contact // TODO: optional
}

// ClientProfileRegistrationPayload holds the registration input we need to register a client
//
// into the system. Every Client us a user first
type ClientProfileRegistrationPayload struct {
// every client is a user first
// biodata is linked to the user record
// the client record is for bridging to other identifiers e.g patient record IDs
UserID string // TODO: Foreign key to User

ClientType string // TODO: enum; e.g PMTCT, OVC

PrimaryIdentifier *Identifier // TODO: optional, default set if not givemn

Addresses []*Address

FacilityID string

TreatmentEnrollmentDate *time.Time

ClientCounselled bool

// TODO: when returning to UI, calculate length of treatment (return as days for ease of use in frontend)
}

// Contact hold contact information/details for users
type Contact struct {
ID string

Type string // TODO enum

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
}

// Metric reprents the metrics data structure input
type Metric struct {
// ensures we don't re-save the same metric; opaque; globally unique
Expand Down
101 changes: 101 additions & 0 deletions pkg/onboarding/usecases/client/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package client

import "github.com/savannahghi/onboarding-service/pkg/onboarding/domain"

// IRegisterClient ...
type IRegisterClient interface {
// TODO: the input client profile must not have an ID set
// validate identifiers when creating
// if the enrollemnt date is not supplied, set it automatically
// default to the client profile being active right after creation
// create a patient on FHIR (HealthRecordID
// if identifers not supplied (e.g patient being created on app), set
// an internal identifier as the default. It should be updated later
// with the CCC number or other final identifier
// TODO: ensure the user exists...supplied user ID
// TODO: only register clients who've been counselled
// TODO: consider: after successful registration, send invite link automatically
RegisterClient(user domain.User, profile domain.ClientProfileRegistrationPayload) (*domain.ClientProfile, error)
}

// IAddClientIdentifier ...
type IAddClientIdentifier interface {
// TODO idType is an enum
// TODO use idType and settings to decide if it's a primary identifier or not
AddIdentifier(clientID string, idType string, idValue string, isPrimary bool) (*domain.Identifier, error)
}

// IInactivateClient ...
type IInactivateClient interface {
// TODO Consider making reasons an enum
InactivateClient(clientID string, reason string, notes string) (bool, error)
}

// IReactivateClient ...
type IReactivateClient interface {
ReactivateClient(clientID string, reason string, notes string) (bool, error)
}

// ITransferClient ...
type ITransferClient interface {
// TODO: maintain log of past transfers, who did it etc
TransferClient(
clientID string,
OriginFacilityID string,
DestinationFacilityID string,
Reason string, // TODO: consider making this an enum
Notes string, // optional notes...e.g if the reason given is "Other"
) (bool, error)
}

// IGetClientIdentifiers ...
type IGetClientIdentifiers interface {
GetIdentifiers(clientID string, active bool) ([]*domain.Identifier, error)
}

// IInactivateClientIdentifier ...
type IInactivateClientIdentifier interface {
InactivateIdentifier(clientID string, identifierID string) (bool, error)
}

// IAssignTreatmentSupporter ...
type IAssignTreatmentSupporter interface {
AssignTreatmentSupporter(
clientID string,
treatmentSupporterID string,
treatmentSupporterType string, // TODO: enum, start with CHV and Treatment buddy
) (bool, error)
}

// IUnassignTreatmentSupporter ...
type IUnassignTreatmentSupporter interface {
UnassignTreatmentSupporter(
clientID string,
treatmentSupporterID string,
reason string, // TODO: ensure these are in an audit log
notes string, // TODO: Optional
) (bool, error)
}

// IAddRelatedPerson ...
type IAddRelatedPerson interface {
// add next of kin
AddRelatedPerson(
clientID string,
relatedPerson *domain.RelatedPerson,
) (*domain.RelatedPerson, bool)
}

// ClientProfileUseCases ...
type ClientProfileUseCases interface {
IAddClientIdentifier
IGetClientIdentifiers
IInactivateClientIdentifier
IRegisterClient
IInactivateClient
IReactivateClient
ITransferClient
IAssignTreatmentSupporter
IUnassignTreatmentSupporter
IAddRelatedPerson
}
19 changes: 9 additions & 10 deletions pkg/onboarding/usecases/facility/facility.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"context"

"github.com/google/uuid"
"github.com/savannahghi/firebasetools"
"github.com/savannahghi/onboarding-service/pkg/onboarding/application/dto"
"github.com/savannahghi/onboarding-service/pkg/onboarding/domain"
"github.com/savannahghi/onboarding-service/pkg/onboarding/infrastructure"
Expand Down Expand Up @@ -108,15 +107,15 @@ func (f *UseCaseFacilityImpl) Reactivate(id string) (*domain.Facility, error) {
return nil, nil
}

// List returns a list if health facility
// TODO Document: callers should specify active
func (f *UseCaseFacilityImpl) List(
pagination *firebasetools.PaginationInput,
filter []*dto.FacilityFilterInput,
sort []*dto.FacilitySortInput,
) (*dto.FacilityConnection, error) {
return nil, nil
}
// // List returns a list if health facility
// // TODO Document: callers should specify active
// func (f *UseCaseFacilityImpl) List(
// pagination *firebasetools.PaginationInput,
// filter []*dto.FacilityFilterInput,
// sort []*dto.FacilitySortInput,
// ) (*dto.FacilityConnection, error) {
// return nil, nil
// }

// RetrieveFacility find the health facility by ID
func (f *UseCaseFacilityImpl) RetrieveFacility(ctx context.Context, id *uuid.UUID) (*domain.Facility, error) {
Expand Down
Loading