Skip to content

Commit

Permalink
chore: add fetch all facilities api
Browse files Browse the repository at this point in the history
Signed-off-by: Otieno Calvine <nyarangaotieno@gmail.com>
  • Loading branch information
NYARAS committed Oct 12, 2021
1 parent 4451606 commit a6a2021
Show file tree
Hide file tree
Showing 14 changed files with 434 additions and 34 deletions.
54 changes: 54 additions & 0 deletions pkg/onboarding/application/dto/input.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package dto

import (
"net/url"

"github.com/savannahghi/enumutils"
)

// FacilityInput describes the facility input
type FacilityInput struct {
Name string `json:"name"`
Expand All @@ -8,3 +14,51 @@ type FacilityInput struct {
County string `json:"county"`
Description string `json:"description"`
}

// FacilityFilterInput is used to supply filter parameters for healthcare facility filter inputs
type FacilityFilterInput struct {
Search *string `json:"search"`
Name *string `json:"name"`
MFLCode *string `json:"code"`
}

// ToURLValues transforms the filter input to `url.Values`
func (i *FacilityFilterInput) ToURLValues() (values url.Values) {
vals := url.Values{}
if i.Search != nil {
vals.Add("search", *i.Search)
}
if i.Name != nil {
vals.Add("name", *i.Name)
}
if i.MFLCode != nil {
vals.Add("code", *i.MFLCode)
}
return vals
}

// FacilitySortInput is used to supply sort input for healthcare facility list queries
type FacilitySortInput struct {
Name *enumutils.SortOrder `json:"name"`
MFLCode *enumutils.SortOrder `json:"code"`
}

// ToURLValues transforms the filter input to `url.Values`
func (i *FacilitySortInput) ToURLValues() (values url.Values) {
vals := url.Values{}
if i.Name != nil {
if *i.Name == enumutils.SortOrderAsc {
vals.Add("order_by", "name")
} else {
vals.Add("order_by", "-name")
}
}
if i.MFLCode != nil {
if *i.Name == enumutils.SortOrderAsc {
vals.Add("code", "number")
} else {
vals.Add("code", "-number")
}
}
return vals
}
17 changes: 17 additions & 0 deletions pkg/onboarding/application/dto/output.go
Original file line number Diff line number Diff line change
@@ -1 +1,18 @@
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"`
}

// FacilityConnection is used to serialize GraphQL Relay connections for healthcare facilities
type FacilityConnection struct {
Edges []*FacilityEdge `json:"edges"`
PageInfo *firebasetools.PageInfo `json:"pageInfo"`
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
type GormMock struct {
CreateFacilityFn func(ctx context.Context, facility *gorm.Facility) (*gorm.Facility, error)
RetrieveFn func(id *int64) (*gorm.Facility, error)
GetFacilitiesFn func(ctx context.Context) ([]gorm.Facility, error)
}

// NewGormMock initializes a new instance of `GormMock` then mocking the case of success.
Expand Down Expand Up @@ -48,6 +49,23 @@ func NewGormMock() *GormMock {
Description: description,
}, nil
},
GetFacilitiesFn: func(ctx context.Context) ([]gorm.Facility, error) {
var facilities []gorm.Facility
facilityID := int64(1)
name := "Kanairo One"
code := "KN001"
county := "Kanairo"
description := "This is just for mocking"
facilities = append(facilities, gorm.Facility{
FacilityID: &facilityID,
Name: name,
Code: code,
Active: true,
County: county,
Description: description,
})
return facilities, nil
},
}
}

Expand All @@ -60,3 +78,8 @@ func (gm *GormMock) CreateFacility(ctx context.Context, facility *gorm.Facility)
func (gm *GormMock) Retrieve(id *int64) (*gorm.Facility, error) {
return gm.RetrieveFn(id)
}

// GetFacilities mocks the implementation of `gorm's` GetFacilities method.
func (gm *GormMock) GetFacilities(ctx context.Context) ([]gorm.Facility, error) {
return gm.GetFacilitiesFn(ctx)
}
17 changes: 16 additions & 1 deletion pkg/onboarding/infrastructure/database/postgres/gorm/query.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,34 @@
package gorm

