diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6c966688..b0f6b7b0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,5 +3,5 @@ cd frontend npm run dist go mod vendor -go run backend/cmd/fasten/fasten.go start --config ./config.yaml -``` \ No newline at end of file +go run backend/cmd/fasten/fasten.go start --config ./config.yaml --debug +``` diff --git a/backend/pkg/hub/internal/fhir/base/base_client.go b/backend/pkg/hub/internal/fhir/base/base_client.go index 5ab05d5f..4735b54f 100644 --- a/backend/pkg/hub/internal/fhir/base/base_client.go +++ b/backend/pkg/hub/internal/fhir/base/base_client.go @@ -11,6 +11,7 @@ import ( "golang.org/x/oauth2" "io" "net/http" + "net/url" "os" "strings" "time" @@ -96,9 +97,26 @@ func NewBaseClient(ctx context.Context, appConfig config.Interface, globalLogger //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // HttpClient //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -func (c *BaseClient) GetRequest(resourceSubpath string, decodeModelPtr interface{}) error { - url := fmt.Sprintf("%s/%s", strings.TrimRight(c.Source.ApiEndpointBaseUrl, "/"), strings.TrimLeft(resourceSubpath, "/")) - resp, err := c.OauthClient.Get(url) +func (c *BaseClient) GetRequest(resourceSubpathOrNext string, decodeModelPtr interface{}) error { + resourceUrl, err := url.Parse(resourceSubpathOrNext) + if err != nil { + return err + } + if !resourceUrl.IsAbs() { + resourceUrl, err = url.Parse(fmt.Sprintf("%s/%s", strings.TrimRight(c.Source.ApiEndpointBaseUrl, "/"), strings.TrimLeft(resourceSubpathOrNext, "/"))) + } + if err != nil { + return err + } + + req, err := http.NewRequest(http.MethodGet, resourceUrl.String(), nil) + if err != nil { + return err + } + req.Header.Add("Accept", "application/json+fhir") + + //resp, err := c.OauthClient.Get(url) + resp, err := c.OauthClient.Do(req) if err != nil { return err @@ -106,7 +124,7 @@ func (c *BaseClient) GetRequest(resourceSubpath string, decodeModelPtr interface defer resp.Body.Close() if resp.StatusCode >= 300 || resp.StatusCode < 200 { - return fmt.Errorf("An error occurred during request %s - %d - %s", url, resp.StatusCode, resp.Status) + return fmt.Errorf("An error occurred during request %s - %d - %s", resourceUrl, resp.StatusCode, resp.Status) } err = ParseBundle(resp.Body, decodeModelPtr) diff --git a/backend/pkg/hub/internal/fhir/base/fhir401_client.go b/backend/pkg/hub/internal/fhir/base/fhir401_client.go index af50e41e..84ff4674 100644 --- a/backend/pkg/hub/internal/fhir/base/fhir401_client.go +++ b/backend/pkg/hub/internal/fhir/base/fhir401_client.go @@ -32,6 +32,39 @@ func (c *FHIR401Client) GetPatientBundle(patientId string) (fhir401.Bundle, erro // https://www.hl7.org/fhir/patient-operation-everything.html bundle := fhir401.Bundle{} err := c.GetRequest(fmt.Sprintf("Patient/%s/$everything", patientId), &bundle) + if err != nil { + return bundle, err + } + var next string + var self string + for _, link := range bundle.Link { + if link.Relation == "next" { + next = link.Url + } else if link.Relation == "self" { + self = link.Url + } + } + + for len(next) > 0 && next != self { + c.Logger.Debugf("Paginated request => %s", next) + nextBundle := fhir401.Bundle{} + err := c.GetRequest(next, &nextBundle) + if err != nil { + return bundle, nil //ignore failures when paginating? + } + bundle.Entry = append(bundle.Entry, nextBundle.Entry...) + + next = "" //reset the next pointer + self = "" + for _, link := range nextBundle.Link { + if link.Relation == "next" { + next = link.Url + } else if link.Relation == "self" { + self = link.Url + } + } + } + return bundle, err } diff --git a/backend/pkg/hub/internal/fhir/logica/client_test.go b/backend/pkg/hub/internal/fhir/logica/client_test.go new file mode 100644 index 00000000..a36c4b66 --- /dev/null +++ b/backend/pkg/hub/internal/fhir/logica/client_test.go @@ -0,0 +1,49 @@ +package logica + +import ( + "context" + mock_config "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/config/mock" + "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/database" + "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/hub/internal/fhir/base" + "github.com/fastenhealth/fastenhealth-onprem/backend/pkg/models" + "github.com/golang/mock/gomock" + "github.com/sirupsen/logrus" + "github.com/stretchr/testify/require" + "io/ioutil" + "os" + "testing" +) + +func TestLogicaClient_SyncAll(t *testing.T) { + t.Parallel() + //setup + mockCtrl := gomock.NewController(t) + defer mockCtrl.Finish() + fakeConfig := mock_config.NewMockInterface(mockCtrl) + + testDatabase, err := ioutil.TempFile("testdata", "fasten.db") + require.NoError(t, err) + defer os.Remove(testDatabase.Name()) + fakeConfig.EXPECT().GetString("web.database.location").AnyTimes().Return(testDatabase.Name()) + testLogger := logrus.WithFields(logrus.Fields{ + "type": "test", + }) + httpClient := base.OAuthVcrSetup(t, false) + client, _, err := NewClient(context.Background(), fakeConfig, testLogger, models.Source{ + SourceType: "logica", + PatientId: "smart-1288992", + ApiEndpointBaseUrl: "https://api.logicahealth.org/fastenhealth/data", + ClientId: "12b14c49-a4da-42f7-9e6f-2f19db622962", + }, httpClient) + require.NoError(t, err) + + db, err := database.NewRepository(fakeConfig, testLogger) + require.NoError(t, err) + + //test + err = client.SyncAll(db) + require.NoError(t, err) + + //assert + require.NoError(t, err) +}