Skip to content

Commit

Permalink
feat(CPL-235): definitive profile creation on healthcrm service
Browse files Browse the repository at this point in the history
  • Loading branch information
biko committed Apr 16, 2024
1 parent ea951e1 commit f7c87a9
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 93 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ coverage.html
coverage.json
coverage_report.txt
.vscode/
venv
venv
.env/
29 changes: 29 additions & 0 deletions healthcrm.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,3 +367,32 @@ func (h *HealthCRMLib) GetService(ctx context.Context, serviceID string) (*Facil

return &service, nil
}

// CreateProfile is used to create profile in health CRM service
func (h *HealthCRMLib) CreateProfile(ctx context.Context, profile *ProfileInput) (*ProfileOutput, error) {
path := "/v1/identities/profiles/"
response, err := h.client.MakeRequest(ctx, http.MethodPost, path, nil, profile)
if err != nil {
return nil, err
}

defer response.Body.Close()

respBytes, err := io.ReadAll(response.Body)
if err != nil {
return nil, fmt.Errorf("could not read response: %w", err)
}

if response.StatusCode != http.StatusCreated {
return nil, errors.New(string(respBytes))
}

var profileResponse *ProfileOutput

err = json.Unmarshal(respBytes, &profileResponse)
if err != nil {
return nil, err
}

return profileResponse, nil
}
139 changes: 139 additions & 0 deletions healthcrm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1199,3 +1199,142 @@ func TestHealthCRMLib_GetService(t *testing.T) {
})
}
}

func TestHealthCRMLib_CreateProfile(t *testing.T) {
type args struct {
ctx context.Context
profile *ProfileInput
}
tests := []struct {
name string
args args
wantErr bool
}{
{
name: "Happy Case: Create Profile",
args: args{
ctx: context.Background(),
profile: &ProfileInput{
FirstName: "TestProfile",
LastName: "BikoTest",
OtherName: "SteveTest",
DateOfBirth: gofakeit.Date().String(),
Gender: "MALE",
EnrolmentDate: "2023-09-01",
SladeCode: "6000",
ServiceCode: "50",
Contacts: []*ProfileContactInput{
{
ContactType: "PHONE_NUMBER",
ContactValue: "+254788223223",
},
},
Identifiers: []*ProfileIdentifierInput{
{
IdentifierType: "SLADE_CODE",
IdentifierValue: "3243",
},
},
},
},
wantErr: false,
},
{
name: "Sad Case: Unable To Create Profile",
args: args{
ctx: context.Background(),
profile: &ProfileInput{
FirstName: gofakeit.FirstName(),
LastName: gofakeit.LastName(),
},
},
wantErr: true,
},
{
name: "Sad Case: Unable To Make Request",
args: args{
ctx: context.Background(),
profile: &ProfileInput{
FirstName: gofakeit.FirstName(),
LastName: gofakeit.LastName(),
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.name == "Happy Case: Create Profile" {
path := fmt.Sprintf("%s/v1/identities/profiles/", BaseURL)
httpmock.RegisterResponder(http.MethodPost, path, func(r *http.Request) (*http.Response, error) {
resp := &ProfileOutput{
FirstName: gofakeit.FirstName(),
LastName: gofakeit.LastName(),
OtherName: gofakeit.BeerName(),
DateOfBirth: gofakeit.Date().String(),
Gender: gofakeit.Gender(),
EnrolmentDate: gofakeit.Date().String(),
SladeCode: "50202",
ServiceCode: "50",
Contacts: []*ProfileContactOutput{
{
ContactType: "PHONE_NUMBER",
ContactValue: "+254788223223",
},
},
Identifiers: []*ProfileIdentifierOutput{
{
IdentifierType: "SLADE_CODE",
IdentifierValue: "3243",
},
},
}
return httpmock.NewJsonResponse(http.StatusCreated, resp)
})
}
if tt.name == "Sad Case: Unable To Create Profile" {
path := fmt.Sprintf("%s/v1/identities/profiles/", BaseURL)
httpmock.RegisterResponder(http.MethodPost, path, func(r *http.Request) (*http.Response, error) {
resp := &ProfileInput{
FirstName: gofakeit.FirstName(),
LastName: gofakeit.LastName(),
OtherName: gofakeit.BeerName(),
DateOfBirth: gofakeit.Date().String(),
Gender: gofakeit.Gender(),
EnrolmentDate: gofakeit.Date().String(),
SladeCode: "50202",
ServiceCode: "50",
Contacts: []*ProfileContactInput{},
Identifiers: []*ProfileIdentifierInput{},
}
return httpmock.NewJsonResponse(http.StatusBadRequest, resp)
})
}
if tt.name == "Sad Case: Unable To Make Request" {
httpmock.RegisterResponder(http.MethodPost, fmt.Sprintf("%s/oauth2/token/", serverutils.MustGetEnvVar("HEALTH_CRM_AUTH_SERVER_ENDPOINT")), func(r *http.Request) (*http.Response, error) {
resp := authutils.OAUTHResponse{
Scope: "",
ExpiresIn: 3600,
AccessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
RefreshToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c",
TokenType: "Bearer",
}
return httpmock.NewJsonResponse(http.StatusBadRequest, resp)
})
}

httpmock.Activate()
defer httpmock.DeactivateAndReset()
MockAuthenticate()
h, err := NewHealthCRMLib()
if err != nil {
t.Errorf("unable to initialize sdk: %v", err)
}
_, err = h.CreateProfile(tt.args.ctx, tt.args.profile)
if (err != nil) != tt.wantErr {
t.Errorf("HealthCRMLib.CreateProfile() error = %v, wantErr %v", err, tt.wantErr)
return
}
})
}
}
90 changes: 58 additions & 32 deletions input.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,26 @@ import "fmt"

// Facility is the hospitals data class
type Facility struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
FacilityType string `json:"facility_type,omitempty"`
County string `json:"county,omitempty"`
Country string `json:"country,omitempty"`
Address string `json:"address,omitempty"`
Coordinates *Coordinates `json:"coordinates,omitempty"`
Contacts []Contacts `json:"contacts,omitempty"`
Identifiers []Identifiers `json:"identifiers,omitempty"`
BusinessHours []BusinessHours `json:"businesshours,omitempty"`
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Description string `json:"description,omitempty"`
FacilityType string `json:"facility_type,omitempty"`
County string `json:"county,omitempty"`
Country string `json:"country,omitempty"`
Address string `json:"address,omitempty"`
Coordinates *Coordinates `json:"coordinates,omitempty"`
Contacts []Contacts `json:"contacts,omitempty"`
Identifiers []Identifiers `json:"identifiers,omitempty"`
BusinessHours []BusinessHours `json:"businesshours,omitempty"`
}