import (
"context"
"fmt"
"log"
)

// Query contains all the db query methods
type Query interface {
Retrieve(id *int64) (*Facility, error)
GetFacilities(ctx context.Context) ([]Facility, error)
}

// Retrieve ...
// Retrieve fetches a single facility
func (db *PGInstance) Retrieve(id *int64) (*Facility, error) {
var facility Facility
if err := db.DB.Where(&Facility{FacilityID: id}).First(&facility).Error; err != nil {
return nil, fmt.Errorf("failed to get facility by ID %v: %v", id, err)
}
return &facility, nil
}

// GetFacilities fetches all the healthcare facilities in the platform.
func (db *PGInstance) GetFacilities(ctx context.Context) ([]Facility, error) {
var facility []Facility
facilities := db.DB.Find(&facility).Error
log.Printf("these are the facilities %v", facilities)
// if err != nil {
// return nil, fmt.Errorf("failed to query all facilities %v", err)
// }
log.Printf("these are the facilities %v", facility)
return facility, nil
}
18 changes: 18 additions & 0 deletions pkg/onboarding/infrastructure/database/postgres/mock/pg_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
// PostgresMock struct implements mocks of `postgres's` internal methods.
type PostgresMock struct {
CreateFacilityFn func(ctx context.Context, facility *dto.FacilityInput) (*domain.Facility, error)
GetFacilitiesFn func(ctx context.Context) ([]*domain.Facility, error)
}

// NewPostgresMock initializes a new instance of `GormMock` then mocking the case of success.
Expand All @@ -30,6 +31,23 @@ func NewPostgresMock() *PostgresMock {
Description: description,
}, nil
},
GetFacilitiesFn: func(ctx context.Context) ([]*domain.Facility, error) {
id := int64(1)
name := "Kanairo One"
code := "KN001"
county := "Kanairo"
description := "This is just for mocking"
return []*domain.Facility{
{
ID: id,
Name: name,
Code: code,
Active: true,
County: county,
Description: description,
},
}, nil
},
}
}

Expand Down
35 changes: 35 additions & 0 deletions pkg/onboarding/infrastructure/database/postgres/pg_query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package postgres

