Merge pull request #395 from fastenhealth/fix_wizard_upload
added test to verify that wizard resource creation works correctly.
This commit is contained in:
commit
e9d1d577ab
|
@ -8,7 +8,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SECURITY: this is a secure endpoint, and should only be called after a double confirmation
|
// UX: this is a secure endpoint, and should only be called after a double confirmation
|
||||||
func DeleteAccount(c *gin.Context) {
|
func DeleteAccount(c *gin.Context) {
|
||||||
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
logger := c.MustGet(pkg.ContextKeyTypeLogger).(*logrus.Entry)
|
||||||
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
databaseRepo := c.MustGet(pkg.ContextKeyTypeDatabase).(database.DatabaseRepository)
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg"
|
||||||
|
mock_config "github.com/fastenhealth/fasten-onprem/backend/pkg/config/mock"
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/database"
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/event_bus"
|
||||||
|
"github.com/fastenhealth/fasten-onprem/backend/pkg/models"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/golang/mock/gomock"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Go through this page to understand how this file is structured.
|
||||||
|
// https://pkg.go.dev/github.com/stretchr/testify/suite#section-documentation
|
||||||
|
|
||||||
|
// Define the suite, and absorb the built-in basic suite
|
||||||
|
// functionality from testify - including a T() method which
|
||||||
|
// returns the current testing context
|
||||||
|
type ResourceRelatedHandlerTestSuite struct {
|
||||||
|
suite.Suite
|
||||||
|
MockCtrl *gomock.Controller
|
||||||
|
TestDatabase *os.File
|
||||||
|
|
||||||
|
AppConfig *mock_config.MockInterface
|
||||||
|
AppRepository database.DatabaseRepository
|
||||||
|
AppEventBus event_bus.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
// BeforeTest has a function to be executed right before the test starts and receives the suite and test names as input
|
||||||
|
func (suite *ResourceRelatedHandlerTestSuite) BeforeTest(suiteName, testName string) {
|
||||||
|
suite.MockCtrl = gomock.NewController(suite.T())
|
||||||
|
|
||||||
|
dbFile, err := ioutil.TempFile("", fmt.Sprintf("%s.*.db", testName))
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
suite.TestDatabase = dbFile
|
||||||
|
|
||||||
|
appConfig := mock_config.NewMockInterface(suite.MockCtrl)
|
||||||
|
appConfig.EXPECT().GetString("database.location").Return(suite.TestDatabase.Name()).AnyTimes()
|
||||||
|
appConfig.EXPECT().GetString("database.type").Return("sqlite").AnyTimes()
|
||||||
|
appConfig.EXPECT().IsSet("database.encryption.key").Return(false).AnyTimes()
|
||||||
|
appConfig.EXPECT().GetString("log.level").Return("INFO").AnyTimes()
|
||||||
|
suite.AppConfig = appConfig
|
||||||
|
|
||||||
|
appRepo, err := database.NewRepository(suite.AppConfig, logrus.WithField("test", suite.T().Name()), event_bus.NewNoopEventBusServer())
|
||||||
|
suite.AppRepository = appRepo
|
||||||
|
|
||||||
|
suite.AppEventBus = event_bus.NewNoopEventBusServer()
|
||||||
|
|
||||||
|
appRepo.CreateUser(context.Background(), &models.User{
|
||||||
|
Username: "test_username",
|
||||||
|
Password: "test",
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// AfterTest has a function to be executed right after the test finishes and receives the suite and test names as input
|
||||||
|
func (suite *ResourceRelatedHandlerTestSuite) AfterTest(suiteName, testName string) {
|
||||||
|
suite.MockCtrl.Finish()
|
||||||
|
os.Remove(suite.TestDatabase.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
// In order for 'go test' to run this suite, we need to create
|
||||||
|
// a normal test function and pass our suite to suite.Run
|
||||||
|
func TestResourceRelatedHandlerTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(ResourceRelatedHandlerTestSuite))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ResourceRelatedHandlerTestSuite) TestResourceRelatedHandlerTestSuite() {
|
||||||
|
//setup
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
ctx, _ := gin.CreateTestContext(w)
|
||||||
|
ctx.Set(pkg.ContextKeyTypeLogger, logrus.WithField("test", suite.T().Name()))
|
||||||
|
ctx.Set(pkg.ContextKeyTypeDatabase, suite.AppRepository)
|
||||||
|
ctx.Set(pkg.ContextKeyTypeConfig, suite.AppConfig)
|
||||||
|
ctx.Set(pkg.ContextKeyTypeEventBusServer, suite.AppEventBus)
|
||||||
|
ctx.Set(pkg.ContextKeyTypeAuthUsername, "test_username")
|
||||||
|
|
||||||
|
//test
|
||||||
|
relatedJsonForm, relatedJsonWriter := createMultipartFormData(suite.T(), "file", "testdata/related.json") // just pass the file name
|
||||||
|
relatedReq, err := http.NewRequest("POST", "/api/v1/resource/related", &relatedJsonForm)
|
||||||
|
// set the content type, this will contain the boundary.
|
||||||
|
relatedReq.Header.Set("Content-Type", relatedJsonWriter.FormDataContentType())
|
||||||
|
ctx.Request = relatedReq
|
||||||
|
require.NoError(suite.T(), err)
|
||||||
|
|
||||||
|
CreateRelatedResources(ctx)
|
||||||
|
|
||||||
|
var responseWrapper models.ResponseWrapper
|
||||||
|
err = json.Unmarshal(w.Body.Bytes(), &responseWrapper)
|
||||||
|
require.NoError(suite.T(), err)
|
||||||
|
|
||||||
|
summary := responseWrapper.Data.(map[string]interface{})
|
||||||
|
|
||||||
|
//assert
|
||||||
|
assert.EqualValues(suite.T(), http.StatusOK, w.Code)
|
||||||
|
assert.Equal(suite.T(), summary["TotalResources"], float64(3))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/56696333/1157633
|
||||||
|
func createMultipartFormData(t *testing.T, fieldName, fileName string) (bytes.Buffer, *multipart.Writer) {
|
||||||
|
var b bytes.Buffer
|
||||||
|
var err error
|
||||||
|
w := multipart.NewWriter(&b)
|
||||||
|
var fw io.Writer
|
||||||
|
file := mustOpen(fileName)
|
||||||
|
if fw, err = w.CreateFormFile(fieldName, file.Name()); err != nil {
|
||||||
|
t.Errorf("Error creating writer: %v", err)
|
||||||
|
}
|
||||||
|
if _, err = io.Copy(fw, file); err != nil {
|
||||||
|
t.Errorf("Error with io.Copy: %v", err)
|
||||||
|
}
|
||||||
|
w.Close()
|
||||||
|
return b, w
|
||||||
|
}
|
||||||
|
|
||||||
|
func mustOpen(f string) *os.File {
|
||||||
|
r, err := os.Open(f)
|
||||||
|
if err != nil {
|
||||||
|
pwd, _ := os.Getwd()
|
||||||
|
fmt.Println("PWD: ", pwd)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
{
|
||||||
|
"resourceType": "List",
|
||||||
|
"entry": [],
|
||||||
|
"encounter": {
|
||||||
|
"reference": "Encounter/4e4e6346-2350-47c0-ba73-ee0fdaf3a8a7"
|
||||||
|
},
|
||||||
|
"contained": [
|
||||||
|
{
|
||||||
|
"resourceType": "Encounter",
|
||||||
|
"id": "4e4e6346-2350-47c0-ba73-ee0fdaf3a8a7",
|
||||||
|
"serviceType": {
|
||||||
|
"id": "562",
|
||||||
|
"identifier": [
|
||||||
|
{
|
||||||
|
"system": "http://terminology.hl7.org/CodeSystem/service-type",
|
||||||
|
"code": "562",
|
||||||
|
"display": "Test Message"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"text": "Test Message"
|
||||||
|
},
|
||||||
|
"status": "finished",
|
||||||
|
"participant": [],
|
||||||
|
"period": {
|
||||||
|
"start": "2024-01-04T08:00:00.000Z",
|
||||||
|
"end": "2024-01-27T08:00:00.000Z"
|
||||||
|
},
|
||||||
|
"reasonReference": [],
|
||||||
|
"serviceProvider": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4e2611e4-df0d-4dae-ad32-1c90537b32af",
|
||||||
|
"resourceType": "Binary",
|
||||||
|
"contentType": "application/pdf",
|
||||||
|
"data": "JVBERi0xLjcKCjEgMCBvYmogICUgZW50cnkgcG9pbnQKPDwKICAvVHlwZSAvQ2F0YWxvZwogIC9QYWdlcyAyIDAgUgo+PgplbmRvYmoKCjIgMCBvYmoKPDwKICAvVHlwZSAvUGFnZXMKICAvTWVkaWFCb3ggWyAwIDAgMjAwIDIwMCBdCiAgL0NvdW50IDEKICAvS2lkcyBbIDMgMCBSIF0KPj4KZW5kb2JqCgozIDAgb2JqCjw8CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAgL1Jlc291cmNlcyA8PAogICAgL0ZvbnQgPDwKICAgICAgL0YxIDQgMCBSIAogICAgPj4KICA+PgogIC9Db250ZW50cyA1IDAgUgo+PgplbmRvYmoKCjQgMCBvYmoKPDwKICAvVHlwZSAvRm9udAogIC9TdWJ0eXBlIC9UeXBlMQogIC9CYXNlRm9udCAvVGltZXMtUm9tYW4KPj4KZW5kb2JqCgo1IDAgb2JqICAlIHBhZ2UgY29udGVudAo8PAogIC9MZW5ndGggNDQKPj4Kc3RyZWFtCkJUCjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8sIHdvcmxkISkgVGoKRVQKZW5kc3RyZWFtCmVuZG9iagoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDEwIDAwMDAwIG4gCjAwMDAwMDAwNzkgMDAwMDAgbiAKMDAwMDAwMDE3MyAwMDAwMCBuIAowMDAwMDAwMzAxIDAwMDAwIG4gCjAwMDAwMDAzODAgMDAwMDAgbiAKdHJhaWxlcgo8PAogIC9TaXplIDYKICAvUm9vdCAxIDAgUgo+PgpzdGFydHhyZWYKNDkyCiUlRU9G"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "53066674-9e89-41e9-95d9-8eaf24b41ea6",
|
||||||
|
"resourceType": "DocumentReference",
|
||||||
|
"status": "current",
|
||||||
|
"category": [
|
||||||
|
{
|
||||||
|
"coding": [
|
||||||
|
{
|
||||||
|
"system": "http://loinc.org",
|
||||||
|
"code": "11488-4",
|
||||||
|
"display": "Consult Note"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"text": "Consult Note"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"content": [
|
||||||
|
{
|
||||||
|
"attachment": {
|
||||||
|
"contentType": "application/pdf",
|
||||||
|
"url": "urn:uuid:4e2611e4-df0d-4dae-ad32-1c90537b32af",
|
||||||
|
"title": "test attachment"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"context": {
|
||||||
|
"encounter": [{
|
||||||
|
"reference": "Encounter/4e4e6346-2350-47c0-ba73-ee0fdaf3a8a7"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -30,7 +30,7 @@
|
||||||
</div><!-- col -->
|
</div><!-- col -->
|
||||||
<div class="col-6 mg-t-10 mg-lg-t-0 mg-l-10">
|
<div class="col-6 mg-t-10 mg-lg-t-0 mg-l-10">
|
||||||
<label for="customFile" class="custom-file-label">{{newAttachmentForm.get('file_name').value || 'Choose file'}}</label>
|
<label for="customFile" class="custom-file-label">{{newAttachmentForm.get('file_name').value || 'Choose file'}}</label>
|
||||||
<input id="customFile" (change)="onAttachmentFileChange($event)" class="custom-file-input" formControlName="file_name" type="file">
|
<input id="customFile" (change)="onAttachmentFileChange($event)" class="custom-file-input" type="file">
|
||||||
</div><!-- col -->
|
</div><!-- col -->
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -41,15 +41,16 @@ export class MedicalRecordWizardAddAttachmentComponent implements OnInit {
|
||||||
console.log("onAttachmentFileChange")
|
console.log("onAttachmentFileChange")
|
||||||
let fileInput = $event.target as HTMLInputElement;
|
let fileInput = $event.target as HTMLInputElement;
|
||||||
if (fileInput.files && fileInput.files[0]) {
|
if (fileInput.files && fileInput.files[0]) {
|
||||||
|
let processingFile = fileInput.files[0]
|
||||||
let reader = new FileReader();
|
let reader = new FileReader();
|
||||||
reader.onloadend = () => {
|
reader.onloadend = () => {
|
||||||
// use a regex to remove data url part
|
// use a regex to remove data url part
|
||||||
const base64String = (reader.result as string).replace('data:', '').replace(/^.+,/, '');
|
const base64String = (reader.result as string).replace('data:', '').replace(/^.+,/, '');
|
||||||
this.newAttachmentForm.get('file_content').setValue(base64String)
|
this.newAttachmentForm.get('file_content').setValue(base64String)
|
||||||
};
|
};
|
||||||
reader.readAsDataURL(fileInput.files[0]);
|
reader.readAsDataURL(processingFile);
|
||||||
this.newAttachmentForm.get('file_name').setValue(fileInput.files[0].name)
|
this.newAttachmentForm.get('file_name').setValue(processingFile.name)
|
||||||
this.newAttachmentForm.get('file_size').setValue(fileInput.files[0].size)
|
this.newAttachmentForm.get('file_size').setValue(processingFile.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -371,13 +371,12 @@ function resourceAttachmentToR4DocumentReference(resourceStorage: ResourceStorag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
context: [
|
context: {
|
||||||
{
|
encounter: [{
|
||||||
encounter: {
|
|
||||||
reference: generateReferenceUriFromResourceOrReference(encounterResource) //Encounter
|
reference: generateReferenceUriFromResourceOrReference(encounterResource) //Encounter
|
||||||
},
|
}],
|
||||||
}
|
}
|
||||||
]
|
|
||||||
// date: `${new Date(resourceDocumentReference.date.year,resourceDocumentReference.date.month-1,resourceDocumentReference.date.day).toISOString()}`,
|
// date: `${new Date(resourceDocumentReference.date.year,resourceDocumentReference.date.month-1,resourceDocumentReference.date.day).toISOString()}`,
|
||||||
} as DocumentReference
|
} as DocumentReference
|
||||||
resourceStorage['DocumentReference'][documentReferenceResource.id] = documentReferenceResource
|
resourceStorage['DocumentReference'][documentReferenceResource.id] = documentReferenceResource
|
||||||
|
|
|
@ -124,7 +124,7 @@ export class MedicalSourcesConnectedComponent implements OnInit {
|
||||||
//TOOD: print this message in the UI
|
//TOOD: print this message in the UI
|
||||||
let errMsg = "an error occurred while authenticating to this source. Please try again later"
|
let errMsg = "an error occurred while authenticating to this source. Please try again later"
|
||||||
console.error(errMsg, callbackErrorDescription)
|
console.error(errMsg, callbackErrorDescription)
|
||||||
throw new Error(errMsg)
|
throw new Error(`callback error: ${callbackError}, description: ${callbackErrorDescription}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("callback code:", callbackCode)
|
console.log("callback code:", callbackCode)
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<br/>
|
<br/>
|
||||||
<br/>
|
<br/>
|
||||||
In <strong>Sandbox mode</strong> Fasten Health cannot access real patient information. You must use Sandbox credentials when authenticating to healthcare sources.
|
In <strong>Sandbox mode</strong> Fasten Health cannot access real patient information. You must use Sandbox credentials when authenticating to healthcare sources.
|
||||||
<a href="https://github.com/fastenhealth/docs/blob/main/BETA.md#connecting-a-new-source" externalLink>These credentials are available on Github</a>
|
<a href="https://docs.fastenhealth.com/getting-started/sandbox.html#connecting-a-new-source" externalLink>These credentials are available in our Sandbox Guide</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
2
go.mod
2
go.mod
|
@ -15,7 +15,7 @@ require (
|
||||||
github.com/dave/jennifer v1.6.1
|
github.com/dave/jennifer v1.6.1
|
||||||
github.com/dominikbraun/graph v0.15.0
|
github.com/dominikbraun/graph v0.15.0
|
||||||
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3
|
github.com/dop251/goja v0.0.0-20230605162241-28ee0ee714f3
|
||||||
github.com/fastenhealth/fasten-sources v0.5.5
|
github.com/fastenhealth/fasten-sources v0.5.6
|
||||||
github.com/fastenhealth/gofhir-models v0.0.6
|
github.com/fastenhealth/gofhir-models v0.0.6
|
||||||
github.com/gin-gonic/gin v1.9.0
|
github.com/gin-gonic/gin v1.9.0
|
||||||
github.com/go-gormigrate/gormigrate/v2 v2.1.1
|
github.com/go-gormigrate/gormigrate/v2 v2.1.1
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -101,8 +101,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
|
||||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/fastenhealth/fasten-sources v0.5.5 h1:+vOIG7R/J7QxEZrrfPFfaBzqYU5eDu7x/bc/cgOjvW4=
|
github.com/fastenhealth/fasten-sources v0.5.6 h1:F4Qmw9ABLSkqkWncoSnChBRDVWLrzkJv+z4z/Ue/fdc=
|
||||||
github.com/fastenhealth/fasten-sources v0.5.5/go.mod h1:hUQATAu5KrxKbACJoVt4iEKIGnRtmiOmHz+5TLfyiCM=
|
github.com/fastenhealth/fasten-sources v0.5.6/go.mod h1:hUQATAu5KrxKbACJoVt4iEKIGnRtmiOmHz+5TLfyiCM=
|
||||||
github.com/fastenhealth/gofhir-models v0.0.6 h1:yJYYaV1eJtHiGEfA1rXLsyOm/9hIi6s2cGoZzGfW1tM=
|
github.com/fastenhealth/gofhir-models v0.0.6 h1:yJYYaV1eJtHiGEfA1rXLsyOm/9hIi6s2cGoZzGfW1tM=
|
||||||
github.com/fastenhealth/gofhir-models v0.0.6/go.mod h1:xB8ikGxu3bUq2b1JYV+CZpHqBaLXpOizFR0eFBCunis=
|
github.com/fastenhealth/gofhir-models v0.0.6/go.mod h1:xB8ikGxu3bUq2b1JYV+CZpHqBaLXpOizFR0eFBCunis=
|
||||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||||
|
|
Loading…
Reference in New Issue