// Coordinates represents geographical coordinates using latitude and longitude.
// Latitude measures the north-south position, while longitude measures
// the east-west position.
type Coordinates struct {
Latitude string `json:"latitude,omitempty"`
Longitude string `json:"longitude,omitempty"`
Radius string `json:"radius,omitempty"`
Latitude string `json:"latitude,omitempty"`
Longitude string `json:"longitude,omitempty"`
Radius string `json:"radius,omitempty"`
}

// ToString returns the location in comma-separated values format.
Expand All @@ -40,41 +40,67 @@ func (c Coordinates) ToString() (string, error) {

// Contacts models facility's model data class
type Contacts struct {
ContactType string `json:"contact_type,omitempty"`
ContactValue string `json:"contact_value,omitempty"`
Role string `json:"role,omitempty"`
}
ContactType string `json:"contact_type,omitempty"`
ContactValue string `json:"contact_value,omitempty"`
Role string `json:"role,omitempty"`
}

// Identifiers models facility's identifiers; can be MFL Code, Slade Code etc...
type Identifiers struct {
IdentifierType string `json:"identifier_type,omitempty"`
IdentifierValue string `json:"identifier_value,omitempty"`
ValidFrom string `json:"valid_from,omitempty"`
ValidTo string `json:"valid_to,omitempty"`
IdentifierType string `json:"identifier_type,omitempty"`
IdentifierValue string `json:"identifier_value,omitempty"`
ValidFrom string `json:"valid_from,omitempty"`
ValidTo string `json:"valid_to,omitempty"`
}

// BusinessHours models data to store business hours
type BusinessHours struct {
Day string `json:"day"`
OpeningTime string `json:"opening_time"`
ClosingTime string `json:"closing_time"`
Day string `json:"day"`
OpeningTime string `json:"opening_time"`
ClosingTime string `json:"closing_time"`
}

// Pagination is used to hold pagination values
type Pagination struct {
Page string `json:"page"`
PageSize string `json:"page_size"`
Page string `json:"page"`
PageSize string `json:"page_size"`
}

// FacilityServiceInput models is used to create a new service
type FacilityServiceInput struct {
Name string `json:"name"`
Description string `json:"description"`
Identifiers []*ServiceIdentifierInput `json:"identifiers"`
Name string `json:"name"`
Description string `json:"description"`
Identifiers []*ServiceIdentifierInput `json:"identifiers"`
}

// ServiceIdentifierInput is used to create an identifier
type ServiceIdentifierInput struct {
IdentifierType string `json:"identifier_type"`
IdentifierValue string `json:"identifier_value"`
IdentifierType string `json:"identifier_type"`
IdentifierValue string `json:"identifier_value"`
}

// Profile is the host of users/client data class
type ProfileInput struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
OtherName string `json:"other_name"`
DateOfBirth string `json:"date_of_birth"`
Gender string `json:"gender"`
EnrolmentDate string `json:"enrolment_date"`
SladeCode string `json:"slade_code"`
ServiceCode string `json:"service_code"`
Contacts []*ProfileContactInput `json:"contacts,omitempty"`
Identifiers []*ProfileIdentifierInput `json:"identifiers,omitempty"`
}

// ProfileIdentifierInput is used to create profile(s) identifier(s)
type ProfileIdentifierInput struct {
IdentifierValue string `json:"identifier_value"`
IdentifierType string `json:"identifier_type"`
}

// ProfileContanctInput is used to create profile(s) contact(s)
type ProfileContactInput struct {
ContactType string `json:"contact_type"`
ContactValue string `json:"contact_value"`
}
Loading

0 comments on commit f7c87a9

Please sign in to comment.