import (
"context"
"fmt"

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

//GetFacilities returns a slice of healthcare facilities in the platform.
func (d *OnboardingDb) GetFacilities(ctx context.Context) ([]*domain.Facility, error) {
var facility []*domain.Facility
facilities, err := d.query.GetFacilities(ctx)
if err != nil {
return nil, fmt.Errorf("failed to get facilities: %v", err)
}

if len(facilities) == 0 {
return facility, nil
}
for _, m := range facilities {
singleFacility := domain.Facility{
ID: int64(m.ID),
Name: m.Name,
Code: m.Code,
Active: m.Active,
County: m.County,
Description: m.Description,
}

facility = append(facility, &singleFacility)
}

return facility, nil
}
98 changes: 98 additions & 0 deletions pkg/onboarding/infrastructure/database/postgres/pg_query_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package postgres

import (
"context"
"fmt"
"testing"

"github.com/savannahghi/onboarding-service/pkg/onboarding/domain"
"github.com/savannahghi/onboarding-service/pkg/onboarding/infrastructure/database/postgres/gorm"
gormMock "github.com/savannahghi/onboarding-service/pkg/onboarding/infrastructure/database/postgres/gorm/mock"
)

func TestOnboardingDb_GetFacilities(t *testing.T) {
ctx := context.Background()
id := int64(1)
name := "Kanairo One"
code := "KN001"
county := "Kanairo"
description := "This is just for mocking"

facility := &domain.Facility{
ID: id,
Name: name,
Code: code,
Active: true,
County: county,
Description: description,
}

facilityData := []*domain.Facility{}
facilityData = append(facilityData, facility)
type args struct {
ctx context.Context
}
tests := []struct {
name string
args args
want []*domain.Facility
wantErr bool
}{
{
name: "happy case - valid payload",
args: args{ctx: ctx},
want: facilityData,
wantErr: false,
},
{
name: "sad case - facility want data not given",
args: args{ctx: ctx},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var fakeGorm = gormMock.NewGormMock()
d := NewOnboardingDb(fakeGorm, fakeGorm)

if tt.name == "sad case - facility want data not given" {
fakeGorm.GetFacilitiesFn = func(ctx context.Context) ([]gorm.Facility, error) {
return nil, fmt.Errorf("failed to get facilities")
}
}
if tt.name == "happy case - valid payload" {
fakeGorm.GetFacilitiesFn = func(ctx context.Context) ([]gorm.Facility, error) {
var facilities []gorm.Facility
facilityID := int64(1)
name := "Kanairo One"
code := "KN001"
county := "Kanairo"
description := "This is just for mocking"
facilities = append(facilities, gorm.Facility{
FacilityID: &facilityID,
Name: name,
Code: code,
Active: true,
County: county,
Description: description,
})
return facilities, nil
}
}
got, err := d.GetFacilities(tt.args.ctx)
if (err != nil) != tt.wantErr {
t.Errorf("OnboardingDb.GetFacilities() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.wantErr && got != nil {
t.Errorf("expected facilities to be nil for %v", tt.name)
return
}

if !tt.wantErr && got == nil {
t.Errorf("expected facilities not to be nil for %v", tt.name)
return
}
})
}
}
4 changes: 4 additions & 0 deletions pkg/onboarding/infrastructure/infrastructure.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const (
// It combines each individual service implementation
type Infrastructure struct {
Create
Query
libOnboardingUsecase.LoginUseCases
libOnboardingUsecase.SignUpUseCases
engagementSvc.ServiceEngagementImpl
Expand All @@ -31,6 +32,7 @@ type Infrastructure struct {
// It combines each individual service implementation
type Interactor struct {
Create
Query
libOnboardingUsecase.LoginUseCases
libOnboardingUsecase.SignUpUseCases
engagementSvc.ServiceEngagementImpl
Expand All @@ -56,9 +58,11 @@ func NewInteractor() Interactor {
}
db := pg.NewOnboardingDb(postgres, postgres)
create := NewServiceCreateImpl(*db)
query := NewServiceQueryImpl(*db)

return Interactor{
create,
query,
login,
signup,
*engagement,
Expand Down
26 changes: 25 additions & 1 deletion pkg/onboarding/infrastructure/repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,31 @@ func NewServiceCreateImpl(on pg.OnboardingDb) Create {
}
}

// CreateFacility is responsible from creating a representation of a facility
// CreateFacility is responsible for creating a representation of a facility
func (f ServiceCreateImpl) CreateFacility(ctx context.Context, facility dto.FacilityInput) (*domain.Facility, error) {
return f.onboarding.CreateFacility(ctx, &facility)
}

// Query represents a contract that contains all `get` ops to the database
//
// All the contracts for get operations are assembled here
type Query interface {
GetFacilities(ctx context.Context) ([]*domain.Facility, error)
}

// ServiceQueryImpl represents create contract implementation object
type ServiceQueryImpl struct {
onboarding pg.OnboardingDb
}

// NewServiceQueryImpl returns new instance of ServiceQueryImpl
func NewServiceQueryImpl(on pg.OnboardingDb) Query {
return &ServiceQueryImpl{
onboarding: on,
}
}

//GetFacilities is responsible for returning a slice of healthcare facilities in the platform.
func (q ServiceQueryImpl) GetFacilities(ctx context.Context) ([]*domain.Facility, error) {
return q.onboarding.GetFacilities(ctx)
}
4 changes: 4 additions & 0 deletions pkg/onboarding/presentation/graph/facility.graphql
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

extend type Mutation {
createFacility(input: FacilityInput!): Facility!
}

extend type Query {
fetchFacilities: [Facility]
}
Loading

0 comments on commit a6a2021

Please sign in to comment.